diff --git a/compiler/fir/analysis-tests/testData/resolve/cfa/reassignOfNonMemberProperty_lateInitialization.kt b/compiler/fir/analysis-tests/testData/resolve/cfa/reassignOfNonMemberProperty_lateInitialization.kt index 7adc3a037fd..0fd4fdf1bd0 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfa/reassignOfNonMemberProperty_lateInitialization.kt +++ b/compiler/fir/analysis-tests/testData/resolve/cfa/reassignOfNonMemberProperty_lateInitialization.kt @@ -10,7 +10,7 @@ class Some { init { x = "ok" - x = "error" + x = "error" z = "error" } @@ -24,17 +24,16 @@ class Some { val b: String = 123.run { // now this@run is an Int, so these are on this@Some - x = "error" + x = "error" y = "ok" - y = "error" + y = "error" z = "error" "there" } init { - x = "error" - y = "error" + x = "error" + y = "error" z = "error" } } - diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/initialization/fromLocalMembers.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/initialization/fromLocalMembers.kt index 1bcf1c82902..50e38af319c 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/initialization/fromLocalMembers.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/initialization/fromLocalMembers.kt @@ -19,7 +19,7 @@ fun test2() { } fun localFunc() { - y = 0 + y = 0 } } @@ -33,11 +33,11 @@ fun test3() { class A { init { - x = 0 + x = 0 } fun localFunc() { - y = 0 + y = 0 } } 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 8110eeab160..6438c4a103f 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 @@ -5,25 +5,15 @@ package org.jetbrains.kotlin.fir.analysis.cfa -import org.jetbrains.kotlin.contracts.description.canBeRevisited -import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited import org.jetbrains.kotlin.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.fir.declarations.utils.isLateInit -import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess +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.isCatchParameter -import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* -import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol -@OptIn(SymbolInternals::class) object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() { override fun analyze( graph: ControlFlowGraph, @@ -32,55 +22,5 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec properties: Set, capturedWrites: Set, context: CheckerContext - ) { - val localProperties = properties.filterNotTo(mutableSetOf()) { it.isInitialized() } - val reporterVisitor = PropertyReporter(data, localProperties, capturedWrites, reporter, context) - graph.traverse(reporterVisitor) - } - - private fun FirVariableSymbol<*>.isInitialized(): Boolean { - return fir.initializer != null - || fir.delegate != null - || this is FirPropertySymbol && fir.isCatchParameter == true - } - - private class PropertyReporter( - val data: PropertyInitializationInfoData, - val localProperties: Set, - val capturedWrites: Set, - val reporter: DiagnosticReporter, - val context: CheckerContext - ) : ControlFlowGraphVisitorVoid() { - override fun visitNode(node: CFGNode<*>) { - // TODO: `node.isUnion` - f({ x = 1 }, { x = 2 }) - which to report? - // Also this is currently indistinguishable from x = 1; f({}, {}). - } - - private val CFGNode<*>.propertySymbol: FirPropertySymbol? - get() = (fir as? FirQualifiedAccess)?.calleeReference?.toResolvedPropertySymbol() - - override fun visitVariableAssignmentNode(node: VariableAssignmentNode) { - val symbol = node.propertySymbol ?: return - if (!symbol.fir.isVal) return - - if (node.fir in capturedWrites) { - if (symbol.fir.isLocal) { - reporter.reportOn(node.fir.lValue.source, FirErrors.CAPTURED_VAL_INITIALIZATION, symbol, context) - } else { - reporter.reportOn(node.fir.lValue.source, FirErrors.CAPTURED_MEMBER_VAL_INITIALIZATION, symbol, context) - } - } else if (data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) { - reporter.reportOn(node.fir.lValue.source, FirErrors.VAL_REASSIGNMENT, symbol, context) - } - } - - override fun visitQualifiedAccessNode(node: QualifiedAccessNode) { - val symbol = node.propertySymbol ?: return - if (symbol in localProperties && !symbol.fir.isLateInit && symbol !is FirSyntheticPropertySymbol && - !data.getValue(node).values.all { it[symbol]?.isDefinitelyVisited() == true } - ) { - reporter.reportOn(node.fir.source, FirErrors.UNINITIALIZED_VARIABLE, symbol, context) - } - } - } + ) = 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/PropertyInitializationInfoCollector.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/PropertyInitializationInfoCollector.kt index 5e5e365b4af..2a3c98728c9 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/PropertyInitializationInfoCollector.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/util/PropertyInitializationInfoCollector.kt @@ -13,9 +13,9 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -class PropertyInitializationInfoData(properties: Set, graph: ControlFlowGraph) { +class PropertyInitializationInfoData(properties: Set, receiver: FirBasedSymbol<*>?, graph: ControlFlowGraph) { private val data by lazy(LazyThreadSafetyMode.NONE) { - PropertyInitializationInfoCollector(properties).getData(graph) + graph.collectDataForNode(TraverseDirection.Forward, PropertyInitializationInfoCollector(properties, receiver)) } fun getValue(node: CFGNode<*>): PathAwarePropertyInitializationInfo { @@ -28,9 +28,6 @@ class PropertyInitializationInfoCollector( private val expectedReceiver: FirBasedSymbol<*>? = null, private val declaredVariableCollector: DeclaredVariableCollector = DeclaredVariableCollector(), ) : PathAwareControlFlowGraphVisitor() { - fun getData(graph: ControlFlowGraph) = - graph.collectDataForNode(TraverseDirection.Forward, this) - companion object { private val EMPTY_INFO: PathAwarePropertyInitializationInfo = persistentMapOf(NormalPath to PropertyInitializationInfo.EMPTY) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt index b1b58383ece..bb6f91017da 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMemberPropertiesChecker.kt @@ -6,28 +6,38 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.contracts.description.canBeRevisited import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited +import org.jetbrains.kotlin.contracts.description.isInPlace +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfo -import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoCollector import org.jetbrains.kotlin.fir.analysis.checkers.contains import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.utils.* -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.BlockExitNode -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.NormalPath +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess +import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression +import org.jetbrains.kotlin.fir.isCatchParameter +import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol import org.jetbrains.kotlin.lexer.KtTokens // See old FE's [DeclarationsChecker] object FirMemberPropertiesChecker : FirClassChecker() { override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { - val info = declaration.collectInitializationInfo() + val info = declaration.collectInitializationInfo(context, reporter) var reachedDeadEnd = (declaration as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph?.enterNode?.isDead == true for (innerDeclaration in declaration.declarations) { @@ -42,15 +52,17 @@ object FirMemberPropertiesChecker : FirClassChecker() { } } - private fun FirClass.collectInitializationInfo(): PropertyInitializationInfo? { + private fun FirClass.collectInitializationInfo(context: CheckerContext, reporter: DiagnosticReporter): PropertyInitializationInfo? { val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null val memberPropertySymbols = declarations.mapNotNullTo(mutableSetOf()) { - (it as? FirProperty)?.takeIf { fir -> fir.initializer == null }?.symbol + (it.symbol as? FirPropertySymbol)?.takeIf { symbol -> symbol.requiresInitialization } } if (memberPropertySymbols.isEmpty()) return null // TODO: this also visits non-constructor member functions... - // TODO: also use FirPropertyInitializationAnalyzer.PropertyReporter to report reassignments or use-before-initialization - return PropertyInitializationInfoCollector(memberPropertySymbols, symbol).getData(graph)[graph.exitNode]?.get(NormalPath) + // TODO: merge with `FirPropertyInitializationAnalyzer` for fewer passes. + val data = PropertyInitializationInfoData(memberPropertySymbols, symbol, graph) + graph.checkPropertyAccesses(memberPropertySymbols, symbol, context, reporter, data) + return data.getValue(graph.exitNode)[NormalPath] } private fun checkProperty( @@ -124,3 +136,96 @@ object FirMemberPropertiesChecker : FirClassChecker() { } } } + +val FirDeclaration.evaluatedInPlace: Boolean + get() = when (this) { + is FirAnonymousFunction -> invocationKind.isInPlace + is FirAnonymousObject -> classKind != ClassKind.ENUM_ENTRY + is FirConstructor -> true // child of class initialization graph + is FirFunction, is FirClass -> false + else -> true // property initializer, etc. + } + +@OptIn(SymbolInternals::class) +val FirPropertySymbol.requiresInitialization: Boolean + get() = this !is FirSyntheticPropertySymbol && !hasInitializer && !hasExplicitBackingField && + hasBackingField && fir.isCatchParameter != true + +fun ControlFlowGraph.checkPropertyAccesses( + properties: Set, + receiver: FirBasedSymbol<*>?, + context: CheckerContext, + reporter: DiagnosticReporter, + data: PropertyInitializationInfoData +) { + // NOTE: assert(properties.all { it.requiresInitialization }) + // If a property has an initializer (or does not need one), then any reads are OK while any writes are OK + // if it's a `var` and bad if it's a `val`. `FirReassignmentAndInvisibleSetterChecker` does this without a CFG. + if (properties.isEmpty()) return + + checkPropertyAccesses(properties, receiver, context, reporter, data, null, mutableMapOf()) +} + +@OptIn(SymbolInternals::class) +private fun ControlFlowGraph.checkPropertyAccesses( + properties: Set, + receiver: FirBasedSymbol<*>?, + context: CheckerContext, + reporter: DiagnosticReporter, + data: PropertyInitializationInfoData, + scope: FirDeclaration?, + scopes: MutableMap, +) { + fun FirQualifiedAccess.hasCorrectReceiver() = + (dispatchReceiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol == receiver + + for (node in nodes) { + when { + // TODO: `node.isUnion` - f({ x = 1 }, { x = 2 }) - which to report? + // Also this is currently indistinguishable from x = 1; f({}, {}). + + node is VariableDeclarationNode -> { + val symbol = node.fir.symbol + if (scope != null && receiver == null && node.fir.isVal && symbol in properties) { + // It's OK to initialize this variable from a nested called-in-place function, but not from + // a non-called-in-place function or a non-anonymous-object class initializer. + scopes[symbol] = scope + } + } + + node is VariableAssignmentNode -> { + val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue + if (!symbol.fir.isVal || !node.fir.hasCorrectReceiver() || symbol !in properties) continue + + if (scope != scopes[symbol]) { + val error = if (receiver != null) + FirErrors.CAPTURED_MEMBER_VAL_INITIALIZATION + else + FirErrors.CAPTURED_VAL_INITIALIZATION + reporter.reportOn(node.fir.lValue.source, error, symbol, context) + } else if (data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) { + reporter.reportOn(node.fir.lValue.source, FirErrors.VAL_REASSIGNMENT, symbol, context) + } + } + + node is QualifiedAccessNode -> { + val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue + if (!symbol.isLateInit && node.fir.hasCorrectReceiver() && symbol in properties && + data.getValue(node).values.any { it[symbol]?.isDefinitelyVisited() != true } + ) { + reporter.reportOn(node.fir.source, FirErrors.UNINITIALIZED_VARIABLE, symbol, context) + } + } + + // In the class case, subgraphs of the exit node are member functions, which are considered to not + // be part of initialization, so any val is considered to be initialized there and the CFG is not + // needed. The errors on reassignments will be emitted by `FirReassignmentAndInvisibleSetterChecker`. + node is CFGNodeWithSubgraphs<*> && (receiver == null || node !== exitNode) -> { + for (subGraph in node.subGraphs) { + val newScope = subGraph.declaration?.takeIf { !it.evaluatedInPlace } ?: scope + subGraph.checkPropertyAccesses(properties, receiver, context, reporter, data, newScope, scopes) + } + } + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt index 4a0ee73a11d..485734ea9d5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReassignmentAndInvisibleSetterChecker.kt @@ -10,15 +10,13 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.evaluatedInPlace +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.requiresInitialization import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.fir.containingClassLookupTag -import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer -import org.jetbrains.kotlin.fir.declarations.FirConstructor -import org.jetbrains.kotlin.fir.declarations.FirProperty -import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor -import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField +import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.visibility +import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment @@ -29,12 +27,8 @@ import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol import org.jetbrains.kotlin.fir.references.toResolvedValueParameterSymbol import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue -import org.jetbrains.kotlin.fir.resolve.toSymbol -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol -import org.jetbrains.kotlin.fir.types.toSymbol import org.jetbrains.kotlin.fir.visibilityChecker object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker() { @@ -43,7 +37,7 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker() checkValReassignmentViaBackingField(expression, context, reporter) checkValReassignmentOnValueParameter(expression, context, reporter) checkAssignmentToThis(expression, context, reporter) - checkValReassignmentOfNonLocalProperty(expression, context, reporter) + checkValReassignment(expression, context, reporter) } private fun checkInvisibleSetter( @@ -127,39 +121,30 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker() } } - private fun checkValReassignmentOfNonLocalProperty( - expression: FirVariableAssignment, - context: CheckerContext, - reporter: DiagnosticReporter - ) { + private fun checkValReassignment(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { val property = expression.lValue.toResolvedPropertySymbol() ?: return - if (property.isLocal || property.isVar) return - val isLegal = when { - property is FirSyntheticPropertySymbol -> false - property.hasDelegate || !property.hasBackingField -> false - property.hasInitializer -> false - else -> { - val thisReceiverSymbol = (expression.dispatchReceiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol - thisReceiverSymbol != null && isInsideInitializationOfClass(thisReceiverSymbol, context) + if (property.isVar) return + // Assignments of uninitialized `val`s must be checked via CFG, since the first one is OK. + // See `FirPropertyInitializationAnalyzer` for locals and `FirMemberPropertiesChecker` for backing fields in initializers. + if (property.requiresInitialization && (property.isLocal || isInOwnersInitializer(expression.dispatchReceiver, context))) return + + reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context) + } + + private fun isInOwnersInitializer(receiver: FirExpression, context: CheckerContext): Boolean { + val uninitializedThisSymbol = (receiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol ?: return false + var foundInitializer = false + for ((i, declaration) in context.containingDeclarations.withIndex()) { + if (declaration is FirClass) { + foundInitializer = if (context.containingDeclarations.getOrNull(i + 1)?.evaluatedInPlace == false) { + // In member function of a class, assume all outer classes are already initialized + // by the time this function is called. + false + } else { + foundInitializer || declaration.symbol == uninitializedThisSymbol + } } } - if (!isLegal) { - reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, property, context) - } - } - - private fun isInsideInitializationOfClass(classSymbol: FirBasedSymbol<*>, context: CheckerContext): Boolean { - val session = context.session - for (containingDeclaration in context.containingDeclarations) { - val containingClassSymbol = when (containingDeclaration) { - is FirProperty -> containingDeclaration.containingClassLookupTag()?.toSymbol(session) - is FirAnonymousInitializer -> containingDeclaration.dispatchReceiverType?.toSymbol(session) - is FirConstructor -> containingDeclaration.containingClassLookupTag()?.toSymbol(session) - else -> null - } ?: continue - - if (containingClassSymbol == classSymbol) return true - } - return false + return foundInitializer } } 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 258cfe09ee8..e79c832d788 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 @@ -30,7 +30,7 @@ class ControlFlowAnalysisDiagnosticComponent( val (properties, capturedWrites) = LocalPropertyAndCapturedWriteCollector.collect(graph) if (properties.isNotEmpty()) { - val data = PropertyInitializationInfoData(properties, graph) + val data = PropertyInitializationInfoData(properties, receiver = null, graph) variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties, capturedWrites, context) } } } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt index bcd55608f36..7f61c231d27 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt @@ -134,7 +134,7 @@ class AnonymousInitializers(var a: String, val b: String) { get() = 20 init { - i = 13 + i = 13 j = 34 } @@ -174,7 +174,7 @@ class AnonymousInitializers(var a: String, val b: String) { val n: Int init { - while (n == 0) { + while (n == 0) { } n = 10 while (n == 0) { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.fir.kt index f20a3b350a6..cb930b76e58 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.fir.kt @@ -7,7 +7,7 @@ class Test { val t = object { fun some() { // See KT-13597 - a = "12" + a = "12" } } @@ -20,7 +20,7 @@ class Test2 { init { val t = object { fun some() { - a = "12" + a = "12" } } @@ -41,7 +41,7 @@ class Test4 { init { exec { // See KT-14381 - a = "12" + a = "12" } a = "34" } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.fir.kt index 47b188bc841..906cb88415d 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.fir.kt @@ -27,7 +27,7 @@ fun gav() { class B { init { // Error! See KT-10445 - x = "" + x = "" } } // Error! See KT-10042 @@ -36,7 +36,7 @@ fun gav() { class C(val s: String) { constructor(): this("") { // Error! - y = s + y = s } } y.length @@ -77,7 +77,7 @@ class My { class Your { init { // Error! See KT-10445 - x = "" + x = "" } } } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_after.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_after.fir.kt index 6e7d54e92c7..92bc609ce34 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_after.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_after.fir.kt @@ -10,9 +10,9 @@ class Test { run { // Not sure do we need diagnostic also here - this@Test.str = "B" + this@Test.str = "B" } - str = "C" + str = "C" } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.fir.kt index b04ada52941..11fbf5ef942 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalViaExplicitThis_before.fir.kt @@ -10,9 +10,9 @@ class Test { run { // Not sure do we need diagnostic also here - this@Test.str = "B" + this@Test.str = "B" } - str = "C" + str = "C" } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.fir.kt index 639356afe88..bc4d852b749 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.fir.kt @@ -3,9 +3,9 @@ class A(val next: A? = null) { init { next?.x = "a" x = "b" - this.x = "c" - x = "d" // don't repeat the same diagnostic again with this receiver - this.x = "e" + this.x = "c" + x = "d" // don't repeat the same diagnostic again with this receiver + this.x = "e" next?.x = "f" } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.fir.kt index 70e085e7f8c..3b2cdaae39e 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.fir.kt @@ -11,9 +11,9 @@ class Outer { outer.outerProp = "3" innerProp = "4" + inner.innerProp - this@Inner.innerProp = "5" - innerProp = "6" // do not repeat the same diagnostic with this receiver - this@Inner.innerProp = "7" + this@Inner.innerProp = "5" + innerProp = "6" // do not repeat the same diagnostic with this receiver + this@Inner.innerProp = "7" inner.innerProp = "8" } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.fir.kt index 3cd5644c1b2..8c2b51fa08b 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.fir.kt @@ -3,9 +3,9 @@ class A(val next: A? = null) { init { next?.x = "a" this@A.x = "b" - this.x = "c" - x = "d" // don't repeat the same diagnostic again with this receiver - this@A.x = "e" + this.x = "c" + x = "d" // don't repeat the same diagnostic again with this receiver + this@A.x = "e" next?.x = "f" } diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithPrimary.fir.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithPrimary.fir.kt index f63590ed30f..77dbaa83059 100644 --- a/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithPrimary.fir.kt +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithPrimary.fir.kt @@ -11,7 +11,7 @@ class A(val w: Char) { constructor(): this('a') { y = 1 - overinitialized = 2 + overinitialized = 2 uninitialized = 3 } diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithoutPrimary.fir.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithoutPrimary.fir.kt deleted file mode 100644 index 9dcb548bd5a..00000000000 --- a/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithoutPrimary.fir.kt +++ /dev/null @@ -1,39 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -class A { - val x: Int - var y: Int - val z: Int - val v = -1 - - val uninitialized: Int - val overinitialized: Int - - constructor() { - x = 1 - y = 2 - - overinitialized = 3 - uninitialized = 4 - } - - constructor(a: Int): super() { - x = 5 - y = 6 - } - - constructor(x: String): this() { - y = 7 - uninitialized = 8 - } - - //anonymous - init { - z = 9 - overinitialized = 10 - } - - // anonymous - init { - y = 12 - } -} diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithoutPrimary.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithoutPrimary.kt index 35f31915e4f..c2cdd885dad 100644 --- a/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithoutPrimary.kt +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/propertyInitializationWithoutPrimary.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER class A { val x: Int diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithPrimary.fir.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithPrimary.fir.kt index 6d4a56af1dd..da1868491b7 100644 --- a/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithPrimary.fir.kt +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithPrimary.fir.kt @@ -1,8 +1,8 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER class A(val w: Int) { val x: Int - val useUnitialized = x + - y + + val useUnitialized = x + + y + v var y: Int val v = -1 @@ -11,20 +11,20 @@ class A(val w: Int) { val uninitialized: Int constructor(): this(1) { - x + y + v + uninitialized + w + x + y + v + uninitialized + w } // anonymous init { - x + y + v + uninitialized + w + x + y + v + uninitialized + w x = 1 - x + y + v + uninitialized + w + x + y + v + uninitialized + w } // anonymous init { - x + y + v + uninitialized + w + x + y + v + uninitialized + w y = 7 - x + y + v + uninitialized + w + x + y + v + uninitialized + w } } diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithoutPrimary.fir.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithoutPrimary.fir.kt index 60a66e4b4ef..cc283d39bc6 100644 --- a/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithoutPrimary.fir.kt +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/useOfPropertiesWithoutPrimary.fir.kt @@ -1,8 +1,8 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER class A { val x: Int - val useUnitialized = x + // reported on each secondary constructor - y + + val useUnitialized = x + // reported on each secondary constructor + y + v var y: Int val v = -1 @@ -19,7 +19,7 @@ class A { x = 1 y = 2 - x + y + v + uninitialized + x + y + v + uninitialized uninitialized = 3 @@ -27,16 +27,16 @@ class A { } constructor(a: Int): super() { - x + y + v + uninitialized + x + y + v + uninitialized x = 4 y = 5 - x + y + v + uninitialized + x + y + v + uninitialized } //anonymous init { - y + y } // anonymous diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.fir.kt index c47f14ab5f1..79b8359396e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteInitialization.fir.kt @@ -87,7 +87,7 @@ class DefiniteInitializationInInitSection { init { myRun { x = 42 } - unknownRun { y = 239 } + unknownRun { y = 239 } } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/1.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/1.fir.kt index cef0d9586af..545bb0baedb 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/1.fir.kt @@ -41,8 +41,8 @@ class case_5 { var value_5: Int init { funWithAtMostOnceCallsInPlace { value_1 = 1 } - funWithUnknownCallsInPlace { value_2 = 1 } - funWithAtLeastOnceCallsInPlace { value_3 = 1 } + funWithUnknownCallsInPlace { value_2 = 1 } + funWithAtLeastOnceCallsInPlace { value_3 = 1 } funWithAtMostOnceCallsInPlace { value_4 = 2 } funWithUnknownCallsInPlace { value_5 = 3 } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/3.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/3.fir.kt index 7eccd38a3af..0aa0c8103c5 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/3.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/3.fir.kt @@ -39,7 +39,7 @@ class case_3(value_1: Any?) { init { if (value_1 is String) { funWithUnknownCallsInPlace { value_2 = 0 } - value_2.div(10) + value_2.div(10) } else if (value_1 == null) { funWithAtLeastOnceCallsInPlace { value_2 = 1 } value_2.div(10) @@ -47,7 +47,7 @@ class case_3(value_1: Any?) { value_2 = 2 } - value_2.div(10) + value_2.div(10) } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/5.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/5.fir.kt index 34e9552498a..76a16a85c5f 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/5.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/5.fir.kt @@ -35,7 +35,7 @@ fun case_4() { fun case_5() { val value_1: Int val o = object { - fun l() { value_1 = 10 } + fun l() { value_1 = 10 } } funWithExactlyOnceCallsInPlace(o::l) value_1.inc()