Prefer stable types to diagnose useless cast
#KT-11622 Fixed
This commit is contained in:
+18
-15
@@ -375,30 +375,34 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
|
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KotlinTypeChecker typeChecker = KotlinTypeChecker.DEFAULT;
|
KotlinTypeChecker typeChecker = KotlinTypeChecker.DEFAULT;
|
||||||
if (isExactTypeCast(actualType, targetType)) {
|
if (castIsUseless(expression, context, targetType, actualType, typeChecker)) {
|
||||||
// cast to itself: String as String
|
|
||||||
context.trace.report(USELESS_CAST.on(expression));
|
context.trace.report(USELESS_CAST.on(expression));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Collection<KotlinType> possibleTypes = components.dataFlowAnalyzer.getAllPossibleTypes(
|
|
||||||
expression.getLeft(), context.dataFlowInfo, actualType, context);
|
|
||||||
|
|
||||||
boolean checkExactType = shouldCheckForExactType(expression, context.expectedType);
|
|
||||||
for (KotlinType possibleType : possibleTypes) {
|
|
||||||
boolean castIsUseless = checkExactType
|
|
||||||
? isExactTypeCast(possibleType, targetType)
|
|
||||||
: isUpcast(possibleType, targetType, typeChecker);
|
|
||||||
if (castIsUseless) {
|
|
||||||
context.trace.report(USELESS_CAST.on(expression));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (CastDiagnosticsUtil.isCastErased(actualType, targetType, typeChecker)) {
|
if (CastDiagnosticsUtil.isCastErased(actualType, targetType, typeChecker)) {
|
||||||
context.trace.report(UNCHECKED_CAST.on(expression, actualType, targetType));
|
context.trace.report(UNCHECKED_CAST.on(expression, actualType, targetType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean castIsUseless(
|
||||||
|
@NotNull KtBinaryExpressionWithTypeRHS expression,
|
||||||
|
@NotNull ExpressionTypingContext context,
|
||||||
|
@NotNull KotlinType targetType,
|
||||||
|
@NotNull KotlinType actualType,
|
||||||
|
@NotNull KotlinTypeChecker typeChecker
|
||||||
|
) {
|
||||||
|
Collection<KotlinType> possibleTypes = DataFlowAnalyzer.getAllPossibleTypes(expression.getLeft(), actualType, context);
|
||||||
|
KotlinType intersectedType = TypeIntersector.intersectTypes(typeChecker, possibleTypes);
|
||||||
|
if (intersectedType == null) return false;
|
||||||
|
|
||||||
|
return shouldCheckForExactType(expression, context.expectedType)
|
||||||
|
? isExactTypeCast(intersectedType, targetType)
|
||||||
|
: isUpcast(intersectedType, targetType, typeChecker);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean shouldCheckForExactType(KtBinaryExpressionWithTypeRHS expression, KotlinType expectedType) {
|
private static boolean shouldCheckForExactType(KtBinaryExpressionWithTypeRHS expression, KotlinType expectedType) {
|
||||||
if (TypeUtils.noExpectedType(expectedType)) {
|
if (TypeUtils.noExpectedType(expectedType)) {
|
||||||
return checkExactTypeForUselessCast(expression);
|
return checkExactTypeForUselessCast(expression);
|
||||||
@@ -414,7 +418,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isUpcast(KotlinType candidateType, KotlinType targetType, KotlinTypeChecker typeChecker) {
|
private static boolean isUpcast(KotlinType candidateType, KotlinType targetType, KotlinTypeChecker typeChecker) {
|
||||||
if (KotlinBuiltIns.isNullableNothing(candidateType)) return false;
|
|
||||||
if (!typeChecker.isSubtypeOf(candidateType, targetType)) return false;
|
if (!typeChecker.isSubtypeOf(candidateType, targetType)) return false;
|
||||||
|
|
||||||
if (isFunctionType(candidateType) && isFunctionType(targetType)) {
|
if (isFunctionType(candidateType) && isFunctionType(targetType)) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -344,13 +344,12 @@ public class DataFlowAnalyzer {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public static Collection<KotlinType> getAllPossibleTypes(
|
public static Collection<KotlinType> getAllPossibleTypes(
|
||||||
@NotNull KtExpression expression,
|
@NotNull KtExpression expression,
|
||||||
@NotNull DataFlowInfo dataFlowInfo,
|
|
||||||
@NotNull KotlinType type,
|
@NotNull KotlinType type,
|
||||||
@NotNull ResolutionContext c
|
@NotNull ResolutionContext c
|
||||||
) {
|
) {
|
||||||
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
|
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c);
|
||||||
Collection<KotlinType> possibleTypes = Sets.newHashSet(type);
|
Collection<KotlinType> possibleTypes = Sets.newHashSet(type);
|
||||||
possibleTypes.addAll(dataFlowInfo.getStableTypes(dataFlowValue));
|
possibleTypes.addAll(c.dataFlowInfo.getStableTypes(dataFlowValue));
|
||||||
return possibleTypes;
|
return possibleTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -8,11 +8,11 @@ fun test() : Unit {
|
|||||||
checkSubtype<Int>(y)
|
checkSubtype<Int>(y)
|
||||||
checkSubtype<Int>(x as Int)
|
checkSubtype<Int>(x as Int)
|
||||||
checkSubtype<Int>(y <!USELESS_CAST!>as Int<!>)
|
checkSubtype<Int>(y <!USELESS_CAST!>as Int<!>)
|
||||||
checkSubtype<Int?>(x <!USELESS_CAST!>as Int?<!>)
|
checkSubtype<Int?>(x as Int?)
|
||||||
checkSubtype<Int?>(y as Int?)
|
checkSubtype<Int?>(y as Int?)
|
||||||
checkSubtype<Int?>(x <!USELESS_CAST!>as? Int<!>)
|
checkSubtype<Int?>(x <!USELESS_CAST!>as? Int<!>)
|
||||||
checkSubtype<Int?>(y <!USELESS_CAST!>as? Int<!>)
|
checkSubtype<Int?>(y <!USELESS_CAST!>as? Int<!>)
|
||||||
checkSubtype<Int?>(x <!USELESS_CAST!>as? Int?<!>)
|
checkSubtype<Int?>(x as? Int?)
|
||||||
checkSubtype<Int?>(y as? Int?)
|
checkSubtype<Int?>(y as? Int?)
|
||||||
|
|
||||||
val <!UNUSED_VARIABLE!>s<!> = "" as Any
|
val <!UNUSED_VARIABLE!>s<!> = "" as Any
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
|
interface I1
|
||||||
|
interface I2
|
||||||
|
|
||||||
|
fun foo(i: I1) {}
|
||||||
|
fun foo(i: I2) {}
|
||||||
|
|
||||||
|
fun bar(i: I1) {
|
||||||
|
if (i is I2) {
|
||||||
|
foo(i as I1)
|
||||||
|
foo(i as I2)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun bar(/*0*/ i: I1): kotlin.Unit
|
||||||
|
public fun foo(/*0*/ i: I1): kotlin.Unit
|
||||||
|
public fun foo(/*0*/ i: I2): kotlin.Unit
|
||||||
|
|
||||||
|
public interface I1 {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface I2 {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
@@ -2956,6 +2956,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("StableTypeForUselessCast.kt")
|
||||||
|
public void testStableTypeForUselessCast() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/StableTypeForUselessCast.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("WhenErasedDisallowFromAny.kt")
|
@TestMetadata("WhenErasedDisallowFromAny.kt")
|
||||||
public void testWhenErasedDisallowFromAny() throws Exception {
|
public void testWhenErasedDisallowFromAny() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt");
|
||||||
|
|||||||
Vendored
+5
-5
@@ -7,12 +7,12 @@ fun test() : Unit {
|
|||||||
checkSubtype<Int?>(x)
|
checkSubtype<Int?>(x)
|
||||||
checkSubtype<Int>(y)
|
checkSubtype<Int>(y)
|
||||||
checkSubtype<Int>(x as Int)
|
checkSubtype<Int>(x as Int)
|
||||||
checkSubtype<Int>(y <warning>as Int</warning>)
|
checkSubtype<Int>(y <warning descr="[USELESS_CAST] No cast needed">as Int</warning>)
|
||||||
checkSubtype<Int?>(x <warning>as Int?</warning>)
|
checkSubtype<Int?>(x as Int?)
|
||||||
checkSubtype<Int?>(y as Int?)
|
checkSubtype<Int?>(y as Int?)
|
||||||
checkSubtype<Int?>(x <warning>as Int?</warning>)
|
checkSubtype<Int?>(x as Int?)
|
||||||
checkSubtype<Int?>(y <warning>as? Int</warning>)
|
checkSubtype<Int?>(y <warning descr="[USELESS_CAST] No cast needed">as? Int</warning>)
|
||||||
checkSubtype<Int?>(x <warning>as? Int?</warning>)
|
checkSubtype<Int?>(x as? Int?)
|
||||||
checkSubtype<Int?>(y as? Int?)
|
checkSubtype<Int?>(y as? Int?)
|
||||||
Unit
|
Unit
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user