From ee58b52a3db75f8237ca4508b82a3dfa58864a17 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Tue, 25 Jan 2022 18:39:58 +0300 Subject: [PATCH] [FIR] Fix false positive NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY --- .../testData/resolve/elvisReturnSimple.dot | 98 +++++++++++++++++++ .../resolve/elvisReturnSimple.fir.txt | 12 +++ .../testData/resolve/elvisReturnSimple.kt | 6 ++ .../dfa/cfg/ControlFlowGraphBuilder.kt | 15 ++- 4 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.dot create mode 100644 compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.dot b/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.dot new file mode 100644 index 00000000000..8d226574f23 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.dot @@ -0,0 +1,98 @@ +digraph elvisReturnSimple_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function stringReturnInLeftLen" style="filled" fillcolor=red]; + subgraph cluster_1 { + color=blue + 1 [label="Enter block"]; + subgraph cluster_2 { + color=blue + 2 [label="Enter when"]; + subgraph cluster_3 { + color=blue + 3 [label="Enter when branch condition "]; + 4 [label="Access variable R|/s|"]; + 5 [label="Const: Null(null)"]; + 6 [label="Equality operator !="]; + 7 [label="Exit when branch condition"]; + } + subgraph cluster_4 { + color=blue + 8 [label="Enter when branch condition else"]; + 9 [label="Exit when branch condition"]; + } + 10 [label="Enter when branch result"]; + subgraph cluster_5 { + color=blue + 11 [label="Enter block"]; + 12 [label="Const: Null(null)"]; + 13 [label="Exit block"]; + } + 14 [label="Exit when branch result"]; + 15 [label="Enter when branch result"]; + subgraph cluster_6 { + color=blue + 16 [label="Enter block"]; + 17 [label="Access variable R|/s|"]; + 18 [label="Access variable R|kotlin/String.length|"]; + 19 [label="Jump: ^stringReturnInLeftLen R|/s|.R|kotlin/String.length|"]; + 20 [label="Stub" style="filled" fillcolor=gray]; + 21 [label="Exit block" style="filled" fillcolor=gray]; + } + 22 [label="Exit when branch result" style="filled" fillcolor=gray]; + 23 [label="Exit when"]; + } + 24 [label="Exit lhs of ?:"]; + 25 [label="Enter rhs of ?:"]; + 26 [label="Const: Int(0)"]; + 27 [label="Jump: ^stringReturnInLeftLen Int(0)"]; + 28 [label="Stub" style="filled" fillcolor=gray]; + 29 [label="Lhs of ?: is not null" style="filled" fillcolor=gray]; + 30 [label="Exit ?:" style="filled" fillcolor=gray]; + 31 [label="Variable declaration: lval s1: R|kotlin/Nothing|" style="filled" fillcolor=gray]; + 32 [label="Exit block" style="filled" fillcolor=gray]; + } + 33 [label="Exit function stringReturnInLeftLen" style="filled" fillcolor=red]; + } + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {15 8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + 14 -> {23}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {33}; + 19 -> {20} [style=dotted]; + 20 -> {21} [style=dotted]; + 21 -> {22} [style=dotted]; + 22 -> {23} [style=dotted]; + 23 -> {24}; + 24 -> {25}; + 24 -> {29} [style=dotted]; + 25 -> {26}; + 26 -> {27}; + 27 -> {33}; + 27 -> {28} [style=dotted]; + 28 -> {30} [style=dotted]; + 29 -> {30} [style=dotted]; + 30 -> {31} [style=dotted]; + 31 -> {32} [style=dotted]; + 32 -> {33} [style=dotted]; + +} diff --git a/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.fir.txt b/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.fir.txt new file mode 100644 index 00000000000..aad16e4da34 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.fir.txt @@ -0,0 +1,12 @@ +FILE: elvisReturnSimple.kt + public final fun stringReturnInLeftLen(s: R|kotlin/String?|): R|kotlin/Int| { + lval s1: R|kotlin/Nothing| = when () { + !=(R|/s|, Null(null)) -> { + ^stringReturnInLeftLen R|/s|.R|kotlin/String.length| + } + else -> { + Null(null) + } + } + ?: ^stringReturnInLeftLen Int(0) + } diff --git a/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.kt b/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.kt new file mode 100644 index 00000000000..bc66338e144 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/elvisReturnSimple.kt @@ -0,0 +1,6 @@ +// DUMP_CFG + +fun stringReturnInLeftLen(s : String?) : Int { + // this shouldn't trigger NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY + val s1 = (if (s != null) { return s.length } else { null }) ?: return 0 +} 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 2f8760b2d55..3a6053d542c 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 @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.fir.resolve.dfa.* import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol -import org.jetbrains.kotlin.fir.types.isNothing +import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.util.ListMultimap import org.jetbrains.kotlin.fir.util.listMultimapOf import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor @@ -1317,13 +1317,22 @@ class ControlFlowGraphBuilder { exitElvisExpressionNodes.push(it) } + val typedFir = lastNodes.topOrNull()?.fir as? FirExpression + val type = typedFir?.typeRef?.coneTypeSafe() + val lhsExitNode = createElvisLhsExitNode(elvisExpression).also { popAndAddEdge(it) } val lhsIsNotNullNode = createElvisLhsIsNotNullNode(elvisExpression).also { - addEdge(lhsExitNode, it) - addEdge(it, exitNode) + val preferredKind = if (type?.isNullableNothing == true) { + EdgeKind.DeadForward + } else { + EdgeKind.Forward + } + + addEdge(lhsExitNode, it, preferredKind = preferredKind) + addEdge(it, exitNode, propagateDeadness = false) } val rhsEnterNode = elvisRhsEnterNodes.pop().also {