FIR: do not use CFG in FirPropertyInitializationChecker
This commit is contained in:
+32
-58
@@ -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>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user