diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyInitializationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyInitializationChecker.kt index e9131e8b3b9..4f695c85bc7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyInitializationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyInitializationChecker.kt @@ -7,76 +7,50 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.analysis.cfa.util.TraverseDirection -import org.jetbrains.kotlin.fir.analysis.cfa.util.collectDataForNode +import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer -import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment 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.visitors.FirVisitorVoid object FirPropertyInitializationChecker : FirRegularClassChecker() { override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { - val properties = mutableSetOf() - val toReport = mutableSetOf() - for (decl in declaration.declarations.asReversed()) { - when (decl) { - is FirProperty -> properties.add(decl.symbol) - is FirAnonymousInitializer -> - if (properties.isNotEmpty()) { - collectInitAssignments(decl, properties, toReport) - } - else -> { - } + val interestingProperties = mutableSetOf() + val visitor = object : FirVisitorVoid() { + override fun visitElement(element: FirElement) = element.acceptChildren(this) + + override fun visitConstructor(constructor: FirConstructor) {} + + override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) {} + + override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor) {} + + 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( - init: FirAnonymousInitializer, - properties: Properties, - assignments: MutableSet - ) { - val graph = init.controlFlowGraphReference?.controlFlowGraph ?: return - 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>>() { - - override fun visitNode(node: CFGNode<*>, data: Collection>): Properties { - if (data.isEmpty()) return emptySet() - return data.map { it.second }.reduce { l, r -> l.intersect(r) } - } - - override fun visitUnionNode( - node: T, data: Collection> - ): 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>): 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 + for (member in declaration.declarations.asReversed()) { + if (interestingProperties.isNotEmpty()) { + member.accept(visitor) + } + if (member is FirProperty) { + interestingProperties.add(member.symbol) } } } } - -private typealias Properties = Set