[FIR] Fix cfg for safe call inside elvis
This commit is contained in:
committed by
teamcityserver
parent
c160511244
commit
7e9f27436a
+3
-1
@@ -61,7 +61,9 @@ digraph elvis_kt {
|
||||
5 -> {6};
|
||||
6 -> {7};
|
||||
7 -> {8};
|
||||
8 -> {9 11};
|
||||
8 -> {9};
|
||||
8 -> {11} [color=red];
|
||||
8 -> {13} [color=green];
|
||||
9 -> {10};
|
||||
10 -> {11};
|
||||
11 -> {12};
|
||||
|
||||
+3
-1
@@ -469,7 +469,9 @@ digraph returns_kt {
|
||||
151 -> {152};
|
||||
152 -> {153};
|
||||
153 -> {154};
|
||||
154 -> {155 157};
|
||||
154 -> {155};
|
||||
154 -> {157} [color=red];
|
||||
154 -> {159} [color=green];
|
||||
155 -> {156};
|
||||
156 -> {157};
|
||||
157 -> {158};
|
||||
|
||||
@@ -583,7 +583,9 @@ digraph nullability_kt {
|
||||
194 -> {195 197};
|
||||
195 -> {196};
|
||||
196 -> {197};
|
||||
197 -> {198 200};
|
||||
197 -> {198};
|
||||
197 -> {200} [color=red];
|
||||
197 -> {202} [color=green];
|
||||
198 -> {199};
|
||||
199 -> {200};
|
||||
200 -> {201};
|
||||
|
||||
@@ -1288,6 +1288,10 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
|
||||
// ----------------------------------- Elvis -----------------------------------
|
||||
|
||||
fun enterElvis(elvisExpression: FirElvisExpression) {
|
||||
graphBuilder.enterElvis(elvisExpression)
|
||||
}
|
||||
|
||||
fun exitElvisLhs(elvisExpression: FirElvisExpression) {
|
||||
val (lhsExitNode, lhsIsNotNullNode, rhsEnterNode) = graphBuilder.exitElvisLhs(elvisExpression)
|
||||
lhsExitNode.mergeIncomingFlow()
|
||||
|
||||
@@ -119,7 +119,9 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
|
||||
protected set
|
||||
|
||||
internal fun updateDeadStatus() {
|
||||
isDead = incomingEdges.size == previousNodes.size && incomingEdges.values.all { it.kind == EdgeKind.DeadForward }
|
||||
isDead = incomingEdges.size == previousNodes.size && incomingEdges.values.all {
|
||||
it.kind == EdgeKind.DeadForward || !it.kind.usedInCfa
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R
|
||||
|
||||
+15
-2
@@ -95,6 +95,7 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
private val exitSafeCallNodes: Stack<ExitSafeCallNode> = stackOf()
|
||||
private val exitElvisExpressionNodes: Stack<ElvisExitNode> = stackOf()
|
||||
private val elvisRhsEnterNodes: Stack<ElvisRhsEnterNode> = stackOf()
|
||||
|
||||
/*
|
||||
* ignoredFunctionCalls is needed for resolve of += operator:
|
||||
@@ -1165,7 +1166,15 @@ class ControlFlowGraphBuilder {
|
||||
val exitNode = createExitSafeCallNode(safeCall)
|
||||
exitSafeCallNodes.push(exitNode)
|
||||
addEdge(lastNode, enterNode)
|
||||
addEdge(lastNode, exitNode)
|
||||
if (elvisRhsEnterNodes.topOrNull()?.fir?.lhs === safeCall) {
|
||||
//if this is safe call in lhs of elvis, we make two edges
|
||||
// 1. Df-only edge to exit node, to get not null implications there
|
||||
// 2. Cf-only edge to elvis rhs
|
||||
addEdge(lastNode, exitNode, preferredKind = EdgeKind.DfgForward)
|
||||
addEdge(lastNode, elvisRhsEnterNodes.top(), preferredKind = EdgeKind.CfgForward)
|
||||
} else {
|
||||
addEdge(lastNode, exitNode)
|
||||
}
|
||||
return enterNode
|
||||
}
|
||||
|
||||
@@ -1184,6 +1193,10 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
// ----------------------------------- Elvis -----------------------------------
|
||||
|
||||
fun enterElvis(elvisExpression: FirElvisExpression) {
|
||||
elvisRhsEnterNodes.push(createElvisRhsEnterNode(elvisExpression))
|
||||
}
|
||||
|
||||
fun exitElvisLhs(elvisExpression: FirElvisExpression): Triple<ElvisLhsExitNode, ElvisLhsIsNotNullNode, ElvisRhsEnterNode> {
|
||||
val exitNode = createElvisExitNode(elvisExpression).also {
|
||||
exitElvisExpressionNodes.push(it)
|
||||
@@ -1198,7 +1211,7 @@ class ControlFlowGraphBuilder {
|
||||
addEdge(it, exitNode)
|
||||
}
|
||||
|
||||
val rhsEnterNode = createElvisRhsEnterNode(elvisExpression).also {
|
||||
val rhsEnterNode = elvisRhsEnterNodes.pop().also {
|
||||
addEdge(lhsExitNode, it)
|
||||
}
|
||||
lastNodes.push(rhsEnterNode)
|
||||
|
||||
+1
@@ -224,6 +224,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTran
|
||||
withExpectedType(expectedType, mayBeCoercionToUnitApplied = true)
|
||||
else
|
||||
withExpectedType(expectedType?.withNullability(ConeNullability.NULLABLE, session.typeContext))
|
||||
dataFlowAnalyzer.enterElvis(elvisExpression)
|
||||
elvisExpression.transformLhs(transformer, resolutionModeForLhs)
|
||||
dataFlowAnalyzer.exitElvisLhs(elvisExpression)
|
||||
|
||||
|
||||
+20
-1
@@ -10,6 +10,13 @@ inline fun <T> Any?.myRun(block: () -> T): T {
|
||||
return block()
|
||||
}
|
||||
|
||||
inline fun <T> directRun(block: () -> T): T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return block()
|
||||
}
|
||||
|
||||
fun bad(): String {
|
||||
val x: String? = null
|
||||
|
||||
@@ -20,4 +27,16 @@ fun ok(): String {
|
||||
val x: String? = null
|
||||
|
||||
x?.run { return "non-null" } ?: return "null"
|
||||
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
|
||||
}
|
||||
|
||||
fun ok2(): String {
|
||||
directRun {
|
||||
return "nonNull"
|
||||
}
|
||||
}
|
||||
|
||||
fun ok3(arg: Any?): String {
|
||||
arg?.myRun {
|
||||
return "nonNull"
|
||||
} ?: error("null")
|
||||
}
|
||||
|
||||
+19
@@ -10,6 +10,13 @@ inline fun <T> Any?.myRun(block: () -> T): T {
|
||||
return block()
|
||||
}
|
||||
|
||||
inline fun <T> directRun(block: () -> T): T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return block()
|
||||
}
|
||||
|
||||
fun bad(): String {
|
||||
val x: String? = null
|
||||
|
||||
@@ -20,4 +27,16 @@ fun ok(): String {
|
||||
val x: String? = null
|
||||
|
||||
x?.run { return "non-null" } ?: return "null"
|
||||
}
|
||||
|
||||
fun ok2(): String {
|
||||
directRun {
|
||||
return "nonNull"
|
||||
}
|
||||
}
|
||||
|
||||
fun ok3(arg: Any?): String {
|
||||
arg?.myRun {
|
||||
return "nonNull"
|
||||
} ?: error("null")
|
||||
}
|
||||
+5
@@ -1,7 +1,12 @@
|
||||
package
|
||||
|
||||
public fun bad(): kotlin.String
|
||||
public inline fun </*0*/ T> directRun(/*0*/ block: () -> T): T
|
||||
CallsInPlace(block, EXACTLY_ONCE)
|
||||
|
||||
public fun ok(): kotlin.String
|
||||
public fun ok2(): kotlin.String
|
||||
public fun ok3(/*0*/ arg: kotlin.Any?): kotlin.String
|
||||
public inline fun </*0*/ T> kotlin.Any?.myRun(/*0*/ block: () -> T): T
|
||||
CallsInPlace(block, EXACTLY_ONCE)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user