From 2a022ca9e0734ff4334d991c2ba008d1eb68d056 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 17 Feb 2023 16:08:03 +0200 Subject: [PATCH] [FIR] Properly propagate deadness from try main block throw finally block ^KT-56476 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 6 + ...eeOldFrontendDiagnosticsTestGenerated.java | 6 + ...siOldFrontendDiagnosticsTestGenerated.java | 6 + .../dfa/cfg/ControlFlowGraphBuilder.kt | 47 ++- .../kotlin/fir/resolve/dfa/cfg/CFGNode.kt | 9 + .../definiteReturn/singleReturnFromTry.dot | 399 ++++++++++++++++++ .../definiteReturn/singleReturnFromTry.kt | 46 ++ .../test/runners/DiagnosticTestGenerated.java | 6 + 8 files changed, 511 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt 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 61fe508a067..8d033a6f2f5 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 @@ -6410,6 +6410,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag public void testSimpleClass() throws Exception { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.kt"); } + + @Test + @TestMetadata("singleReturnFromTry.kt") + public void testSingleReturnFromTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt"); + } } @Nested 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 da1a4246461..78d408866f8 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 @@ -6410,6 +6410,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir public void testSimpleClass() throws Exception { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.kt"); } + + @Test + @TestMetadata("singleReturnFromTry.kt") + public void testSingleReturnFromTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt"); + } } @Nested 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 7be928245a3..99498058d8a 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 @@ -6416,6 +6416,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia public void testSimpleClass() throws Exception { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.kt"); } + + @Test + @TestMetadata("singleReturnFromTry.kt") + public void testSingleReturnFromTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt"); + } } @Nested diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 4eb7a93b9bb..66b45ab8ba7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -923,15 +923,11 @@ class ControlFlowGraphBuilder { } fun exitFinallyBlock(): FinallyBlockExitNode { - val enterNode = finallyBlocksInProgress.pop() + val enterNode = finallyBlocksInProgress.top() val tryExitNode = tryExitNodes.top() val exitNode = createFinallyBlockExitNode(enterNode.fir) popAndAddEdge(exitNode) - val allNormalInputsAreDead = enterNode.previousNodes.all { - val edge = enterNode.edgeFrom(it) - edge.kind.isDead || edge.label != NormalPath - } - addEdge(exitNode, tryExitNode, isDead = allNormalInputsAreDead) + addEdge(exitNode, tryExitNode, isDead = enterNode.allNormalInputsAreDead) // TODO: there should also be edges to outer catch blocks? Control flow can go like this: // try { try { throw E2() } catch (e: E1) { } finally { } } catch (e: E2) { } // \-----------------------------^ \-----------------^ @@ -960,6 +956,12 @@ class ControlFlowGraphBuilder { return exitNode } + private val FinallyBlockEnterNode.allNormalInputsAreDead: Boolean + get() = previousNodes.all { + val edge = edgeFrom(it) + edge.kind.isDead || edge.label != NormalPath + } + private fun CFGNode<*>.addReturnEdges(nodes: Iterable, minLevel: Int) where T : CFGNode<*>, T : EdgeLabel { for (node in nodes) { when { @@ -974,8 +976,23 @@ class ControlFlowGraphBuilder { } fun exitTryExpression(callCompleted: Boolean): TryExpressionExitNode { - notCompletedFunctionCalls.pop().forEach(::completeFunctionCall) + var haveNothingReturnCall = false + notCompletedFunctionCalls.pop().forEach { haveNothingReturnCall = completeFunctionCall(it) || haveNothingReturnCall } val node = tryExitNodes.pop() + if (node.fir.finallyBlock != null) { + val enterFinallyNode = finallyBlocksInProgress.pop() + /** + * If it appears that after completion try main expression returns nothing and try has finally block, + * we should make edge from finally exist to try exit a dead (and it may be not dead originally + * before completion) + */ + if (haveNothingReturnCall && enterFinallyNode.allNormalInputsAreDead) { + val exitFinallyNode = node.previousNodes.single() + assert(exitFinallyNode is FinallyBlockExitNode) + CFGNode.removeAllIncomingEdges(node) + addEdge(exitFinallyNode, node, isDead = true) + } + } mergeDataFlowFromPostponedLambdas(node, callCompleted) node.updateDeadStatus() lastNodes.push(node) @@ -1001,13 +1018,14 @@ class ControlFlowGraphBuilder { private fun levelOfNextExceptionCatchingGraph(): Int = graphs.all().first { it.kind != ControlFlowGraph.Kind.AnonymousFunctionCalledInPlace }.exitNode.level - //this is a workaround to make function call dead when call is completed _after_ building its node in the graph - //this happens when completing the last call in try/catch blocks - //todo this doesn't make fully 'right' Nothing node (doesn't support going to catch and pass through finally) - // because doing those afterwards is quite challenging - // it would be much easier if we could build calls after full completion only, at least for Nothing calls - private fun completeFunctionCall(node: FunctionCallNode) { - if (!node.fir.resultType.isNothing) return + // this is a workaround to make function call dead when call is completed _after_ building its node in the graph + // this happens when completing the last call in try/catch blocks + // todo this doesn't make fully 'right' Nothing node (doesn't support going to catch and pass through finally) + // because doing those afterwards is quite challenging + // it would be much easier if we could build calls after full completion only, at least for Nothing calls + // @returns `true` if node actually returned Nothing + private fun completeFunctionCall(node: FunctionCallNode): Boolean { + if (!node.fir.resultType.isNothing) return false val stub = StubNode(node.owner, node.level) val edges = node.followingNodes.map { it to node.edgeTo(it) } CFGNode.removeAllOutgoingEdges(node) @@ -1018,6 +1036,7 @@ class ControlFlowGraphBuilder { to.updateDeadStatus() propagateDeadnessForward(to) } + return true } // ----------------------------------- Resolvable call ----------------------------------- diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt index 6056ecc6669..5f780cfd027 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt @@ -77,6 +77,15 @@ sealed class CFGNode(val owner: ControlFlowGraph, val level: } from._followingNodes.clear() } + + @CfgInternals + fun removeAllIncomingEdges(to: CFGNode<*>) { + for (from in to._previousNodes) { + from._followingNodes.remove(to) + } + to._previousNodes.clear() + to._incomingEdges?.clear() + } } private val _previousNodes: MutableList> = SmartList() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot new file mode 100644 index 00000000000..2bbdaaaced9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.dot @@ -0,0 +1,399 @@ +digraph singleReturnFromTry_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function myRun" style="filled" fillcolor=red]; + subgraph cluster_1 { + color=blue + 1 [label="Enter block"]; + 2 [label="Function call: R|/block|.R|SubstitutionOverride|()" style="filled" fillcolor=yellow]; + 3 [label="Jump: ^myRun R|/block|.R|SubstitutionOverride|()"]; + 4 [label="Stub" style="filled" fillcolor=gray]; + 5 [label="Exit block" style="filled" fillcolor=gray]; + } + 6 [label="Exit function myRun" style="filled" fillcolor=red]; + } + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {6}; + 3 -> {4} [style=dotted]; + 4 -> {5} [style=dotted]; + 5 -> {6} [style=dotted]; + + subgraph cluster_2 { + color=red + 7 [label="Enter function test_1" style="filled" fillcolor=red]; + subgraph cluster_3 { + color=blue + 8 [label="Enter block"]; + 9 [label="Postponed enter to lambda"]; + subgraph cluster_4 { + color=blue + 10 [label="Enter function " style="filled" fillcolor=red]; + subgraph cluster_5 { + color=blue + 11 [label="Enter block"]; + 12 [label="Const: Int(2)"]; + 13 [label="Jump: ^test_1 Int(2)"]; + 14 [label="Stub" style="filled" fillcolor=gray]; + 15 [label="Exit block" style="filled" fillcolor=gray]; + } + 16 [label="Exit function " style="filled" fillcolor=gray]; + } + 17 [label="Postponed exit from lambda"]; + 18 [label="Function call: R|/myRun|(...)" style="filled" fillcolor=yellow]; + 19 [label="Stub" style="filled" fillcolor=gray]; + 20 [label="Exit block" style="filled" fillcolor=gray]; + } + 21 [label="Exit function test_1" style="filled" fillcolor=red]; + } + 7 -> {8}; + 8 -> {9}; + 9 -> {10 17 18}; + 9 -> {10} [style=dashed]; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {21}; + 13 -> {14} [style=dotted]; + 14 -> {15} [style=dotted]; + 15 -> {16} [style=dotted]; + 16 -> {17} [style=dotted]; + 17 -> {18}; + 17 -> {9} [color=green style=dashed]; + 18 -> {19} [style=dotted]; + 19 -> {20} [style=dotted]; + 20 -> {21} [style=dotted]; + + subgraph cluster_6 { + color=red + 22 [label="Enter function test_2" style="filled" fillcolor=red]; + subgraph cluster_7 { + color=blue + 23 [label="Enter block"]; + subgraph cluster_8 { + color=blue + 24 [label="Try expression enter"]; + subgraph cluster_9 { + color=blue + 25 [label="Try main block enter"]; + subgraph cluster_10 { + color=blue + 26 [label="Enter block"]; + 27 [label="Const: Int(2)"]; + 28 [label="Jump: ^test_2 Int(2)"]; + 29 [label="Stub" style="filled" fillcolor=gray]; + 30 [label="Exit block" style="filled" fillcolor=gray]; + } + 31 [label="Try main block exit" style="filled" fillcolor=gray]; + } + subgraph cluster_11 { + color=blue + 32 [label="Enter finally"]; + subgraph cluster_12 { + color=blue + 33 [label="Enter block"]; + 34 [label="Exit block"]; + } + 35 [label="Exit finally"]; + } + 36 [label="Try expression exit" style="filled" fillcolor=gray]; + } + 37 [label="Exit block" style="filled" fillcolor=gray]; + } + 38 [label="Exit function test_2" style="filled" fillcolor=red]; + } + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 24 -> {32} [label="onUncaughtException"]; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {32} [label="return@/test_2"]; + 28 -> {29} [style=dotted]; + 29 -> {30} [style=dotted]; + 30 -> {31} [style=dotted]; + 31 -> {32} [style=dotted]; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {38} [label="return@/test_2"]; + 35 -> {36} [style=dotted]; + 36 -> {37} [style=dotted]; + 37 -> {38} [style=dotted]; + + subgraph cluster_13 { + color=red + 39 [label="Enter function test_3" style="filled" fillcolor=red]; + subgraph cluster_14 { + color=blue + 40 [label="Enter block"]; + subgraph cluster_15 { + color=blue + 41 [label="Try expression enter"]; + subgraph cluster_16 { + color=blue + 42 [label="Try main block enter"]; + subgraph cluster_17 { + color=blue + 43 [label="Enter block"]; + 44 [label="Postponed enter to lambda"]; + subgraph cluster_18 { + color=blue + 45 [label="Enter function " style="filled" fillcolor=red]; + subgraph cluster_19 { + color=blue + 46 [label="Enter block"]; + 47 [label="Const: Int(2)"]; + 48 [label="Jump: ^test_3 Int(2)"]; + 49 [label="Stub" style="filled" fillcolor=gray]; + 50 [label="Exit block" style="filled" fillcolor=gray]; + } + 51 [label="Exit function " style="filled" fillcolor=gray]; + } + 52 [label="Postponed exit from lambda"]; + 53 [label="Function call: R|/myRun|(...)" style="filled" fillcolor=yellow]; + 54 [label="Stub" style="filled" fillcolor=gray]; + 55 [label="Exit block" style="filled" fillcolor=gray]; + } + 56 [label="Try main block exit" style="filled" fillcolor=gray]; + } + subgraph cluster_20 { + color=blue + 57 [label="Enter finally"]; + subgraph cluster_21 { + color=blue + 58 [label="Enter block"]; + 59 [label="Exit block"]; + } + 60 [label="Exit finally"]; + } + 61 [label="Try expression exit" style="filled" fillcolor=gray]; + } + 62 [label="Exit block" style="filled" fillcolor=gray]; + } + 63 [label="Exit function test_3" style="filled" fillcolor=red]; + } + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 41 -> {57} [label="onUncaughtException"]; + 42 -> {43}; + 43 -> {44}; + 44 -> {45 52 53}; + 44 -> {45} [style=dashed]; + 45 -> {46}; + 46 -> {47}; + 47 -> {48}; + 48 -> {57} [label="return@/test_3"]; + 48 -> {49} [style=dotted]; + 49 -> {50} [style=dotted]; + 50 -> {51} [style=dotted]; + 51 -> {52} [style=dotted]; + 52 -> {44} [color=green style=dashed]; + 52 -> {53} [color=green]; + 52 -> {61} [style=dotted]; + 53 -> {54} [style=dotted]; + 54 -> {57} [style=dotted label="onUncaughtException"]; + 54 -> {55} [style=dotted]; + 55 -> {56} [style=dotted]; + 56 -> {57} [style=dotted]; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; + 60 -> {63} [label="return@/test_3"]; + 60 -> {61} [style=dotted]; + 61 -> {62} [style=dotted]; + 62 -> {63} [style=dotted]; + + subgraph cluster_22 { + color=red + 64 [label="Enter function test_4" style="filled" fillcolor=red]; + subgraph cluster_23 { + color=blue + 65 [label="Enter block"]; + 66 [label="Postponed enter to lambda"]; + subgraph cluster_24 { + color=blue + 67 [label="Enter function " style="filled" fillcolor=red]; + subgraph cluster_25 { + color=blue + 68 [label="Enter block"]; + subgraph cluster_26 { + color=blue + 69 [label="Try expression enter"]; + subgraph cluster_27 { + color=blue + 70 [label="Try main block enter"]; + subgraph cluster_28 { + color=blue + 71 [label="Enter block"]; + 72 [label="Const: Int(2)"]; + 73 [label="Jump: ^test_4 Int(2)"]; + 74 [label="Stub" style="filled" fillcolor=gray]; + 75 [label="Exit block" style="filled" fillcolor=gray]; + } + 76 [label="Try main block exit" style="filled" fillcolor=gray]; + } + subgraph cluster_29 { + color=blue + 77 [label="Enter finally"]; + subgraph cluster_30 { + color=blue + 78 [label="Enter block"]; + 79 [label="Exit block"]; + } + 80 [label="Exit finally"]; + } + 81 [label="Try expression exit" style="filled" fillcolor=gray]; + } + 82 [label="Exit block" style="filled" fillcolor=gray]; + } + 83 [label="Exit function " style="filled" fillcolor=gray]; + } + 84 [label="Postponed exit from lambda"]; + 85 [label="Function call: R|/myRun|(...)" style="filled" fillcolor=yellow]; + 86 [label="Stub" style="filled" fillcolor=gray]; + 87 [label="Exit block" style="filled" fillcolor=gray]; + } + 88 [label="Exit function test_4" style="filled" fillcolor=red]; + } + 64 -> {65}; + 65 -> {66}; + 66 -> {67 84 85}; + 66 -> {67} [style=dashed]; + 67 -> {68}; + 68 -> {69}; + 69 -> {70}; + 69 -> {77} [label="onUncaughtException"]; + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + 73 -> {77} [label="return@/test_4"]; + 73 -> {74} [style=dotted]; + 74 -> {75} [style=dotted]; + 75 -> {76} [style=dotted]; + 76 -> {77} [style=dotted]; + 77 -> {78}; + 78 -> {79}; + 79 -> {80}; + 80 -> {88} [label="return@/test_4"]; + 80 -> {81} [style=dotted]; + 81 -> {82} [style=dotted]; + 82 -> {83} [style=dotted]; + 83 -> {84} [style=dotted]; + 84 -> {85}; + 84 -> {66} [color=green style=dashed]; + 85 -> {86} [style=dotted]; + 86 -> {87} [style=dotted]; + 87 -> {88} [style=dotted]; + + subgraph cluster_31 { + color=red + 89 [label="Enter function test_5" style="filled" fillcolor=red]; + subgraph cluster_32 { + color=blue + 90 [label="Enter block"]; + subgraph cluster_33 { + color=blue + 91 [label="Try expression enter"]; + subgraph cluster_34 { + color=blue + 92 [label="Try main block enter"]; + subgraph cluster_35 { + color=blue + 93 [label="Enter block"]; + 94 [label="Postponed enter to lambda"]; + subgraph cluster_36 { + color=blue + 95 [label="Enter function " style="filled" fillcolor=red]; + subgraph cluster_37 { + color=blue + 96 [label="Enter block"]; + 97 [label="Const: Int(2)"]; + 98 [label="Jump: ^test_5 Int(2)"]; + 99 [label="Stub" style="filled" fillcolor=gray]; + 100 [label="Exit block" style="filled" fillcolor=gray]; + } + 101 [label="Exit function " style="filled" fillcolor=gray]; + } + 102 [label="Postponed exit from lambda"]; + 103 [label="Function call: R|/myRun|(...)" style="filled" fillcolor=yellow]; + 104 [label="Stub" style="filled" fillcolor=gray]; + 105 [label="Exit block" style="filled" fillcolor=gray]; + } + 106 [label="Try main block exit" style="filled" fillcolor=gray]; + } + subgraph cluster_38 { + color=blue + 107 [label="Catch enter"]; + 108 [label="Variable declaration: e: R|kotlin/Exception|"]; + subgraph cluster_39 { + color=blue + 109 [label="Enter block"]; + 110 [label="Const: String(hello)"]; + 111 [label="Exit block"]; + } + 112 [label="Catch exit"]; + } + subgraph cluster_40 { + color=blue + 113 [label="Enter finally"]; + subgraph cluster_41 { + color=blue + 114 [label="Enter block"]; + 115 [label="Exit block"]; + } + 116 [label="Exit finally"]; + } + 117 [label="Try expression exit"]; + } + 118 [label="Exit block"]; + } + 119 [label="Exit function test_5" style="filled" fillcolor=red]; + } + 89 -> {90}; + 90 -> {91}; + 91 -> {92 107}; + 91 -> {113} [label="onUncaughtException"]; + 92 -> {93}; + 93 -> {94}; + 94 -> {95 102 103}; + 94 -> {95} [style=dashed]; + 95 -> {96}; + 96 -> {97}; + 97 -> {98}; + 98 -> {113} [label="return@/test_5"]; + 98 -> {99} [style=dotted]; + 99 -> {100} [style=dotted]; + 100 -> {101} [style=dotted]; + 101 -> {102} [style=dotted]; + 102 -> {94} [color=green style=dashed]; + 102 -> {103} [color=green]; + 102 -> {117} [color=red]; + 103 -> {104} [style=dotted]; + 104 -> {113} [style=dotted label="onUncaughtException"]; + 104 -> {105 107} [style=dotted]; + 105 -> {106} [style=dotted]; + 106 -> {107 113} [style=dotted]; + 107 -> {108}; + 107 -> {113} [label="onUncaughtException"]; + 108 -> {109}; + 109 -> {110}; + 110 -> {111}; + 111 -> {112}; + 112 -> {113}; + 113 -> {114}; + 114 -> {115}; + 115 -> {116}; + 116 -> {117}; + 116 -> {119} [label="return@/test_5"]; + 117 -> {118}; + 118 -> {119}; + +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt new file mode 100644 index 00000000000..2c4ff4a9445 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt @@ -0,0 +1,46 @@ +// FIR_IDENTICAL +// DUMP_CFG +// DIAGNOSTICS: -UNUSED_EXPRESSION +// ISSUE: KT-56476 + +inline fun myRun(block: () -> R): R { + return block() +} + +fun test_1(): Int { + myRun { + return 2 + } +} + +fun test_2(): Int { + try { + return 2 + } finally {} +} + +fun test_3(): Int { + try { + myRun { + return 2 + } + } finally {} +} + +fun test_4(): Int { + myRun { + try { + return 2 + } finally {} + } +} + +fun test_5(): Int { // should be an error about missing return + try { + myRun { + return 2 + } + } catch (e: Exception) { + "hello" + } finally {} +} 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 931a83df48c..0638e3b8c16 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 @@ -6416,6 +6416,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { public void testSimpleClass() throws Exception { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.kt"); } + + @Test + @TestMetadata("singleReturnFromTry.kt") + public void testSingleReturnFromTry() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/singleReturnFromTry.kt"); + } } @Nested