FIR CFG: ignore exception edges when looking for return values

^KT-54668 Fixed
This commit is contained in:
pyos
2022-11-28 11:14:07 +01:00
committed by teamcity
parent 664a70ec13
commit 9f4cfa2c50
5 changed files with 17 additions and 12 deletions
@@ -15,5 +15,5 @@ FILE: lazyValWithElvisToNothingInside.kt
} }
) )
public get(): R|kotlin/collections/Map<kotlin/String, kotlin/String>| { public get(): R|kotlin/collections/Map<kotlin/String, kotlin/String>| {
^ D|/x|.<CS errors: kotlin/getValue>#<R|kotlin/collections/Map<kotlin/String, kotlin/String>|>(Null(null), ::R|/x|) ^ D|/x|.R|kotlin/getValue|<R|kotlin/collections/Map<kotlin/String, kotlin/String>|>(Null(null), ::R|/x|)
} }
@@ -7,8 +7,8 @@ interface A {
fun getA(): A? = null fun getA(): A? = null
val x by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, NEW_INFERENCE_ERROR!>lazy { val x by lazy {
(getA() ?: error("error")).list.associateBy { (getA() ?: error("error")).list.associateBy {
it it
} }
}<!> }
@@ -38,12 +38,12 @@ FILE: kotlinSam.kt
} }
) )
R|/foo1|(R|<local>/f|) R|/foo1|(R|<local>/f|)
<Inapplicable(INAPPLICABLE): /foo2>#(<L> = foo2@fun <anonymous>(x: R|kotlin/Nothing|): R|kotlin/Nothing| <inline=Unknown> { <Inapplicable(INAPPLICABLE): /foo2>#(<L> = foo2@fun <anonymous>(x: R|kotlin/Nothing|): R|kotlin/Boolean| <inline=Unknown> {
^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1))) ^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1)))
} }
) )
<Inapplicable(INAPPLICABLE): /foo2>#(R|<local>/f|) <Inapplicable(INAPPLICABLE): /foo2>#(R|<local>/f|)
<Inapplicable(INAPPLICABLE): /foo3>#(<L> = foo3@fun <anonymous>(x: R|kotlin/Nothing|): R|kotlin/Nothing| <inline=Unknown> { <Inapplicable(INAPPLICABLE): /foo3>#(<L> = foo3@fun <anonymous>(x: R|kotlin/Nothing|): R|kotlin/Boolean| <inline=Unknown> {
^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1))) ^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1)))
} }
) )
@@ -2,7 +2,7 @@ FILE: main.kt
public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| { public final fun foo(m: R|MyRunnable|): R|kotlin/Unit| {
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
Q|JavaUsage|.<Inapplicable(INAPPLICABLE): /JavaUsage.foo>#(<L> = foo@fun <anonymous>(x: R|kotlin/Nothing|): R|kotlin/Nothing| <inline=Unknown> { Q|JavaUsage|.<Inapplicable(INAPPLICABLE): /JavaUsage.foo>#(<L> = foo@fun <anonymous>(x: R|kotlin/Nothing|): R|kotlin/Boolean| <inline=Unknown> {
^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1))) ^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1)))
} }
) )
@@ -97,24 +97,29 @@ class ControlFlowGraphBuilder {
// ----------------------------------- Public API ----------------------------------- // ----------------------------------- Public API -----------------------------------
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection<FirStatement>? { fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): Collection<FirStatement>? {
val exitNode = function.controlFlowGraphReference?.controlFlowGraph?.exitNode val exitNode = function.controlFlowGraphReference?.controlFlowGraph?.exitNode ?: return null
?: return null
fun FirElement.extractArgument(): FirElement = when { fun FirElement.extractArgument(): FirElement = when {
this is FirReturnExpression && target.labeledElement.symbol == function.symbol -> result.extractArgument() this is FirReturnExpression && target.labeledElement.symbol == function.symbol -> result.extractArgument()
else -> this else -> this
} }
fun CFGNode<*>.extractArgument(): FirElement? = when (this) { fun CFGNode<*>.extractArgument(): FirStatement? = when (this) {
is FunctionEnterNode, is TryMainBlockEnterNode, is FinallyBlockExitNode, is CatchClauseEnterNode -> null is FunctionEnterNode, is TryMainBlockEnterNode, is FinallyBlockExitNode, is CatchClauseEnterNode -> null
is BlockExitNode -> if (function.isLambda || isDead) firstPreviousNode.extractArgument() else null is BlockExitNode -> if (function.isLambda || isDead) firstPreviousNode.extractArgument() else null
is StubNode -> firstPreviousNode.extractArgument() is StubNode -> firstPreviousNode.extractArgument()
else -> fir.extractArgument() else -> fir.extractArgument() as FirStatement?
} }
return (nonDirectJumps[exitNode] + exitNode.previousNodes).mapNotNullTo(mutableSetOf()) { val returnValues = exitNode.previousNodes.mapNotNullTo(mutableSetOf()) {
it.extractArgument() as FirStatement? val edge = exitNode.incomingEdges.getValue(it)
// * NormalPath: last expression = return value
// * UncaughtExceptionPath: last expression = whatever threw, *not* a return value
// * ReturnPath(this lambda): these go from `finally` blocks, so that's not the return value;
// look in `nonDirectJumps` instead.
it.takeIf { edge.kind.usedInCfa && edge.label == NormalPath }?.extractArgument()
} }
return nonDirectJumps[exitNode].mapNotNullTo(returnValues) { it.extractArgument() }
} }
@OptIn(CfgBuilderInternals::class) @OptIn(CfgBuilderInternals::class)