don't report USELESS_CAST in case of casting to superype using 'as'

This commit is contained in:
Dmitry Jemerov
2015-04-24 16:18:54 +02:00
parent f374eec8f1
commit 8258d72e23
4 changed files with 37 additions and 8 deletions
@@ -20,6 +20,7 @@ import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet; import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import kotlin.Function0; import kotlin.Function0;
import kotlin.Function1; import kotlin.Function1;
import kotlin.KotlinPackage; 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.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver; 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.*;
import org.jetbrains.kotlin.types.checker.JetTypeChecker; import org.jetbrains.kotlin.types.checker.JetTypeChecker;
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryPackage;
@@ -249,8 +249,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
} }
Collection<JetType> possibleTypes = DataFlowUtils.getAllPossibleTypes( Collection<JetType> possibleTypes = DataFlowUtils.getAllPossibleTypes(
expression.getLeft(), context.dataFlowInfo, actualType, context); expression.getLeft(), context.dataFlowInfo, actualType, context);
boolean checkExactType = checkExactTypeForUselessCast(expression);
for (JetType possibleType : possibleTypes) { 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)); context.trace.report(USELESS_CAST.on(expression));
return; 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 @Override
public JetTypeInfo visitThisExpression(@NotNull JetThisExpression expression, ExpressionTypingContext context) { public JetTypeInfo visitThisExpression(@NotNull JetThisExpression expression, ExpressionTypingContext context) {
JetType result = null; JetType result = null;
+7 -2
View File
@@ -9,10 +9,15 @@ fun test() : Unit {
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 <!USELESS_CAST!>as Int?<!>)
checkSubtype<Int?>(y <!USELESS_CAST!>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 <!USELESS_CAST!>as? Int?<!>)
checkSubtype<Int?>(y <!USELESS_CAST!>as? Int?<!>) checkSubtype<Int?>(y as? Int?)
val <!UNUSED_VARIABLE!>s<!> = "" <!USELESS_CAST!>as Any<!>
("" as String?)?.length()
(@data("" as String?))?.length()
([data]( "" as String?))?.length()
Unit Unit
} }
@@ -15,6 +15,6 @@ fun bar() {
val x = X val x = X
x.foo() x.foo()
X.foo() X.foo()
(X <!USELESS_CAST!>as C<!>).foo() (X as C).foo()
((if (1<2) X else Y) <!USELESS_CAST!>as C<!>).foo() ((if (1<2) X else Y) <!USELESS_CAST!>as C<!>).foo()
} }
@@ -13,16 +13,16 @@ class B: A()
fun test(a: Any?) { fun test(a: Any?) {
if (a is B) { if (a is B) {
(a <!USELESS_CAST!>as A<!>).foo() (a as A).foo()
} }
} }
fun test1(a: B) { fun test1(a: B) {
(a <!USELESS_CAST!>as A?<!>)?.foo() (a as A?)?.foo()
} }
fun test2(b: B?) { fun test2(b: B?) {
if (b != null) { if (b != null) {
(b <!USELESS_CAST!>as A<!>).foo() (b as A).foo()
} }
} }