diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt index 29fcc187ef3..da3b2dd6d84 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt @@ -18,7 +18,6 @@ abstract class AbstractFirPropertyInitializationChecker { reporter: DiagnosticReporter, data: PropertyInitializationInfoData, properties: Set, - capturedWrites: Set, context: CheckerContext ) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt index 6438c4a103f..a6a2c3e4713 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt @@ -10,7 +10,6 @@ import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.checkPropertyAccesses import org.jetbrains.kotlin.fir.analysis.checkers.declaration.requiresInitialization -import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol @@ -20,7 +19,6 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec reporter: DiagnosticReporter, data: PropertyInitializationInfoData, properties: Set, - capturedWrites: Set, context: CheckerContext ) = graph.checkPropertyAccesses(properties.filterTo(mutableSetOf()) { it.requiresInitialization }, null, context, reporter, data) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/LocalPropertyAndCapturedWriteCollector.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/LocalPropertyAndCapturedWriteCollector.kt deleted file mode 100644 index 4890915008a..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/LocalPropertyAndCapturedWriteCollector.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.fir.analysis.cfa.util - -import org.jetbrains.kotlin.contracts.description.isInPlace -import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction -import org.jetbrains.kotlin.fir.declarations.FirFunction -import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment -import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* -import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol - -// TODO: this should be a FIR tree visitor, not a control flow graph visitor. -class LocalPropertyAndCapturedWriteCollector private constructor() : ControlFlowGraphVisitorVoid() { - companion object { - fun collect(graph: ControlFlowGraph): Pair, Set> { - val collector = LocalPropertyAndCapturedWriteCollector() - graph.traverse(collector) - return collector.symbols.keys to collector.capturedWrites - } - } - - // Mapping from a property symbol to its declaration context - // `true` if the (local) property is declared in the currently visited function. - // `false` if it is declared in a lambda or a local function (inside the currently visited function). - private val symbols: MutableMap = mutableMapOf() - - private val lambdaOrLocalFunctionStack: MutableList = mutableListOf() - private val capturedWrites: MutableSet = mutableSetOf() - - override fun visitNode(node: CFGNode<*>) {} - - override fun visitVariableDeclarationNode(node: VariableDeclarationNode) { - symbols[node.fir.symbol] = lambdaOrLocalFunctionStack.lastOrNull() == null - } - - override fun visitSplitPostponedLambdasNode(node: SplitPostponedLambdasNode) { - lambdaOrLocalFunctionStack.addAll(node.lambdas) - } - - override fun visitAnonymousFunctionExpressionNode(node: AnonymousFunctionExpressionNode) { - lambdaOrLocalFunctionStack.add(node.fir.anonymousFunction) - } - - override fun visitLocalFunctionDeclarationNode(node: LocalFunctionDeclarationNode) { - lambdaOrLocalFunctionStack.add(node.fir) - } - - override fun visitFunctionExitNode(node: FunctionExitNode) { - lambdaOrLocalFunctionStack.remove(node.fir) - } - - override fun visitVariableAssignmentNode(node: VariableAssignmentNode) { - val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return - - // Check if this variable assignment is inside a lambda or a local function. - if (lambdaOrLocalFunctionStack.isEmpty()) return - - // Check if the assigned variable doesn't belong to any lambda or local function. - if (symbol !in symbols || symbols[symbol] == false) return - - // If all nested declarations are lambdas that are invoked in-place (according to the contract), - // this variable assignment is not a captured write. - if (lambdaOrLocalFunctionStack.all { it is FirAnonymousFunction && it.invocationKind.isInPlace }) return - - capturedWrites.add(node.fir) - } -} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt index d6250d70fba..0f6a2636f79 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeValChecker.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.getChildren import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData -import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol @@ -27,7 +26,6 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() { reporter: DiagnosticReporter, data: PropertyInitializationInfoData, properties: Set, - capturedWrites: Set, context: CheckerContext ) { val unprocessedProperties = mutableSetOf() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt index 75e7c7c5e04..b72623cc60c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt @@ -36,7 +36,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() { reporter: DiagnosticReporter, data: PropertyInitializationInfoData, properties: Set, - capturedWrites: Set, context: CheckerContext ) { val ownData = ValueWritesWithoutReading(context.session, properties).getData(graph) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt index e79c832d788..0de95b10ca5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt @@ -10,10 +10,13 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers import org.jetbrains.kotlin.fir.analysis.checkersComponent import org.jetbrains.kotlin.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.fir.analysis.cfa.util.LocalPropertyAndCapturedWriteCollector import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraphVisitorVoid +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.VariableDeclarationNode import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol class ControlFlowAnalysisDiagnosticComponent( session: FirSession, @@ -28,10 +31,10 @@ class ControlFlowAnalysisDiagnosticComponent( if (graph.isSubGraph) return cfaCheckers.forEach { it.analyze(graph, reporter, context) } - val (properties, capturedWrites) = LocalPropertyAndCapturedWriteCollector.collect(graph) + val properties = mutableSetOf().apply { graph.traverse(LocalPropertyCollector(this)) } if (properties.isNotEmpty()) { val data = PropertyInitializationInfoData(properties, receiver = null, graph) - variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties, capturedWrites, context) } + variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties, context) } } } @@ -68,4 +71,12 @@ class ControlFlowAnalysisDiagnosticComponent( override fun visitConstructor(constructor: FirConstructor, data: CheckerContext) { analyze(constructor, data) } + + private class LocalPropertyCollector(private val result: MutableSet) : ControlFlowGraphVisitorVoid() { + override fun visitNode(node: CFGNode<*>) {} + + override fun visitVariableDeclarationNode(node: VariableDeclarationNode) { + result.add(node.fir.symbol) + } + } }