FIR CFA: use a common fake node for annotation calls and contracts
The graphs for both will be completely discarded and should never be visited.
This commit is contained in:
+6
-6
@@ -1030,12 +1030,12 @@ abstract class FirDataFlowAnalyzer(
|
|||||||
|
|
||||||
// ----------------------------------- Annotations -----------------------------------
|
// ----------------------------------- Annotations -----------------------------------
|
||||||
|
|
||||||
fun enterAnnotation(annotation: FirAnnotation) {
|
fun enterAnnotation() {
|
||||||
graphBuilder.enterAnnotation(annotation).mergeIncomingFlow()
|
graphBuilder.enterFakeExpression().mergeIncomingFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitAnnotation(annotation: FirAnnotation) {
|
fun exitAnnotation() {
|
||||||
graphBuilder.exitAnnotation(annotation).mergeIncomingFlow()
|
graphBuilder.exitFakeExpression()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Init block -----------------------------------
|
// ----------------------------------- Init block -----------------------------------
|
||||||
@@ -1053,11 +1053,11 @@ abstract class FirDataFlowAnalyzer(
|
|||||||
// ----------------------------------- Contract description -----------------------------------
|
// ----------------------------------- Contract description -----------------------------------
|
||||||
|
|
||||||
fun enterContractDescription() {
|
fun enterContractDescription() {
|
||||||
graphBuilder.enterContractDescription().mergeIncomingFlow()
|
graphBuilder.enterFakeExpression().mergeIncomingFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitContractDescription() {
|
fun exitContractDescription() {
|
||||||
graphBuilder.exitContractDescription()
|
graphBuilder.exitFakeExpression()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Elvis -----------------------------------
|
// ----------------------------------- Elvis -----------------------------------
|
||||||
|
|||||||
+11
-25
@@ -1186,20 +1186,23 @@ class ControlFlowGraphBuilder {
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Annotations -----------------------------------
|
// ----------------------------------- Fake expressions -----------------------------------
|
||||||
|
|
||||||
fun enterAnnotation(annotation: FirAnnotation): AnnotationEnterNode {
|
fun enterFakeExpression(): FakeExpressionEnterNode {
|
||||||
pushGraph(ControlFlowGraph(null, "STUB_GRAPH_FOR_ANNOTATION_CALL", ControlFlowGraph.Kind.AnnotationCall))
|
// Things like annotations and `contract { ... }` use normal call resolution, but aren't real expressions
|
||||||
return createAnnotationEnterNode(annotation).also {
|
// and are never evaluated. We'll push all nodes created in the process into a stub graph, then throw it away.
|
||||||
|
// TODO: don't waste time creating the nodes in the first place
|
||||||
|
pushGraph(ControlFlowGraph(null, "STUB_GRAPH_FOR_FAKE_EXPRESSION", ControlFlowGraph.Kind.FakeCall))
|
||||||
|
return createFakeExpressionEnterNode().also {
|
||||||
lastNodes.push(it)
|
lastNodes.push(it)
|
||||||
|
exitTargetsForTry.push(it) // technically might create CFG loops, but the graph will never be visited anyway...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitAnnotation(annotation: FirAnnotation): AnnotationExitNode {
|
fun exitFakeExpression() {
|
||||||
val node = createAnnotationExitNode(annotation)
|
lastNodes.pop()
|
||||||
popAndAddEdge(node)
|
exitTargetsForTry.pop()
|
||||||
popGraph()
|
popGraph()
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Callable references -----------------------------------
|
// ----------------------------------- Callable references -----------------------------------
|
||||||
@@ -1319,23 +1322,6 @@ class ControlFlowGraphBuilder {
|
|||||||
return exitNode
|
return exitNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Contract description -----------------------------------
|
|
||||||
|
|
||||||
fun enterContractDescription(): CFGNode<*> {
|
|
||||||
pushGraph(ControlFlowGraph(null, "contract description", ControlFlowGraph.Kind.AnonymousFunction))
|
|
||||||
|
|
||||||
return createContractDescriptionEnterNode().also {
|
|
||||||
lastNodes.push(it)
|
|
||||||
exitTargetsForTry.push(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exitContractDescription() {
|
|
||||||
lastNodes.pop()
|
|
||||||
exitTargetsForTry.pop()
|
|
||||||
popGraph()
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
|
|||||||
+2
-8
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.fir.expressions.*
|
|||||||
|
|
||||||
fun ControlFlowGraphBuilder.createStubNode(): StubNode = StubNode(currentGraph, levelCounter, createId())
|
fun ControlFlowGraphBuilder.createStubNode(): StubNode = StubNode(currentGraph, levelCounter, createId())
|
||||||
|
|
||||||
fun ControlFlowGraphBuilder.createContractDescriptionEnterNode(): ContractDescriptionEnterNode =
|
fun ControlFlowGraphBuilder.createFakeExpressionEnterNode(): FakeExpressionEnterNode =
|
||||||
ContractDescriptionEnterNode(currentGraph, levelCounter, createId())
|
FakeExpressionEnterNode(currentGraph, levelCounter, createId())
|
||||||
|
|
||||||
fun ControlFlowGraphBuilder.createLoopExitNode(fir: FirLoop): LoopExitNode = LoopExitNode(currentGraph, fir, levelCounter, createId())
|
fun ControlFlowGraphBuilder.createLoopExitNode(fir: FirLoop): LoopExitNode = LoopExitNode(currentGraph, fir, levelCounter, createId())
|
||||||
|
|
||||||
@@ -145,12 +145,6 @@ fun ControlFlowGraphBuilder.createStringConcatenationCallNode(fir: FirStringConc
|
|||||||
fun ControlFlowGraphBuilder.createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode =
|
fun ControlFlowGraphBuilder.createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode =
|
||||||
VariableAssignmentNode(currentGraph, fir, levelCounter, createId())
|
VariableAssignmentNode(currentGraph, fir, levelCounter, createId())
|
||||||
|
|
||||||
fun ControlFlowGraphBuilder.createAnnotationExitNode(fir: FirAnnotation): AnnotationExitNode =
|
|
||||||
AnnotationExitNode(currentGraph, fir, levelCounter, createId())
|
|
||||||
|
|
||||||
fun ControlFlowGraphBuilder.createAnnotationEnterNode(fir: FirAnnotation): AnnotationEnterNode =
|
|
||||||
AnnotationEnterNode(currentGraph, fir, levelCounter, createId())
|
|
||||||
|
|
||||||
fun ControlFlowGraphBuilder.createElvisLhsIsNotNullNode(fir: FirElvisExpression): ElvisLhsIsNotNullNode =
|
fun ControlFlowGraphBuilder.createElvisLhsIsNotNullNode(fir: FirElvisExpression): ElvisLhsIsNotNullNode =
|
||||||
ElvisLhsIsNotNullNode(currentGraph, fir, levelCounter, createId())
|
ElvisLhsIsNotNullNode(currentGraph, fir, levelCounter, createId())
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1098,9 +1098,9 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
|
|||||||
annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.Types)
|
annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.Types)
|
||||||
return context.forAnnotation {
|
return context.forAnnotation {
|
||||||
withFirArrayOfCallTransformer {
|
withFirArrayOfCallTransformer {
|
||||||
dataFlowAnalyzer.enterAnnotation(annotationCall)
|
dataFlowAnalyzer.enterAnnotation()
|
||||||
val result = callResolver.resolveAnnotationCall(annotationCall)
|
val result = callResolver.resolveAnnotationCall(annotationCall)
|
||||||
dataFlowAnalyzer.exitAnnotation(result ?: annotationCall)
|
dataFlowAnalyzer.exitAnnotation()
|
||||||
if (result == null) return annotationCall
|
if (result == null) return annotationCall
|
||||||
callCompleter.completeCall(result, noExpectedType)
|
callCompleter.completeCall(result, noExpectedType)
|
||||||
(result.argumentList as FirResolvedArgumentList).let { annotationCall.replaceArgumentMapping((it).toAnnotationArgumentMapping()) }
|
(result.argumentList as FirResolvedArgumentList).let { annotationCall.replaceArgumentMapping((it).toAnnotationArgumentMapping()) }
|
||||||
|
|||||||
+2
-2
@@ -143,9 +143,9 @@ private class FirExpressionsResolveTransformerForSpecificAnnotations(
|
|||||||
) : FirExpressionsResolveTransformer(transformer) {
|
) : FirExpressionsResolveTransformer(transformer) {
|
||||||
|
|
||||||
override fun transformAnnotation(annotation: FirAnnotation, data: ResolutionMode): FirStatement {
|
override fun transformAnnotation(annotation: FirAnnotation, data: ResolutionMode): FirStatement {
|
||||||
dataFlowAnalyzer.enterAnnotation(annotation)
|
dataFlowAnalyzer.enterAnnotation()
|
||||||
annotation.transformChildren(transformer, ResolutionMode.ContextDependent)
|
annotation.transformChildren(transformer, ResolutionMode.ContextDependent)
|
||||||
dataFlowAnalyzer.exitAnnotation(annotation)
|
dataFlowAnalyzer.exitAnnotation()
|
||||||
return annotation
|
return annotation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -747,18 +747,6 @@ class StubNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(CfgInternals::class)
|
|
||||||
class ContractDescriptionEnterNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(owner, level, id) {
|
|
||||||
init {
|
|
||||||
owner.enterNode = this
|
|
||||||
}
|
|
||||||
|
|
||||||
override val fir: FirStub = FirStub
|
|
||||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
|
||||||
return visitor.visitContractDescriptionEnterNode(this, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VariableDeclarationNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id) {
|
class VariableDeclarationNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id) {
|
||||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||||
return visitor.visitVariableDeclarationNode(this, data)
|
return visitor.visitVariableDeclarationNode(this, data)
|
||||||
@@ -813,32 +801,6 @@ class WhenSubjectExpressionExitNode(owner: ControlFlowGraph, override val fir: F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Other -----------------------------------
|
|
||||||
|
|
||||||
@OptIn(CfgInternals::class)
|
|
||||||
class AnnotationEnterNode(owner: ControlFlowGraph, override val fir: FirAnnotation, level: Int, id: Int) : CFGNode<FirAnnotation>(owner, level, id),
|
|
||||||
EnterNodeMarker {
|
|
||||||
init {
|
|
||||||
owner.enterNode = this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
|
||||||
return visitor.visitAnnotationEnterNode(this, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(CfgInternals::class)
|
|
||||||
class AnnotationExitNode(owner: ControlFlowGraph, override val fir: FirAnnotation, level: Int, id: Int) : CFGNode<FirAnnotation>(owner, level, id),
|
|
||||||
ExitNodeMarker {
|
|
||||||
init {
|
|
||||||
owner.exitNode = this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
|
||||||
return visitor.visitAnnotationExitNode(this, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------- Stub -----------------------------------
|
// ----------------------------------- Stub -----------------------------------
|
||||||
|
|
||||||
object FirStub : FirExpression() {
|
object FirStub : FirExpression() {
|
||||||
@@ -853,6 +815,16 @@ object FirStub : FirExpression() {
|
|||||||
override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) }
|
override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeExpressionEnterNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(owner, level, id) {
|
||||||
|
init { isDead = true }
|
||||||
|
|
||||||
|
override val fir: FirStub = FirStub
|
||||||
|
|
||||||
|
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||||
|
throw IllegalStateException("fake expressions should not appear in graphs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------- Smart-cast node -----------------------------------
|
// ----------------------------------- Smart-cast node -----------------------------------
|
||||||
|
|
||||||
class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: FirSmartCastExpression, level: Int, id: Int) : CFGNode<FirSmartCastExpression>(owner, level, id) {
|
class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: FirSmartCastExpression, level: Int, id: Int) : CFGNode<FirSmartCastExpression>(owner, level, id) {
|
||||||
|
|||||||
+1
-3
@@ -90,8 +90,6 @@ fun CFGNode<*>.render(): String =
|
|||||||
is FieldInitializerExitNode -> "Exit field"
|
is FieldInitializerExitNode -> "Exit field"
|
||||||
is InitBlockEnterNode -> "Enter init block"
|
is InitBlockEnterNode -> "Enter init block"
|
||||||
is InitBlockExitNode -> "Exit init block"
|
is InitBlockExitNode -> "Exit init block"
|
||||||
is AnnotationEnterNode -> "Enter annotation"
|
|
||||||
is AnnotationExitNode -> "Exit annotation"
|
|
||||||
|
|
||||||
is EnterSafeCallNode -> "Enter safe call"
|
is EnterSafeCallNode -> "Enter safe call"
|
||||||
is ExitSafeCallNode -> "Exit safe call"
|
is ExitSafeCallNode -> "Exit safe call"
|
||||||
@@ -113,7 +111,7 @@ fun CFGNode<*>.render(): String =
|
|||||||
is ScriptEnterNode -> "Enter class ${fir.name}"
|
is ScriptEnterNode -> "Enter class ${fir.name}"
|
||||||
is ScriptExitNode -> "Exit class ${fir.name}"
|
is ScriptExitNode -> "Exit class ${fir.name}"
|
||||||
|
|
||||||
is ContractDescriptionEnterNode -> "Enter contract description"
|
is FakeExpressionEnterNode -> "Enter fake expression"
|
||||||
|
|
||||||
is EnterValueParameterNode -> "Enter default value of ${fir.name}"
|
is EnterValueParameterNode -> "Enter default value of ${fir.name}"
|
||||||
is EnterDefaultArgumentsNode -> "Enter default value of ${fir.name}"
|
is EnterDefaultArgumentsNode -> "Enter default value of ${fir.name}"
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
|
|||||||
PropertyInitializer(withBody = true),
|
PropertyInitializer(withBody = true),
|
||||||
FieldInitializer(withBody = true),
|
FieldInitializer(withBody = true),
|
||||||
TopLevel(withBody = false),
|
TopLevel(withBody = false),
|
||||||
AnnotationCall(withBody = true),
|
FakeCall(withBody = true),
|
||||||
DefaultArgument(withBody = true),
|
DefaultArgument(withBody = true),
|
||||||
Stub(withBody = false)
|
Stub(withBody = false)
|
||||||
}
|
}
|
||||||
|
|||||||
-12
@@ -347,10 +347,6 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
|
|||||||
return visitNode(node, data)
|
return visitNode(node, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun visitContractDescriptionEnterNode(node: ContractDescriptionEnterNode, data: D): R {
|
|
||||||
return visitNode(node, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun visitVariableDeclarationNode(node: VariableDeclarationNode, data: D): R {
|
open fun visitVariableDeclarationNode(node: VariableDeclarationNode, data: D): R {
|
||||||
return visitNode(node, data)
|
return visitNode(node, data)
|
||||||
}
|
}
|
||||||
@@ -391,14 +387,6 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
|
|||||||
|
|
||||||
// ----------------------------------- Other -----------------------------------
|
// ----------------------------------- Other -----------------------------------
|
||||||
|
|
||||||
open fun visitAnnotationEnterNode(node: AnnotationEnterNode, data: D): R {
|
|
||||||
return visitNode(node, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun visitAnnotationExitNode(node: AnnotationExitNode, data: D): R {
|
|
||||||
return visitNode(node, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun visitSmartCastExpressionExitNode(node: SmartCastExpressionExitNode, data: D): R {
|
open fun visitSmartCastExpressionExitNode(node: SmartCastExpressionExitNode, data: D): R {
|
||||||
return visitNode(node, data)
|
return visitNode(node, data)
|
||||||
}
|
}
|
||||||
|
|||||||
-20
@@ -291,16 +291,6 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
|
|||||||
visitNode(node)
|
visitNode(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Other -----------------------------------
|
|
||||||
|
|
||||||
open fun visitAnnotationEnterNode(node: AnnotationEnterNode) {
|
|
||||||
visitNode(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun visitAnnotationExitNode(node: AnnotationExitNode) {
|
|
||||||
visitNode(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
final override fun visitNode(node: CFGNode<*>, data: Nothing?) {
|
final override fun visitNode(node: CFGNode<*>, data: Nothing?) {
|
||||||
@@ -591,14 +581,4 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
|
|||||||
final override fun visitExitSafeCallNode(node: ExitSafeCallNode, data: Nothing?) {
|
final override fun visitExitSafeCallNode(node: ExitSafeCallNode, data: Nothing?) {
|
||||||
visitExitSafeCallNode(node)
|
visitExitSafeCallNode(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Other -----------------------------------
|
|
||||||
|
|
||||||
final override fun visitAnnotationEnterNode(node: AnnotationEnterNode, data: Nothing?) {
|
|
||||||
visitAnnotationEnterNode(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun visitAnnotationExitNode(node: AnnotationExitNode, data: Nothing?) {
|
|
||||||
visitAnnotationExitNode(node)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user