diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index d0e2faf9f1c..09e0a53e172 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -8062,6 +8062,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.kt"); } + @Test + @TestMetadata("tryCatchFinallyIfs.kt") + public void testTryCatchFinallyIfs() { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt"); + } + @Test @TestMetadata("tryWithAssignmentUsedInCatch.kt") public void testTryWithAssignmentUsedInCatch() { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 9fa3d54c637..5ae0538ed5a 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -8062,6 +8062,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.kt"); } + @Test + @TestMetadata("tryCatchFinallyIfs.kt") + public void testTryCatchFinallyIfs() { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt"); + } + @Test @TestMetadata("tryWithAssignmentUsedInCatch.kt") public void testTryWithAssignmentUsedInCatch() { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 43004a3d068..83b0fe3d2a6 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -8056,6 +8056,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.kt"); } + @Test + @TestMetadata("tryCatchFinallyIfs.kt") + public void testTryCatchFinallyIfs() { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt"); + } + @Test @TestMetadata("tryWithAssignmentUsedInCatch.kt") public void testTryWithAssignmentUsedInCatch() { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 326a1c66abb..991bfbaa360 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -8062,6 +8062,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.kt"); } + @Test + @TestMetadata("tryCatchFinallyIfs.kt") + public void testTryCatchFinallyIfs() { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt"); + } + @Test @TestMetadata("tryWithAssignmentUsedInCatch.kt") public void testTryWithAssignmentUsedInCatch() { diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/AbstractLightTreeRawFirBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/AbstractLightTreeRawFirBuilder.kt index b63133fa76e..1c9bcaa6f0a 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/AbstractLightTreeRawFirBuilder.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/AbstractLightTreeRawFirBuilder.kt @@ -90,7 +90,7 @@ abstract class AbstractLightTreeRawFirBuilder( } } - private fun LighterASTNode.getLastChildExpression(): LighterASTNode? { + fun LighterASTNode.getLastChildExpression(): LighterASTNode? { var result: LighterASTNode? = null forEachChildren { if (it.isExpression()) { diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt index 34d5371b657..50e86107ed6 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirExpressionBuilder.kt @@ -1402,13 +1402,19 @@ class LightTreeRawFirExpressionBuilder( parent = parent.getParent() ?: return true } val parentTokenType = parent.tokenType - if (parentTokenType == BLOCK) return false - if (parentTokenType == THEN || parentTokenType == ELSE || parentTokenType == WHEN_ENTRY) { - return parent.getParent()?.usedAsExpression ?: true + return when (parentTokenType) { + BLOCK -> parent.getLastChildExpression() == this && parent.usedAsExpression + TRY, CATCH -> parent.usedAsExpression + THEN, ELSE, WHEN_ENTRY -> parent.getParent()?.usedAsExpression ?: true + CLASS_INITIALIZER, SCRIPT_INITIALIZER, SECONDARY_CONSTRUCTOR, FUNCTION_LITERAL, FINALLY -> false + FUN, PROPERTY_ACCESSOR -> parent.getChildrenAsArray().any { it?.tokenType == EQ } + DOT_QUALIFIED_EXPRESSION -> parent.getFirstChild() == this + BODY -> when (parent.getParent()?.tokenType) { + FOR, WHILE, DO_WHILE -> false + else -> true + } + else -> true } - if (parentTokenType != BODY) return true - val type = parent.getParent()?.tokenType ?: return true - return !(type == FOR || type == WHILE || type == DO_WHILE) } /** diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt index 2097b0cb87e..2284454166c 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/PsiRawFirBuilder.kt @@ -2690,17 +2690,33 @@ open class PsiRawFirBuilder( ) { parent = parent.parent } - if (parent is KtBlockExpression) return false + when (parent.elementType) { KtNodeTypes.THEN, KtNodeTypes.ELSE, KtNodeTypes.WHEN_ENTRY -> { return (parent.parent as? KtExpression)?.usedAsExpression != false } } - if (parent is KtScriptInitializer) return false - // Here we check that when used is a single statement of a loop - if (parent !is KtContainerNodeForControlStructureBody) return true - val type = parent.parent.elementType - return !(type == KtNodeTypes.FOR || type == KtNodeTypes.WHILE || type == KtNodeTypes.DO_WHILE) + + fun PsiElement.getLastChildExpression() = children.asList().asReversed().firstIsInstanceOrNull() + + return when (parent) { +// todo KT-62472 Replace with the following when K2 is used in TeamCity +// is KtBlockExpression, is KtTryExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression + is KtBlockExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression + is KtTryExpression -> parent.getLastChildExpression() == this && parent.usedAsExpression + is KtCatchClause -> (parent.parent as? KtTryExpression)?.usedAsExpression == true + is KtClassInitializer, is KtScriptInitializer, is KtSecondaryConstructor, is KtFunctionLiteral, is KtFinallySection -> false + is KtDotQualifiedExpression -> parent.firstChild == this +// todo KT-62472 Replace with the following when K2 is used in TeamCity +// is KtFunction, is KtPropertyAccessor -> parent.hasBody() && !parent.hasBlockBody() + is KtFunction -> parent.hasBody() && !parent.hasBlockBody() + is KtPropertyAccessor -> parent.hasBody() && !parent.hasBlockBody() + is KtContainerNodeForControlStructureBody -> when (parent.parent.elementType) { + KtNodeTypes.FOR, KtNodeTypes.WHILE, KtNodeTypes.DO_WHILE -> false + else -> true + } + else -> true + } } override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: FirElement?): FirElement { diff --git a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt index 21088a65ee2..2f21e2623a6 100644 --- a/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt +++ b/compiler/testData/diagnostics/tests/FunctionCalleeExpressions.fir.kt @@ -71,7 +71,7 @@ fun main1() { 1."sdf" 1.{} - 1.if (true) {} + 1.if (true) {} } fun test() { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt new file mode 100644 index 00000000000..6545e519c35 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt @@ -0,0 +1,184 @@ +// FIR_IDENTICAL +// DIAGNOSTICS: -UNUSED_EXPRESSION + +fun ifExpr() = try { + if (true) 2 + if (true) 2 +} catch (e: Exception) { + if (true) 3 + if (true) 3 +} catch (e: Throwable) { + if (true) 4 + if (true) 4 +} finally { + if (true) 5 + if (true) 5 +} + +fun ifBlock() = try { + if (true) { + 2 + } + if (true) { + 2 + } +} catch (e: Exception) { + if (true) { + 3 + } + if (true) { + 3 + } +} catch (e: Throwable) { + if (true) { + 4 + } + if (true) { + 4 + } +} finally { + if (true) { + 5 + } + if (true) { + 5 + } +} + +fun whenExpr() = try { + when { + true -> 2 + } + when { + true -> 2 + } +} catch (e: Exception) { + when { + true -> 3 + } + when { + true -> 3 + } +} catch (e: Throwable) { + when { + true -> 4 + } + when { + true -> 4 + } +} finally { + when { + true -> 5 + } + when { + true -> 5 + } +} + +fun whenBlock() = try { + when { + true -> { + 2 + } + } + when { + true -> { + 2 + } + } +} catch (e: Exception) { + when { + true -> { + 3 + } + } + when { + true -> { + 3 + } + } +} catch (e: Throwable) { + when { + true -> { + 4 + } + } + when { + true -> { + 4 + } + } +} finally { + when { + true -> { + 5 + } + } + when { + true -> { + 5 + } + } +} + + +fun ifExpr2(): Any { + try { + if (true) 2 + if (true) 2 + } catch (e: Exception) { + if (true) 3 + if (true) 3 + } catch (e: Throwable) { + if (true) 4 + if (true) 4 + } finally { + if (true) 5 + if (true) 5 + } + + return try { + if (true) 2 + if (true) 2 + } catch (e: Exception) { + if (true) 3 + if (true) 3 + } catch (e: Throwable) { + if (true) 4 + if (true) 4 + } finally { + if (true) 5 + if (true) 5 + } +} + + +fun ifExpr3() { + try { + if (true) 2 + if (true) 2 + } catch (e: Exception) { + if (true) 3 + if (true) 3 + } catch (e: Throwable) { + if (true) 4 + if (true) 4 + } finally { + if (true) 5 + if (true) 5 + } + + try { + if (true) 2 + if (true) 2 + } catch (e: Exception) { + if (true) 3 + if (true) 3 + } catch (e: Throwable) { + if (true) 4 + if (true) 4 + } finally { + if (true) 5 + if (true) 5 + } +} diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifWhenToAnyComplexExpressions.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/ifWhenToAnyComplexExpressions.fir.kt index 280f15a6478..c86777c2f43 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/ifWhenToAnyComplexExpressions.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/ifWhenToAnyComplexExpressions.fir.kt @@ -18,7 +18,7 @@ fun testMixedIfAndWhen() = fun testWrappedExpressions() = if (true) { println() - if (true) { + if (true) { println() if (true) { println() diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt4310.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/kt4310.fir.kt index 861655d6ce9..cdd9c363593 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/kt4310.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/kt4310.fir.kt @@ -1,11 +1,11 @@ package f fun test(a: Boolean, b: Boolean): Int { - return if(a) { + return if(a) { 1 } else { - if (b) { + if (b) { 3 } - } + } } diff --git a/compiler/testData/diagnostics/tests/when/kt4434.fir.kt b/compiler/testData/diagnostics/tests/when/kt4434.fir.kt deleted file mode 100644 index 88b4514dbeb..00000000000 --- a/compiler/testData/diagnostics/tests/when/kt4434.fir.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE) - * - * SPEC VERSION: 0.1-152 - * PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1 - * expressions, when-expression, exhaustive-when-expressions -> paragraph 1 -> sentence 1 - * expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 1 - * expressions, when-expression -> paragraph 6 -> sentence 5 - */ - -// KT-4434 Missed diagnostic about else branch in when - -package test - -fun foo(): Int { - val a = "a" - return if (a.length > 0) { - when (a) { - "a" -> 1 - } - } - else { - 3 - } -} - -fun bar(): Int { - val a = "a" - if (a.length > 0) { - return when (a) { - "a" -> 1 - } - } - else { - return 3 - } -} diff --git a/compiler/testData/diagnostics/tests/when/kt4434.kt b/compiler/testData/diagnostics/tests/when/kt4434.kt index 4b1e2704686..d10b6dcf100 100644 --- a/compiler/testData/diagnostics/tests/when/kt4434.kt +++ b/compiler/testData/diagnostics/tests/when/kt4434.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL /* * KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE) * diff --git a/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.fir.kt b/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.fir.kt deleted file mode 100644 index eeeba7140d3..00000000000 --- a/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -// 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 index 3d9813cfe2d..9f827eff8a9 100644 --- a/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt +++ b/compiler/testData/diagnostics/tests/when/nonExhaustiveDependentContext.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT fun bar(a: String): String { return when { 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 d604f6e2ca0..d3192ad6716 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 @@ -8062,6 +8062,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/throwInLambda.kt"); } + @Test + @TestMetadata("tryCatchFinallyIfs.kt") + public void testTryCatchFinallyIfs() { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryCatchFinallyIfs.kt"); + } + @Test @TestMetadata("tryWithAssignmentUsedInCatch.kt") public void testTryWithAssignmentUsedInCatch() {