diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index 930580fd640..5e684650483 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.psi.*; @@ -513,11 +514,20 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } } - KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(context); KotlinTypeInfo tryResult = facade.getTypeInfo(tryBlock, context); + ExpressionTypingContext tryOutputContext = context.replaceExpectedType(NO_EXPECTED_TYPE); + if (!nothingInAllCatchBranches && + facade.getComponents().languageVersionSettings.supportsFeature(LanguageFeature.SoundSmartCastsAfterTry)) { + PreliminaryLoopVisitor tryVisitor = PreliminaryLoopVisitor.visitTryBlock(expression); + tryOutputContext = tryOutputContext.replaceDataFlowInfo( + tryVisitor.clearDataFlowInfoForAssignedLocalVariables(tryOutputContext.dataFlowInfo, + components.languageVersionSettings) + ); + } + + KotlinTypeInfo result = TypeInfoFactoryKt.noTypeInfo(tryOutputContext); if (finallyBlock != null) { - result = facade.getTypeInfo(finallyBlock.getFinalExpression(), - context.replaceExpectedType(NO_EXPECTED_TYPE)); + result = facade.getTypeInfo(finallyBlock.getFinalExpression(), tryOutputContext); } else if (nothingInAllCatchBranches) { result = tryResult; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt index 1c02e5673ab..5fe06643f56 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PreliminaryLoopVisitor.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.types.expressions import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.psi.KtLoopExpression +import org.jetbrains.kotlin.psi.KtTryExpression import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue import org.jetbrains.kotlin.resolve.calls.smartcasts.IdentifierInfo @@ -59,5 +60,12 @@ class PreliminaryLoopVisitor private constructor() : AssignedVariablesSearcher() loopExpression.accept(visitor, null) return visitor } + + @JvmStatic + fun visitTryBlock(tryExpression: KtTryExpression): PreliminaryLoopVisitor { + val visitor = PreliminaryLoopVisitor() + tryExpression.tryBlock.accept(visitor, null) + return visitor + } } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNotNullInTry.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNotNullInTry.kt index 3d655de33a0..5cabdad83b5 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNotNullInTry.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNotNullInTry.kt @@ -1,3 +1,5 @@ +// !LANGUAGE: +SoundSmartCastsAfterTry + fun bar(arg: Any?) = arg fun foo() { @@ -6,7 +8,7 @@ fun foo() { try { s = "Test" } catch (ex: Exception) {} - bar(s) - if (s != null) { } - s.hashCode() + bar(s) + if (s != null) { } + s.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.kt index c88165dda4a..48f791d2f26 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTry.kt @@ -1,8 +1,10 @@ +// !LANGUAGE: +SoundSmartCastsAfterTry + fun foo() { var s: String? s = "Test" try { s = null } catch (ex: Exception) {} - s.hashCode() + s.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.kt index 073a371397d..108b479a032 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryFinally.kt @@ -1,3 +1,5 @@ +// !LANGUAGE: +SoundSmartCastsAfterTry + fun bar() {} fun foo() { @@ -10,5 +12,5 @@ fun foo() { finally { bar() } - s.hashCode() + s.hashCode() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.kt new file mode 100644 index 00000000000..c88165dda4a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.kt @@ -0,0 +1,8 @@ +fun foo() { + var s: String? + s = "Test" + try { + s = null + } catch (ex: Exception) {} + s.hashCode() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.txt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.txt new file mode 100644 index 00000000000..65a6ac47e1f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.txt @@ -0,0 +1,3 @@ +package + +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setSameInTry.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setSameInTry.kt index 78573861389..a95798dcb66 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setSameInTry.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/setSameInTry.kt @@ -1,8 +1,11 @@ +// !LANGUAGE: +SoundSmartCastsAfterTry + fun foo() { var s: String? s = "Test" try { s = "Other" } catch (ex: Exception) {} - s.hashCode() + // Problem: here we do not see that 's' is always not-null + s.hashCode() } \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 8688241539a..40f5418bd05 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -21186,6 +21186,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("setNullInTryUnsound.kt") + public void testSetNullInTryUnsound() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/setNullInTryUnsound.kt"); + doTest(fileName); + } + @TestMetadata("setSameInTry.kt") public void testSetSameInTry() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/varnotnull/setSameInTry.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 072ecb9ef01..59d49036909 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -51,6 +51,7 @@ enum class LanguageFeature( ArrayLiteralsInAnnotations(KOTLIN_1_2), InlineDefaultFunctionalParameters(KOTLIN_1_2), + SoundSmartCastsAfterTry(KOTLIN_1_2), // Experimental features