From 8258d72e237b0c393d8d33c3fd9fc3d90d0ae210 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 24 Apr 2015 16:18:54 +0200 Subject: [PATCH] don't report USELESS_CAST in case of casting to superype using 'as' --- .../BasicExpressionTypingVisitor.java | 28 +++++++++++++++++-- compiler/testData/diagnostics/tests/Casts.kt | 9 ++++-- .../diagnostics/tests/classObjects/kt3866.kt | 2 +- .../diagnostics/tests/smartCasts/kt5455.kt | 6 ++-- 4 files changed, 37 insertions(+), 8 deletions(-) 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 a2b7db1d10d..c49994237ac 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -20,6 +20,7 @@ import com.google.common.collect.Lists; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; +import com.intellij.psi.util.PsiTreeUtil; import kotlin.Function0; import kotlin.Function1; import kotlin.KotlinPackage; @@ -62,7 +63,6 @@ import org.jetbrains.kotlin.resolve.scopes.WritableScopeImpl; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver; -import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator; import org.jetbrains.kotlin.types.*; import org.jetbrains.kotlin.types.checker.JetTypeChecker; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage; @@ -249,8 +249,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } Collection possibleTypes = DataFlowUtils.getAllPossibleTypes( expression.getLeft(), context.dataFlowInfo, actualType, context); + + boolean checkExactType = checkExactTypeForUselessCast(expression); for (JetType possibleType : possibleTypes) { - if (typeChecker.isSubtypeOf(possibleType, targetType)) { + boolean castIsUseless = checkExactType + ? possibleType.equals(targetType) + : typeChecker.isSubtypeOf(possibleType, targetType); + if (castIsUseless) { context.trace.report(USELESS_CAST.on(expression)); return; } @@ -260,6 +265,25 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { } } + // Casting an argument or a receiver to a supertype may be useful to select an exact overload of a method. + // Casting to a supertype in other contexts is unlikely to be useful. + private static boolean checkExactTypeForUselessCast(JetBinaryExpressionWithTypeRHS expression) { + PsiElement parent = expression.getParent(); + while (parent instanceof JetParenthesizedExpression || + parent instanceof JetLabeledExpression || + parent instanceof JetAnnotatedExpression) { + parent = parent.getParent(); + } + if (parent instanceof JetValueArgument) { + return true; + } + if (parent instanceof JetQualifiedExpression) { + JetExpression receiver = ((JetQualifiedExpression) parent).getReceiverExpression(); + return PsiTreeUtil.isAncestor(receiver, expression, false); + } + return false; + } + @Override public JetTypeInfo visitThisExpression(@NotNull JetThisExpression expression, ExpressionTypingContext context) { JetType result = null; diff --git a/compiler/testData/diagnostics/tests/Casts.kt b/compiler/testData/diagnostics/tests/Casts.kt index 45ef14d8a22..92fe5861128 100644 --- a/compiler/testData/diagnostics/tests/Casts.kt +++ b/compiler/testData/diagnostics/tests/Casts.kt @@ -9,10 +9,15 @@ fun test() : Unit { checkSubtype(x as Int) checkSubtype(y as Int) checkSubtype(x as Int?) - checkSubtype(y as Int?) + checkSubtype(y as Int?) checkSubtype(x as? Int) checkSubtype(y as? Int) checkSubtype(x as? Int?) - checkSubtype(y as? Int?) + checkSubtype(y as? Int?) + + val s = "" as Any + ("" as String?)?.length() + (@data("" as String?))?.length() + ([data]( "" as String?))?.length() Unit } diff --git a/compiler/testData/diagnostics/tests/classObjects/kt3866.kt b/compiler/testData/diagnostics/tests/classObjects/kt3866.kt index cfc76d155ca..8f90fc5307a 100644 --- a/compiler/testData/diagnostics/tests/classObjects/kt3866.kt +++ b/compiler/testData/diagnostics/tests/classObjects/kt3866.kt @@ -15,6 +15,6 @@ fun bar() { val x = X x.foo() X.foo() - (X as C).foo() + (X as C).foo() ((if (1<2) X else Y) as C).foo() } diff --git a/compiler/testData/diagnostics/tests/smartCasts/kt5455.kt b/compiler/testData/diagnostics/tests/smartCasts/kt5455.kt index 8df70b6ee7d..dfd04d194b2 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/kt5455.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/kt5455.kt @@ -13,16 +13,16 @@ class B: A() fun test(a: Any?) { if (a is B) { - (a as A).foo() + (a as A).foo() } } fun test1(a: B) { - (a as A?)?.foo() + (a as A?)?.foo() } fun test2(b: B?) { if (b != null) { - (b as A).foo() + (b as A).foo() } }