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:
pyos
2022-12-10 20:28:44 +01:00
committed by Dmitriy Novozhilov
parent 4bb7b1ac9f
commit e710edbd77
10 changed files with 35 additions and 117 deletions
@@ -1030,12 +1030,12 @@ abstract class FirDataFlowAnalyzer(
// ----------------------------------- Annotations -----------------------------------
fun enterAnnotation(annotation: FirAnnotation) {
graphBuilder.enterAnnotation(annotation).mergeIncomingFlow()
fun enterAnnotation() {
graphBuilder.enterFakeExpression().mergeIncomingFlow()
}
fun exitAnnotation(annotation: FirAnnotation) {
graphBuilder.exitAnnotation(annotation).mergeIncomingFlow()
fun exitAnnotation() {
graphBuilder.exitFakeExpression()
}
// ----------------------------------- Init block -----------------------------------
@@ -1053,11 +1053,11 @@ abstract class FirDataFlowAnalyzer(
// ----------------------------------- Contract description -----------------------------------
fun enterContractDescription() {
graphBuilder.enterContractDescription().mergeIncomingFlow()
graphBuilder.enterFakeExpression().mergeIncomingFlow()
}
fun exitContractDescription() {
graphBuilder.exitContractDescription()
graphBuilder.exitFakeExpression()
}
// ----------------------------------- Elvis -----------------------------------
@@ -1186,20 +1186,23 @@ class ControlFlowGraphBuilder {
return node
}
// ----------------------------------- Annotations -----------------------------------
// ----------------------------------- Fake expressions -----------------------------------
fun enterAnnotation(annotation: FirAnnotation): AnnotationEnterNode {
pushGraph(ControlFlowGraph(null, "STUB_GRAPH_FOR_ANNOTATION_CALL", ControlFlowGraph.Kind.AnnotationCall))
return createAnnotationEnterNode(annotation).also {
fun enterFakeExpression(): FakeExpressionEnterNode {
// Things like annotations and `contract { ... }` use normal call resolution, but aren't real expressions
// 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)
exitTargetsForTry.push(it) // technically might create CFG loops, but the graph will never be visited anyway...
}
}
fun exitAnnotation(annotation: FirAnnotation): AnnotationExitNode {
val node = createAnnotationExitNode(annotation)
popAndAddEdge(node)
fun exitFakeExpression() {
lastNodes.pop()
exitTargetsForTry.pop()
popGraph()
return node
}
// ----------------------------------- Callable references -----------------------------------
@@ -1319,23 +1322,6 @@ class ControlFlowGraphBuilder {
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() {
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.fir.expressions.*
fun ControlFlowGraphBuilder.createStubNode(): StubNode = StubNode(currentGraph, levelCounter, createId())
fun ControlFlowGraphBuilder.createContractDescriptionEnterNode(): ContractDescriptionEnterNode =
ContractDescriptionEnterNode(currentGraph, levelCounter, createId())
fun ControlFlowGraphBuilder.createFakeExpressionEnterNode(): FakeExpressionEnterNode =
FakeExpressionEnterNode(currentGraph, 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 =
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 =
ElvisLhsIsNotNullNode(currentGraph, fir, levelCounter, createId())
@@ -1098,9 +1098,9 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.Types)
return context.forAnnotation {
withFirArrayOfCallTransformer {
dataFlowAnalyzer.enterAnnotation(annotationCall)
dataFlowAnalyzer.enterAnnotation()
val result = callResolver.resolveAnnotationCall(annotationCall)
dataFlowAnalyzer.exitAnnotation(result ?: annotationCall)
dataFlowAnalyzer.exitAnnotation()
if (result == null) return annotationCall
callCompleter.completeCall(result, noExpectedType)
(result.argumentList as FirResolvedArgumentList).let { annotationCall.replaceArgumentMapping((it).toAnnotationArgumentMapping()) }
@@ -143,9 +143,9 @@ private class FirExpressionsResolveTransformerForSpecificAnnotations(
) : FirExpressionsResolveTransformer(transformer) {
override fun transformAnnotation(annotation: FirAnnotation, data: ResolutionMode): FirStatement {
dataFlowAnalyzer.enterAnnotation(annotation)
dataFlowAnalyzer.enterAnnotation()
annotation.transformChildren(transformer, ResolutionMode.ContextDependent)
dataFlowAnalyzer.exitAnnotation(annotation)
dataFlowAnalyzer.exitAnnotation()
return annotation
}