[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};
|
5 -> {6};
|
||||||
6 -> {7};
|
6 -> {7};
|
||||||
7 -> {8};
|
7 -> {8};
|
||||||
8 -> {9 11};
|
8 -> {9};
|
||||||
|
8 -> {11} [color=red];
|
||||||
|
8 -> {13} [color=green];
|
||||||
9 -> {10};
|
9 -> {10};
|
||||||
10 -> {11};
|
10 -> {11};
|
||||||
11 -> {12};
|
11 -> {12};
|
||||||
|
|||||||
+3
-1
@@ -469,7 +469,9 @@ digraph returns_kt {
|
|||||||
151 -> {152};
|
151 -> {152};
|
||||||
152 -> {153};
|
152 -> {153};
|
||||||
153 -> {154};
|
153 -> {154};
|
||||||
154 -> {155 157};
|
154 -> {155};
|
||||||
|
154 -> {157} [color=red];
|
||||||
|
154 -> {159} [color=green];
|
||||||
155 -> {156};
|
155 -> {156};
|
||||||
156 -> {157};
|
156 -> {157};
|
||||||
157 -> {158};
|
157 -> {158};
|
||||||
|
|||||||
@@ -583,7 +583,9 @@ digraph nullability_kt {
|
|||||||
194 -> {195 197};
|
194 -> {195 197};
|
||||||
195 -> {196};
|
195 -> {196};
|
||||||
196 -> {197};
|
196 -> {197};
|
||||||
197 -> {198 200};
|
197 -> {198};
|
||||||
|
197 -> {200} [color=red];
|
||||||
|
197 -> {202} [color=green];
|
||||||
198 -> {199};
|
198 -> {199};
|
||||||
199 -> {200};
|
199 -> {200};
|
||||||
200 -> {201};
|
200 -> {201};
|
||||||
|
|||||||
@@ -1288,6 +1288,10 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
|
|
||||||
// ----------------------------------- Elvis -----------------------------------
|
// ----------------------------------- Elvis -----------------------------------
|
||||||
|
|
||||||
|
fun enterElvis(elvisExpression: FirElvisExpression) {
|
||||||
|
graphBuilder.enterElvis(elvisExpression)
|
||||||
|
}
|
||||||
|
|
||||||
fun exitElvisLhs(elvisExpression: FirElvisExpression) {
|
fun exitElvisLhs(elvisExpression: FirElvisExpression) {
|
||||||
val (lhsExitNode, lhsIsNotNullNode, rhsEnterNode) = graphBuilder.exitElvisLhs(elvisExpression)
|
val (lhsExitNode, lhsIsNotNullNode, rhsEnterNode) = graphBuilder.exitElvisLhs(elvisExpression)
|
||||||
lhsExitNode.mergeIncomingFlow()
|
lhsExitNode.mergeIncomingFlow()
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
|
|||||||
protected set
|
protected set
|
||||||
|
|
||||||
internal fun updateDeadStatus() {
|
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
|
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 exitSafeCallNodes: Stack<ExitSafeCallNode> = stackOf()
|
||||||
private val exitElvisExpressionNodes: Stack<ElvisExitNode> = stackOf()
|
private val exitElvisExpressionNodes: Stack<ElvisExitNode> = stackOf()
|
||||||
|
private val elvisRhsEnterNodes: Stack<ElvisRhsEnterNode> = stackOf()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ignoredFunctionCalls is needed for resolve of += operator:
|
* ignoredFunctionCalls is needed for resolve of += operator:
|
||||||
@@ -1165,7 +1166,15 @@ class ControlFlowGraphBuilder {
|
|||||||
val exitNode = createExitSafeCallNode(safeCall)
|
val exitNode = createExitSafeCallNode(safeCall)
|
||||||
exitSafeCallNodes.push(exitNode)
|
exitSafeCallNodes.push(exitNode)
|
||||||
addEdge(lastNode, enterNode)
|
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
|
return enterNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1184,6 +1193,10 @@ class ControlFlowGraphBuilder {
|
|||||||
|
|
||||||
// ----------------------------------- Elvis -----------------------------------
|
// ----------------------------------- Elvis -----------------------------------
|
||||||
|
|
||||||
|
fun enterElvis(elvisExpression: FirElvisExpression) {
|
||||||
|
elvisRhsEnterNodes.push(createElvisRhsEnterNode(elvisExpression))
|
||||||
|
}
|
||||||
|
|
||||||
fun exitElvisLhs(elvisExpression: FirElvisExpression): Triple<ElvisLhsExitNode, ElvisLhsIsNotNullNode, ElvisRhsEnterNode> {
|
fun exitElvisLhs(elvisExpression: FirElvisExpression): Triple<ElvisLhsExitNode, ElvisLhsIsNotNullNode, ElvisRhsEnterNode> {
|
||||||
val exitNode = createElvisExitNode(elvisExpression).also {
|
val exitNode = createElvisExitNode(elvisExpression).also {
|
||||||
exitElvisExpressionNodes.push(it)
|
exitElvisExpressionNodes.push(it)
|
||||||
@@ -1198,7 +1211,7 @@ class ControlFlowGraphBuilder {
|
|||||||
addEdge(it, exitNode)
|
addEdge(it, exitNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
val rhsEnterNode = createElvisRhsEnterNode(elvisExpression).also {
|
val rhsEnterNode = elvisRhsEnterNodes.pop().also {
|
||||||
addEdge(lhsExitNode, it)
|
addEdge(lhsExitNode, it)
|
||||||
}
|
}
|
||||||
lastNodes.push(rhsEnterNode)
|
lastNodes.push(rhsEnterNode)
|
||||||
|
|||||||
+1
@@ -224,6 +224,7 @@ class FirControlFlowStatementsResolveTransformer(transformer: FirBodyResolveTran
|
|||||||
withExpectedType(expectedType, mayBeCoercionToUnitApplied = true)
|
withExpectedType(expectedType, mayBeCoercionToUnitApplied = true)
|
||||||
else
|
else
|
||||||
withExpectedType(expectedType?.withNullability(ConeNullability.NULLABLE, session.typeContext))
|
withExpectedType(expectedType?.withNullability(ConeNullability.NULLABLE, session.typeContext))
|
||||||
|
dataFlowAnalyzer.enterElvis(elvisExpression)
|
||||||
elvisExpression.transformLhs(transformer, resolutionModeForLhs)
|
elvisExpression.transformLhs(transformer, resolutionModeForLhs)
|
||||||
dataFlowAnalyzer.exitElvisLhs(elvisExpression)
|
dataFlowAnalyzer.exitElvisLhs(elvisExpression)
|
||||||
|
|
||||||
|
|||||||
+20
-1
@@ -10,6 +10,13 @@ inline fun <T> Any?.myRun(block: () -> T): T {
|
|||||||
return block()
|
return block()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <T> directRun(block: () -> T): T {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
return block()
|
||||||
|
}
|
||||||
|
|
||||||
fun bad(): String {
|
fun bad(): String {
|
||||||
val x: String? = null
|
val x: String? = null
|
||||||
|
|
||||||
@@ -20,4 +27,16 @@ fun ok(): String {
|
|||||||
val x: String? = null
|
val x: String? = null
|
||||||
|
|
||||||
x?.run { return "non-null" } ?: return "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()
|
return block()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <T> directRun(block: () -> T): T {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
return block()
|
||||||
|
}
|
||||||
|
|
||||||
fun bad(): String {
|
fun bad(): String {
|
||||||
val x: String? = null
|
val x: String? = null
|
||||||
|
|
||||||
@@ -20,4 +27,16 @@ fun ok(): String {
|
|||||||
val x: String? = null
|
val x: String? = null
|
||||||
|
|
||||||
x?.run { return "non-null" } ?: return "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
|
package
|
||||||
|
|
||||||
public fun bad(): kotlin.String
|
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 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
|
public inline fun </*0*/ T> kotlin.Any?.myRun(/*0*/ block: () -> T): T
|
||||||
CallsInPlace(block, EXACTLY_ONCE)
|
CallsInPlace(block, EXACTLY_ONCE)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user