diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java index dae5c829210..fb956277628 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiUtil.java @@ -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) { 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 febd8f381ce..a5bed06cc73 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -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 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) { diff --git a/compiler/testData/diagnostics/tests/Casts.kt b/compiler/testData/diagnostics/tests/Casts.kt index 38155e7f8ec..e3826b63ff5 100644 --- a/compiler/testData/diagnostics/tests/Casts.kt +++ b/compiler/testData/diagnostics/tests/Casts.kt @@ -10,8 +10,8 @@ fun test() : Unit { 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(y as? Int?) diff --git a/compiler/testData/diagnostics/tests/cast/UselessSafeCast.kt b/compiler/testData/diagnostics/tests/cast/UselessSafeCast.kt new file mode 100644 index 00000000000..35a70d2d6bf --- /dev/null +++ b/compiler/testData/diagnostics/tests/cast/UselessSafeCast.kt @@ -0,0 +1,22 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER, -UNUSED_VARIABLE + +fun test(x: Int?) { + val a1 = x as? Int + val a2 = x as? Int? + val a3 = x as? Number + val a4 = x as? Number? + val a5: Int? = x as? Int + val a6: Number? = x as? Int + val a7: Number? = 1 as? Number + + run { x as? Int } + run { x as? Number } + + foo(x as? Number) + + if (x is Int) { + val b = x as? Int + } +} + +fun foo(x: Number?) {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/cast/UselessSafeCast.txt b/compiler/testData/diagnostics/tests/cast/UselessSafeCast.txt new file mode 100644 index 00000000000..3a84d20171e --- /dev/null +++ b/compiler/testData/diagnostics/tests/cast/UselessSafeCast.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ x: kotlin.Number?): kotlin.Unit +public fun test(/*0*/ x: kotlin.Int?): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/nullableTypes/uselessElvis.kt b/compiler/testData/diagnostics/tests/nullableTypes/uselessElvis.kt index 28985842fc9..6f7468ea97c 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/uselessElvis.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/uselessElvis.kt @@ -33,7 +33,7 @@ fun test() { if (x != null) { takeNotNull(dependOn(x) ?: "") takeNotNull(dependOn(dependOn(x)) ?: "") - takeNotNull(dependOn(dependOn(x) as? String) ?: "") + takeNotNull(dependOn(dependOn(x) as? String) ?: "") } takeNotNull(bar()!!) diff --git a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt index 2c38c7acdc6..fb54d78d676 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/alwaysNull.kt @@ -16,7 +16,7 @@ fun String?.gav() {} fun bar(s: String?) { if (s != null) return s.gav() - s as? String + s as? String s as String? s as String } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/extensionSafeCall.kt b/compiler/testData/diagnostics/tests/smartCasts/extensionSafeCall.kt index 146492c79f3..96d2c174c4c 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/extensionSafeCall.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/extensionSafeCall.kt @@ -7,7 +7,7 @@ fun T?.let(f: (T) -> Unit) { } fun test(your: Your?) { - (your?.foo() as? Any)?.let {} + (your?.foo() as? Any)?.let {} // strange smart cast to 'Your' at this point your.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/safeAs.kt b/compiler/testData/diagnostics/tests/smartCasts/safeAs.kt index 50250d74c35..5951567b45a 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/safeAs.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/safeAs.kt @@ -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 as? Any ?: return arg.hashCode() x.hashCode() } fun bar(arg: Any?) { - arg as? Any ?: return + arg as? Any ?: return arg.hashCode() } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 99064b2b5dd..e0241fcbbe4 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -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"); diff --git a/idea/testData/checker/Casts.kt b/idea/testData/checker/Casts.kt index 8ee02ec3f79..75aadf93089 100644 --- a/idea/testData/checker/Casts.kt +++ b/idea/testData/checker/Casts.kt @@ -11,7 +11,7 @@ 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?) Unit diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index 4ca5a8905d5..545dbab3cb5 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -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; - } }