Support warning about useless cast on safe cast
#KT-13348 Fixed
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -541,6 +541,14 @@ public class KtPsiUtil {
|
||||
((KtBinaryExpression) element).getOperationToken().equals(KtTokens.EQ);
|
||||
}
|
||||
|
||||
public static boolean isSafeCast(@NotNull KtBinaryExpressionWithTypeRHS expression) {
|
||||
return expression.getOperationReference().getReferencedNameElementType() == KtTokens.AS_SAFE;
|
||||
}
|
||||
|
||||
public static boolean isUnsafeCast(@NotNull KtBinaryExpressionWithTypeRHS expression) {
|
||||
return expression.getOperationReference().getReferencedNameElementType() == KtTokens.AS_KEYWORD;
|
||||
}
|
||||
|
||||
public static boolean checkVariableDeclarationInBlock(@NotNull KtBlockExpression block, @NotNull String varName) {
|
||||
for (KtExpression element : block.getStatements()) {
|
||||
if (element instanceof KtVariableDeclaration) {
|
||||
|
||||
+5
-3
@@ -26,7 +26,6 @@ import kotlin.TuplesKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.KtNodeTypes;
|
||||
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||
@@ -395,13 +394,16 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
@NotNull KotlinType actualType,
|
||||
@NotNull KotlinTypeChecker typeChecker
|
||||
) {
|
||||
// Here: x as? Type <=> x as Type?
|
||||
KotlinType refinedTargetType = KtPsiUtil.isSafeCast(expression) ? TypeUtils.makeNullable(targetType) : targetType;
|
||||
|
||||
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);
|
||||
? isExactTypeCast(intersectedType, refinedTargetType)
|
||||
: isUpcast(intersectedType, refinedTargetType, typeChecker);
|
||||
}
|
||||
|
||||
private static boolean shouldCheckForExactType(KtBinaryExpressionWithTypeRHS expression, KotlinType expectedType) {
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ fun test() : Unit {
|
||||
checkSubtype<Int>(y <!USELESS_CAST!>as Int<!>)
|
||||
checkSubtype<Int?>(x as Int?)
|
||||
checkSubtype<Int?>(y as Int?)
|
||||
checkSubtype<Int?>(x <!USELESS_CAST!>as? Int<!>)
|
||||
checkSubtype<Int?>(y <!USELESS_CAST!>as? Int<!>)
|
||||
checkSubtype<Int?>(x as? Int)
|
||||
checkSubtype<Int?>(y as? Int)
|
||||
checkSubtype<Int?>(x as? Int?)
|
||||
checkSubtype<Int?>(y as? Int?)
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER, -UNUSED_VARIABLE
|
||||
|
||||
fun test(x: Int?) {
|
||||
val a1 = x <!USELESS_CAST!>as? Int<!>
|
||||
val a2 = x <!USELESS_CAST!>as? Int?<!>
|
||||
val a3 = x as? Number
|
||||
val a4 = x as? Number?
|
||||
val a5: Int? = x <!USELESS_CAST!>as? Int<!>
|
||||
val a6: Number? = x <!USELESS_CAST!>as? Int<!>
|
||||
val a7: Number? = 1 <!USELESS_CAST!>as? Number<!>
|
||||
|
||||
run { x <!USELESS_CAST!>as? Int<!> }
|
||||
run { x as? Number }
|
||||
|
||||
foo(x as? Number)
|
||||
|
||||
if (x is Int) {
|
||||
val b = x as? Int
|
||||
}
|
||||
}
|
||||
|
||||
fun foo(x: Number?) {}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ x: kotlin.Number?): kotlin.Unit
|
||||
public fun test(/*0*/ x: kotlin.Int?): kotlin.Unit
|
||||
@@ -33,7 +33,7 @@ fun test() {
|
||||
if (x != null) {
|
||||
takeNotNull(dependOn(x) <!USELESS_ELVIS!>?: ""<!>)
|
||||
takeNotNull(dependOn(dependOn(x)) <!USELESS_ELVIS!>?: ""<!>)
|
||||
takeNotNull(dependOn(dependOn(x) <!USELESS_CAST!>as? String<!>) ?: "")
|
||||
takeNotNull(dependOn(dependOn(x) as? String) ?: "")
|
||||
}
|
||||
|
||||
takeNotNull(bar()!!)
|
||||
|
||||
@@ -16,7 +16,7 @@ fun String?.gav() {}
|
||||
fun bar(s: String?) {
|
||||
if (s != null) return
|
||||
<!DEBUG_INFO_CONSTANT!>s<!>.gav()
|
||||
<!DEBUG_INFO_CONSTANT!>s<!> as? String
|
||||
<!DEBUG_INFO_CONSTANT!>s<!> <!USELESS_CAST!>as? String<!>
|
||||
<!DEBUG_INFO_CONSTANT!>s<!> <!USELESS_CAST!>as String?<!>
|
||||
<!ALWAYS_NULL!>s<!> as String
|
||||
}
|
||||
@@ -7,7 +7,7 @@ fun <T> T?.let(f: (T) -> Unit) {
|
||||
}
|
||||
|
||||
fun test(your: Your?) {
|
||||
(your?.foo() as? Any)?.let {}
|
||||
(your?.foo() <!USELESS_CAST!>as? Any<!>)?.let {}
|
||||
// strange smart cast to 'Your' at this point
|
||||
your<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
// See also KT-10992: we should have no errors for all unsafe hashCode() calls
|
||||
|
||||
fun foo(arg: Any?) {
|
||||
val x = arg as? Any ?: return
|
||||
val x = arg <!USELESS_CAST!>as? Any<!> ?: return
|
||||
<!DEBUG_INFO_SMARTCAST!>arg<!>.hashCode()
|
||||
x.hashCode()
|
||||
}
|
||||
|
||||
fun bar(arg: Any?) {
|
||||
arg as? Any ?: return
|
||||
arg <!USELESS_CAST!>as? Any<!> ?: return
|
||||
<!DEBUG_INFO_SMARTCAST!>arg<!>.hashCode()
|
||||
}
|
||||
|
||||
|
||||
@@ -2974,6 +2974,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("UselessSafeCast.kt")
|
||||
public void testUselessSafeCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/UselessSafeCast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("WhenErasedDisallowFromAny.kt")
|
||||
public void testWhenErasedDisallowFromAny() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/WhenErasedDisallowFromAny.kt");
|
||||
|
||||
Vendored
+1
-1
@@ -11,7 +11,7 @@ fun test() : Unit {
|
||||
checkSubtype<Int?>(x as Int?)
|
||||
checkSubtype<Int?>(y as Int?)
|
||||
checkSubtype<Int?>(x as Int?)
|
||||
checkSubtype<Int?>(y <warning descr="[USELESS_CAST] No cast needed">as? Int</warning>)
|
||||
checkSubtype<Int?>(y as? Int)
|
||||
checkSubtype<Int?>(x as? Int?)
|
||||
checkSubtype<Int?>(y as? Int?)
|
||||
Unit
|
||||
|
||||
+2
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
@@ -41,7 +41,6 @@ import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
@@ -59,7 +58,7 @@ import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt.getNameI
|
||||
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeByReference;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.equality;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not;
|
||||
import static org.jetbrains.kotlin.psi.KtPsiUtil.findChildByType;
|
||||
import static org.jetbrains.kotlin.psi.KtPsiUtil.*;
|
||||
import static org.jetbrains.kotlin.types.TypeUtils.*;
|
||||
|
||||
public final class PatternTranslator extends AbstractTranslator {
|
||||
@@ -324,12 +323,4 @@ public final class PatternTranslator extends AbstractTranslator {
|
||||
public JsExpression translateExpressionForExpressionPattern(@NotNull KtExpression patternExpression) {
|
||||
return Translation.translateAsExpression(patternExpression, context());
|
||||
}
|
||||
|
||||
private static boolean isSafeCast(@NotNull KtBinaryExpressionWithTypeRHS expression) {
|
||||
return expression.getOperationReference().getReferencedNameElementType() == KtTokens.AS_SAFE;
|
||||
}
|
||||
|
||||
private static boolean isUnsafeCast(@NotNull KtBinaryExpressionWithTypeRHS expression) {
|
||||
return expression.getOperationReference().getReferencedNameElementType() == KtTokens.AS_KEYWORD;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user