FIR: do not use CFG in FirPropertyInitializationChecker

This commit is contained in:
pyos
2022-12-13 16:24:56 +01:00
committed by Dmitriy Novozhilov
parent e86b87fe0b
commit 63ab9119f8
@@ -7,76 +7,50 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.cfa.util.TraverseDirection import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.cfa.util.collectDataForNode
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
object FirPropertyInitializationChecker : FirRegularClassChecker() { object FirPropertyInitializationChecker : FirRegularClassChecker() {
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
val properties = mutableSetOf<FirPropertySymbol>() val interestingProperties = mutableSetOf<FirPropertySymbol>()
val toReport = mutableSetOf<FirVariableAssignment>() val visitor = object : FirVisitorVoid() {
for (decl in declaration.declarations.asReversed()) { override fun visitElement(element: FirElement) = element.acceptChildren(this)
when (decl) {
is FirProperty -> properties.add(decl.symbol) override fun visitConstructor(constructor: FirConstructor) {}
is FirAnonymousInitializer ->
if (properties.isNotEmpty()) { override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) {}
collectInitAssignments(decl, properties, toReport)
} override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor) {}
else -> {
} override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction) {
// TODO? if (anonymousFunction.invocationKind.isInPlace) visitElement(anonymousFunction)
}
override fun visitRegularClass(regularClass: FirRegularClass) {}
override fun visitEnumEntry(enumEntry: FirEnumEntry) {}
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment) {
variableAssignment.acceptChildren(this)
val propertySymbol = variableAssignment.lValue.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
if (propertySymbol !in interestingProperties) return
reporter.reportOn(variableAssignment.lValue.source, FirErrors.INITIALIZATION_BEFORE_DECLARATION, propertySymbol, context)
} }
} }
toReport.forEach { exp ->
val propertySymbol = exp.lValue.toResolvedCallableSymbol() ?: return@forEach
reporter.reportOn(exp.lValue.source, FirErrors.INITIALIZATION_BEFORE_DECLARATION, propertySymbol, context)
}
}
private fun collectInitAssignments( for (member in declaration.declarations.asReversed()) {
init: FirAnonymousInitializer, if (interestingProperties.isNotEmpty()) {
properties: Properties, member.accept(visitor)
assignments: MutableSet<FirVariableAssignment> }
) { if (member is FirProperty) {
val graph = init.controlFlowGraphReference?.controlFlowGraph ?: return interestingProperties.add(member.symbol)
val visitor = AssignmentVisitor(properties) { assignments.add(it) }
graph.collectDataForNode(TraverseDirection.Forward, emptySet(), visitor, visitSubGraphs = false)
}
private class AssignmentVisitor(private val interestingProperties: Properties, private val acceptor: (FirVariableAssignment) -> Unit) :
ControlFlowGraphVisitor<Properties, Collection<Pair<EdgeLabel, Properties>>>() {
override fun visitNode(node: CFGNode<*>, data: Collection<Pair<EdgeLabel, Properties>>): Properties {
if (data.isEmpty()) return emptySet()
return data.map { it.second }.reduce { l, r -> l.intersect(r) }
}
override fun <T> visitUnionNode(
node: T, data: Collection<Pair<EdgeLabel, Properties>>
): Properties where T : CFGNode<*>, T : UnionNodeMarker {
if (data.isEmpty()) return emptySet()
return data.map { it.second }.reduce { l, r -> l + r }
}
override fun visitVariableAssignmentNode(node: VariableAssignmentNode, data: Collection<Pair<EdgeLabel, Properties>>): Properties {
val input = visitNode(node, data)
val propertySymbol = node.fir.lValue.toResolvedCallableSymbol() as? FirPropertySymbol ?: return input
return if (propertySymbol in interestingProperties && propertySymbol !in input) {
acceptor(node.fir)
input + propertySymbol
} else {
input
} }
} }
} }
} }
private typealias Properties = Set<FirPropertySymbol>