diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 56ce73cfc78..19099f1dfd0 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -35152,6 +35152,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("nonExhaustiveDependentContext.kt") + public void testNonExhaustiveDependentContext() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt"); + } + @Test @TestMetadata("NonExhaustivePlatformEnum.kt") public void testNonExhaustivePlatformEnum() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 1c5924433c3..357d7d832ed 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -35248,6 +35248,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("nonExhaustiveDependentContext.kt") + public void testNonExhaustiveDependentContext() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt"); + } + @Test @TestMetadata("NonExhaustivePlatformEnum.kt") public void testNonExhaustivePlatformEnum() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 9459c86a185..c080ad70215 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -35152,6 +35152,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("nonExhaustiveDependentContext.kt") + public void testNonExhaustiveDependentContext() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt"); + } + @Test @TestMetadata("NonExhaustivePlatformEnum.kt") public void testNonExhaustivePlatformEnum() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt index 668a92b5abe..0ceebaa1cf3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt @@ -91,6 +91,9 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe throw IllegalArgumentException("Should not be there") } + /** + * The synthetic call for the whole [whenExpression] might be not completed yet + */ override fun transformWhenExpression(whenExpression: FirWhenExpression, data: Any?): FirStatement { processExhaustivenessCheck(whenExpression) bodyResolveComponents.session.enumWhenTracker?.reportEnumUsageInWhen( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt index 484b79c2939..c5d145fff10 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirControlFlowStatementsResolveTransformer.kt @@ -64,7 +64,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes @Suppress("NAME_SHADOWING") var whenExpression = whenExpression.transformSubject(transformer, ResolutionMode.ContextIndependent) val subjectType = whenExpression.subject?.typeRef?.coneType?.fullyExpandedType(session) - var callCompleted = false + var completionNeeded = false context.withWhenSubjectType(subjectType, components) { when { whenExpression.branches.isEmpty() -> {} @@ -78,19 +78,39 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirAbstractBodyRes whenExpression = syntheticCallGenerator.generateCalleeForWhenExpression(whenExpression, resolutionContext) ?: run { whenExpression = whenExpression.transformSingle(whenExhaustivenessTransformer, null) - dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted) + dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted = false) whenExpression.resultType = buildErrorTypeRef { diagnostic = ConeSimpleDiagnostic("Can't resolve when expression", DiagnosticKind.InferenceError) } return@withWhenSubjectType whenExpression } - val completionResult = callCompleter.completeCall(whenExpression, data) - whenExpression = completionResult.result - callCompleted = completionResult.callCompleted + completionNeeded = true } } whenExpression = whenExpression.transformSingle(whenExhaustivenessTransformer, null) + + // This is necessary to perform outside the place where the synthetic call is created because + // exhaustiveness is not yet computed there, but at the same time to compute it properly + // we need having branches condition bes analyzed that is why we can't have call + // `whenExpression.transformSingle(whenExhaustivenessTransformer, null)` at the beginning + val callCompleted = when { + completionNeeded -> { + val completionResult = callCompleter.completeCall( + whenExpression, + // For non-exhaustive when expressions, we should complete then as independent because below + // their type is artificially replaced with Unit, while candidate symbol's return type remains the same + // So when combining two when's the inner one was erroneously resolved as a normal dependent exhaustive sub-expression + // At the same time, it all looks suspicious and inconsistent, so we hope it would be investigated at KT-55175 + if (whenExpression.isProperlyExhaustive) data else ResolutionMode.ContextIndependent, + ) + whenExpression = completionResult.result + + completionResult.callCompleted + } + else -> false + } + dataFlowAnalyzer.exitWhenExpression(whenExpression, callCompleted) whenExpression = whenExpression.replaceReturnTypeIfNotExhaustive() whenExpression diff --git a/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.fir.kt b/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.fir.kt new file mode 100644 index 00000000000..eeeba7140d3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.fir.kt @@ -0,0 +1,13 @@ +// SKIP_TXT +fun bar(a: String): String { + return when { + a.length == 1 -> { + when { // Error in K1, no error in K2 + a == "a" -> "" + a == "b" -> "" + } + } + + else -> "" + } +} diff --git a/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt b/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt new file mode 100644 index 00000000000..3d9813cfe2d --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt @@ -0,0 +1,13 @@ +// SKIP_TXT +fun bar(a: String): String { + return when { + a.length == 1 -> { + when { // Error in K1, no error in K2 + a == "a" -> "" + a == "b" -> "" + } + } + + else -> "" + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index f5c28deb708..6a888d7c267 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -35248,6 +35248,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/when/NonExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("nonExhaustiveDependentContext.kt") + public void testNonExhaustiveDependentContext() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt"); + } + @Test @TestMetadata("NonExhaustivePlatformEnum.kt") public void testNonExhaustivePlatformEnum() throws Exception {