diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 0afb36cb513..6e0e980dad7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -375,30 +375,34 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference())); return; } + KotlinTypeChecker typeChecker = KotlinTypeChecker.DEFAULT; - if (isExactTypeCast(actualType, targetType)) { - // cast to itself: String as String + if (castIsUseless(expression, context, targetType, actualType, typeChecker)) { context.trace.report(USELESS_CAST.on(expression)); return; } - Collection 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)) { 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 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) { if (TypeUtils.noExpectedType(expectedType)) { return checkExactTypeForUselessCast(expression); @@ -414,7 +418,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } private static boolean isUpcast(KotlinType candidateType, KotlinType targetType, KotlinTypeChecker typeChecker) { - if (KotlinBuiltIns.isNullableNothing(candidateType)) return false; if (!typeChecker.isSubtypeOf(candidateType, targetType)) return false; if (isFunctionType(candidateType) && isFunctionType(targetType)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index 9e02b07c61e..d891c39e902 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -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"); * you may not use this file except in compliance with the License. @@ -344,13 +344,12 @@ public class DataFlowAnalyzer { @NotNull public static Collection getAllPossibleTypes( @NotNull KtExpression expression, - @NotNull DataFlowInfo dataFlowInfo, @NotNull KotlinType type, @NotNull ResolutionContext c ) { DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, c); Collection possibleTypes = Sets.newHashSet(type); - possibleTypes.addAll(dataFlowInfo.getStableTypes(dataFlowValue)); + possibleTypes.addAll(c.dataFlowInfo.getStableTypes(dataFlowValue)); return possibleTypes; } diff --git a/compiler/testData/diagnostics/tests/Casts.kt b/compiler/testData/diagnostics/tests/Casts.kt index ce45e53f0ce..38155e7f8ec 100644 --- a/compiler/testData/diagnostics/tests/Casts.kt +++ b/compiler/testData/diagnostics/tests/Casts.kt @@ -8,11 +8,11 @@ fun test() : Unit { checkSubtype(y) checkSubtype(x as Int) checkSubtype(y as Int) - checkSubtype(x as Int?) + checkSubtype(x as Int?) checkSubtype(y as Int?) checkSubtype(x as? Int) checkSubtype(y as? Int) - checkSubtype(x as? Int?) + checkSubtype(x as? Int?) checkSubtype(y as? Int?) val s = "" as Any diff --git a/compiler/testData/diagnostics/tests/cast/StableTypeForUselessCast.kt b/compiler/testData/diagnostics/tests/cast/StableTypeForUselessCast.kt new file mode 100644 index 00000000000..fcd72edcbe1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/cast/StableTypeForUselessCast.kt @@ -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) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/StableTypeForUselessCast.txt b/compiler/testData/diagnostics/tests/cast/StableTypeForUselessCast.txt new file mode 100644 index 00000000000..5ee95b8da3e --- /dev/null +++ b/compiler/testData/diagnostics/tests/cast/StableTypeForUselessCast.txt @@ -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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 976929a4ad5..a9c332e8e7f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -2956,6 +2956,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { 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") public void testWhenErasedDisallowFromAny() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt"); diff --git a/idea/testData/checker/Casts.kt b/idea/testData/checker/Casts.kt index e480b4859ab..8ee02ec3f79 100644 --- a/idea/testData/checker/Casts.kt +++ b/idea/testData/checker/Casts.kt @@ -7,12 +7,12 @@ fun test() : Unit { checkSubtype(x) checkSubtype(y) checkSubtype(x as Int) - checkSubtype(y as Int) - checkSubtype(x as Int?) + checkSubtype(y as Int) + checkSubtype(x as Int?) checkSubtype(y as Int?) - checkSubtype(x as Int?) - checkSubtype(y as? Int) - checkSubtype(x as? Int?) + checkSubtype(x as Int?) + checkSubtype(y as? Int) + checkSubtype(x as? Int?) checkSubtype(y as? Int?) Unit }