From f1fa290d49d010f0e1ad62005dff9100c53b8024 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Fri, 19 Feb 2021 00:51:16 -0800 Subject: [PATCH] FIR checker: report val reassignment --- .../extendedCheckers/CanBeValChecker.fir.txt | 2 +- .../extendedCheckers/CanBeValChecker.kt | 4 +- .../good/callsInPlace/atLeastOnce.kt | 4 +- .../fromSource/good/callsInPlace/unknown.kt | 4 +- .../diagnostics/FirDiagnosticsList.kt | 5 +- .../fir/analysis/diagnostics/FirErrors.kt | 4 +- .../kotlin/fir/analysis/cfa/CfaUtils.kt | 219 +++++++++++++++++- .../fir/analysis/cfa/ControlFlowInfo.kt | 4 + .../cfa/FirPropertyInitializationAnalyzer.kt | 51 +++- .../diagnostics/FirDefaultErrorMessages.kt | 2 + .../kotlin/fir/resolve/dfa/cfg/CFGNode.kt | 2 +- .../fir/resolve/dfa/cfg/ControlFlowGraph.kt | 5 + .../dfa/cfg/ControlFlowGraphBuilder.kt | 15 +- .../dfa/cfg/ControlFlowGraphNodeBuilder.kt | 4 +- .../UninitializedOrReassignedVariables.fir.kt | 8 +- .../assignedInTryWithCatch.fir.kt | 12 +- .../assignedInTryWithoutCatch.fir.kt | 4 +- .../doWhileAssignment.fir.kt | 23 -- .../controlFlowAnalysis/doWhileAssignment.kt | 1 + .../reassignmentInTryCatch.fir.kt | 12 +- .../DataFlowInMultiDeclInFor.fir.kt | 2 +- .../tests/reassignment/afterfor.fir.kt | 4 +- .../tests/reassignment/dowhile.fir.kt | 10 - .../diagnostics/tests/reassignment/dowhile.kt | 1 + .../tests/reassignment/else.fir.kt | 11 - .../diagnostics/tests/reassignment/else.kt | 1 + .../tests/reassignment/foronly.fir.kt | 9 - .../diagnostics/tests/reassignment/foronly.kt | 1 + .../diagnostics/tests/reassignment/if.fir.kt | 10 - .../diagnostics/tests/reassignment/if.kt | 1 + .../tests/reassignment/ifelse.fir.kt | 13 -- .../diagnostics/tests/reassignment/ifelse.kt | 1 + .../tests/reassignment/when.fir.kt | 10 - .../diagnostics/tests/reassignment/when.kt | 1 + .../tests/reassignment/whiletrue.fir.kt | 11 - .../tests/reassignment/whiletrue.kt | 1 + .../ExhaustiveValOverConditionalInit.fir.kt | 6 +- .../reassignmentToWhenSubjectVariable.fir.kt | 10 - .../reassignmentToWhenSubjectVariable.kt | 1 + .../breakContinuesInInlinedLambda.fir.kt | 6 +- .../breakContinuesInInlinedLambda.kt | 2 +- .../flowInlining/labeledReturns.fir.kt | 2 +- .../flowInlining/nestedTryCatchFinally.fir.kt | 6 +- .../flowInlining/nestedTryCatchs.fir.kt | 4 +- .../controlflow/flowInlining/tryCatch.fir.kt | 58 ----- .../controlflow/flowInlining/tryCatch.kt | 1 + .../flowInlining/tryCatchFinally.fir.kt | 4 +- .../valDefiniteReassignment.fir.kt | 10 +- .../valDefiniteReassignment.fir.kt | 28 --- .../exactlyOnce/valDefiniteReassignment.kt | 1 + .../valIndefiniteInitialization.fir.kt | 4 +- .../unknown/unknownInvocations.fir.kt | 18 -- .../unknown/unknownInvocations.kt | 1 + .../fromStdlib/fromStandardKt.fir.kt | 2 +- .../p-1/neg/1.1.fir.kt | 2 +- .../statements/assignments/p-2/neg/1.1.fir.kt | 12 +- .../controlFlow/initialization/neg/1.fir.kt | 6 +- .../controlFlow/initialization/neg/2.fir.kt | 2 +- .../controlFlow/initialization/neg/3.fir.kt | 2 +- .../controlFlow/initialization/neg/4.fir.kt | 2 +- .../controlFlow/initialization/pos/3.fir.kt | 2 +- .../analysis/smartcasts/neg/9.fir.kt | 2 +- .../analysis/smartcasts/pos/12.fir.kt | 2 +- .../diagnostics/KtFirDataClassConverters.kt | 8 + .../api/fir/diagnostics/KtFirDiagnostics.kt | 8 +- .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 11 +- 66 files changed, 386 insertions(+), 309 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/reassignment/dowhile.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/reassignment/else.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/reassignment/foronly.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/reassignment/if.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/reassignment/ifelse.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/reassignment/when.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/reassignment/whiletrue.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.fir.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.fir.txt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.fir.txt index d11a97c7a6e..fcaef44d599 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.fir.txt @@ -135,7 +135,7 @@ FILE: CanBeValChecker.kt } lval b: R|kotlin/String| - R|/bool| = Boolean(false) + R|/b| = Boolean(false) } public final fun cycles(): R|kotlin/Unit| { lvar a: R|kotlin/Int| = Int(10) diff --git a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.kt b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.kt index 908fed80c3f..237fef2455f 100644 --- a/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.kt +++ b/compiler/fir/analysis-tests/testData/resolve/extendedCheckers/CanBeValChecker.kt @@ -105,9 +105,9 @@ fun foo() { var a: Int val bool = true if (bool) a = 4 else a = 42 - val b: String + val b: String - bool = false + b = false } fun cycles() { diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt index c50cf9a2b6f..bbbd5783b9f 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/atLeastOnce.kt @@ -19,7 +19,7 @@ fun myRun(block: () -> Unit) { fun test_1() { val x: Int inlineRun { - x = 1 + x = 1 } x.inc() } @@ -27,7 +27,7 @@ fun test_1() { fun test_2() { val x: Int myRun { - x = 1 + x = 1 } x.inc() } \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt index 62ada8e05aa..4a6ed0eb69d 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/callsInPlace/unknown.kt @@ -19,7 +19,7 @@ fun myRun(block: () -> Unit) { fun test_1() { val x: Int inlineRun { - x = 1 + x = 1 } x.inc() } @@ -27,7 +27,7 @@ fun test_1() { fun test_2() { val x: Int myRun { - x = 1 + x = 1 } x.inc() } \ No newline at end of file diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 6832b2d9ecd..1a5fc1152a1 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -355,7 +355,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() { } val CONTROL_FLOW by object : DiagnosticGroup("Control flow diagnostics") { - val UNINITIALIZED_VARIABLE by error { + val UNINITIALIZED_VARIABLE by error { + parameter("variable") + } + val VAL_REASSIGNMENT by error { parameter("variable") } val WRONG_INVOCATION_KIND by warning { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 48a9364f5c6..cf3f00d299a 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate +import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -233,7 +234,8 @@ object FirErrors { val COMPONENT_FUNCTION_ON_NULLABLE by error1() // Control flow diagnostics - val UNINITIALIZED_VARIABLE by error1() + val UNINITIALIZED_VARIABLE by error1() + val VAL_REASSIGNMENT by error1() val WRONG_INVOCATION_KIND by warning3, EventOccurrencesRange, EventOccurrencesRange>() val LEAKED_IN_PLACE_LAMBDA by error1>() val WRONG_IMPLIES_CONDITION by warning0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt index c3057877df5..b5c3899c0a5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt @@ -8,9 +8,15 @@ package org.jetbrains.kotlin.fir.analysis.cfa import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.persistentMapOf import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.util.SetMultimap +import org.jetbrains.kotlin.fir.util.setMultimapOf +import org.jetbrains.kotlin.fir.visitors.FirVisitor abstract class EventOccurrencesRangeInfo, K : Any>( map: PersistentMap = persistentMapOf() @@ -74,8 +80,10 @@ class PathAwarePropertyInitializationInfo( ::EMPTY } -class PropertyInitializationInfoCollector(private val localProperties: Set) : - ControlFlowGraphVisitor>>() { +class PropertyInitializationInfoCollector( + private val localProperties: Set, + private val declaredVariableCollector: DeclaredVariableCollector = DeclaredVariableCollector(), +) : ControlFlowGraphVisitor>>() { override fun visitNode( node: CFGNode<*>, data: Collection> @@ -104,11 +112,11 @@ class PropertyInitializationInfoCollector(private val localProperties: Set> ): PathAwarePropertyInitializationInfo { val dataForNode = visitNode(node, data) - return if (node.fir.initializer == null && node.fir.delegate == null) { - dataForNode - } else { - processVariableWithAssignment(dataForNode, node.fir.symbol) - } + return processVariableWithAssignment( + dataForNode, + node.fir.symbol, + overwriteRange = node.fir.initializer == null && node.fir.delegate == null + ) } fun getData(graph: ControlFlowGraph) = @@ -120,10 +128,160 @@ class PropertyInitializationInfoCollector(private val localProperties: Set = + declaredVariableCollector.enterCapturingStatement(statement) + + private fun exitCapturingStatement(statement: FirStatement) { + declaredVariableCollector.exitCapturingStatement(statement) + } + + // A merge point for a loop with `continue` + override fun visitLoopEnterNode( + node: LoopEnterNode, + data: Collection> + ): PathAwarePropertyInitializationInfo { + val declaredVariableSymbolsInLoop = enterCapturingStatement(node.fir) + if (declaredVariableSymbolsInLoop.isEmpty()) + return visitNode(node, data) + + return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data) + } + + // A merge point for while loop + override fun visitLoopConditionEnterNode( + node: LoopConditionEnterNode, + data: Collection> + ): PathAwarePropertyInitializationInfo { + val declaredVariableSymbolsInLoop = declaredVariableCollector.declaredVariablesPerElement[node.loop] + if (declaredVariableSymbolsInLoop.isEmpty()) + return visitNode(node, data) + + return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data) + } + + // A merge point for do-while loop + override fun visitLoopBlockEnterNode( + node: LoopBlockEnterNode, + data: Collection> + ): PathAwarePropertyInitializationInfo { + val declaredVariableSymbolsInLoop = declaredVariableCollector.declaredVariablesPerElement[node.fir] + if (declaredVariableSymbolsInLoop.isEmpty()) + return visitNode(node, data) + + return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data) + } + + private fun filterDeclaredVariableSymbolsInCapturedScope( + node: CFGNode<*>, + declaredVariableSymbolsInCapturedScope: Collection, + data: Collection> + ): PathAwarePropertyInitializationInfo { + var filteredData = data + for (variableSymbol in declaredVariableSymbolsInCapturedScope) { + filteredData = filteredData.map { (label, pathAwareInfo) -> + label to if (label is LoopBackPath) { + removeRange(pathAwareInfo, variableSymbol, ::PathAwarePropertyInitializationInfo) + } else { + pathAwareInfo + } + } + } + return visitNode(node, filteredData) + } + + override fun visitLoopExitNode( + node: LoopExitNode, + data: Collection> + ): PathAwarePropertyInitializationInfo { + exitCapturingStatement(node.fir) + return visitNode(node, data) + } +} + +// Note that [PreliminaryLoopVisitor] in FIR DFA collects assigned variable names. +// This one collects declared variable symbols per capturing statements. +class DeclaredVariableCollector { + val declaredVariablesPerElement: SetMultimap = setMultimapOf() + + fun enterCapturingStatement(statement: FirStatement): Set { + assert(statement is FirLoop || statement is FirClass<*> || statement is FirFunction<*>) + if (statement !in declaredVariablesPerElement) { + statement.accept(visitor, null) + } + return declaredVariablesPerElement[statement] + } + + fun exitCapturingStatement(statement: FirStatement) { + assert(statement is FirLoop || statement is FirClass<*> || statement is FirFunction<*>) + declaredVariablesPerElement.removeKey(statement) + } + + fun resetState() { + declaredVariablesPerElement.clear() + } + + // FirStatement -- closest statement (loop/lambda/local declaration) which may contain reassignments + private val visitor = object : FirVisitor() { + override fun visitElement(element: FirElement, data: FirStatement?) { + element.acceptChildren(this, data) + } + + override fun visitProperty(property: FirProperty, data: FirStatement?) { + if (property.isLocal) { + requireNotNull(data) + declaredVariablesPerElement.put(data, property.symbol) + } + visitElement(property, data) + } + + override fun visitWhileLoop(whileLoop: FirWhileLoop, data: FirStatement?) { + visitCapturingStatement(whileLoop, data) + } + + override fun visitDoWhileLoop(doWhileLoop: FirDoWhileLoop, data: FirStatement?) { + visitCapturingStatement(doWhileLoop, data) + } + + override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: FirStatement?) { + visitCapturingStatement(anonymousFunction, data) + } + + override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: FirStatement?) { + visitCapturingStatement(simpleFunction, data) + } + + override fun > visitFunction(function: FirFunction, data: FirStatement?) { + visitCapturingStatement(function, data) + } + + override fun visitRegularClass(regularClass: FirRegularClass, data: FirStatement?) { + visitCapturingStatement(regularClass, data) + } + + override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: FirStatement?) { + visitCapturingStatement(anonymousObject, data) + } + + private fun visitCapturingStatement(statement: FirStatement, parent: FirStatement?) { + visitElement(statement, statement) + if (parent != null) { + declaredVariablesPerElement.putAll(parent, declaredVariablesPerElement[statement]) + } + } } } @@ -132,15 +290,54 @@ internal fun

, S : ControlFlowInfo) -> P +): P { + // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } + // after (if key is p1): + // { |-> { p1 |-> PI1 + r }, l1 |-> { p1 |-> r, p2 |-> PI2 } } + return updateRange(pathAwareInfo, key, { existingKind -> existingKind + range }, constructor) +} + +internal fun

, S : ControlFlowInfo, K : Any> overwriteRange( + pathAwareInfo: P, + key: K, + range: EventOccurrencesRange, + constructor: (PersistentMap) -> P +): P { + // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } + // after (if key is p1): + // { |-> { p1 |-> r }, l1 |-> { p1 |-> r, p2 |-> PI2 } } + return updateRange(pathAwareInfo, key, { range }, constructor) +} + +private inline fun

, S : ControlFlowInfo, K : Any> updateRange( + pathAwareInfo: P, + key: K, + computeNewRange: (EventOccurrencesRange) -> EventOccurrencesRange, + constructor: (PersistentMap) -> P ): P { var resultMap = persistentMapOf() // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } for ((label, dataPerLabel) in pathAwareInfo) { val existingKind = dataPerLabel[key] ?: EventOccurrencesRange.ZERO - val kind = existingKind + range + val kind = computeNewRange.invoke(existingKind) resultMap = resultMap.put(label, dataPerLabel.put(key, kind)) } // after (if key is p1): - // { |-> { p1 |-> PI1 + r }, l1 |-> { p1 |-> r, p2 |-> PI2 } } + // { |-> { p1 |-> computeNewRange(PI1) }, l1 |-> { p1 |-> r, p2 |-> PI2 } } + return constructor(resultMap) +} + +private fun

, S : ControlFlowInfo, K : Any> removeRange( + pathAwareInfo: P, + key: K, + constructor: (PersistentMap) -> P +): P { + var resultMap = persistentMapOf() + // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } + for ((label, dataPerLabel) in pathAwareInfo) { + resultMap = resultMap.put(label, dataPerLabel.remove(key)) + } + // after (if key is p1): + // { |-> { }, l1 |-> { p2 |-> PI2 } } return constructor(resultMap) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/ControlFlowInfo.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/ControlFlowInfo.kt index 771c98080cf..8bd281d8468 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/ControlFlowInfo.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/ControlFlowInfo.kt @@ -31,5 +31,9 @@ abstract class ControlFlowInfo, K : Any, V : Any> p return constructor(map.put(key, value)) } + override fun remove(key: K): S { + return constructor(map.remove(key)) + } + abstract fun merge(other: S): S } 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 77f95118e22..b79e83fd713 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 @@ -6,16 +6,15 @@ package org.jetbrains.kotlin.fir.analysis.cfa import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange +import org.jetbrains.kotlin.contracts.description.canBeRevisited import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.isLateInit +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraphVisitorVoid -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.QualifiedAccessNode +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol @@ -34,11 +33,11 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec val localProperties = properties.filter { it.fir.initializer == null && it.fir.delegate == null }.toSet() - val reporterVisitor = UninitializedPropertyReporter(localData, localProperties, reporter, context) + val reporterVisitor = PropertyReporter(localData, localProperties, reporter, context) graph.traverse(TraverseDirection.Forward, reporterVisitor) } - private class UninitializedPropertyReporter( + private class PropertyReporter( val data: Map, PathAwarePropertyInitializationInfo>, val localProperties: Set, val reporter: DiagnosticReporter, @@ -46,6 +45,38 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec ) : ControlFlowGraphVisitorVoid() { override fun visitNode(node: CFGNode<*>) {} + private fun getPropertySymbol(node: CFGNode<*>): FirPropertySymbol? { + val reference = (node.fir as? FirQualifiedAccess)?.calleeReference as? FirResolvedNamedReference ?: return null + return reference.resolvedSymbol as? FirPropertySymbol + } + + override fun visitVariableAssignmentNode(node: VariableAssignmentNode) { + val symbol = getPropertySymbol(node) ?: return + val pathAwareInfo = data.getValue(node) + for (label in pathAwareInfo.keys) { + if (investigateVariableAssignment(pathAwareInfo[label]!!, symbol, node)) { + // To avoid duplicate reports, stop investigating remaining paths if the property is re-initialized at any path. + break + } + } + } + + private fun investigateVariableAssignment( + info: PropertyInitializationInfo, + symbol: FirPropertySymbol, + node: VariableAssignmentNode + ): Boolean { + val kind = info[symbol] ?: EventOccurrencesRange.ZERO + if (symbol.fir.isVal && kind.canBeRevisited()) { + node.fir.lValue.source?.let { + // TODO: differentiate CAPTURED_VAL_INITIALIZATION + reporter.report(FirErrors.VAL_REASSIGNMENT.on(it, symbol), context) + return true + } + } + return false + } + override fun visitQualifiedAccessNode(node: QualifiedAccessNode) { val reference = node.fir.calleeReference as? FirResolvedNamedReference ?: return val symbol = reference.resolvedSymbol as? FirPropertySymbol ?: return @@ -53,14 +84,18 @@ object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChec if (symbol.fir.isLateInit) return val pathAwareInfo = data.getValue(node) for (info in pathAwareInfo.values) { - if (investigate(info, symbol, node)) { + if (investigateVariableAccess(info, symbol, node)) { // To avoid duplicate reports, stop investigating remaining paths if the property is not initialized at any path. break } } } - private fun investigate(info: PropertyInitializationInfo, symbol: FirPropertySymbol, node: QualifiedAccessNode): Boolean { + private fun investigateVariableAccess( + info: PropertyInitializationInfo, + symbol: FirPropertySymbol, + node: QualifiedAccessNode + ): Boolean { val kind = info[symbol] ?: EventOccurrencesRange.ZERO if (!kind.isDefinitelyVisited()) { node.fir.source?.let { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 71f6d59d6f3..a8f93e6c14a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -171,6 +171,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_OPERATOR_C import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAL_REASSIGNMENT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAL_WITH_SETTER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_EXPECTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_INITIALIZER_IS_REDUNDANT @@ -510,6 +511,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { // Control flow diagnostics map.put(UNINITIALIZED_VARIABLE, "{0} must be initialized before access", PROPERTY_NAME) + map.put(VAL_REASSIGNMENT, "Val cannot be reassigned", PROPERTY_NAME) map.put( WRONG_INVOCATION_KIND, "{2} wrong invocation kind: given {3} case, but {4} case is possible", diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt index 97b791957b3..a674ac4243c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/CFGNode.kt @@ -394,7 +394,7 @@ class LoopBlockExitNode(owner: ControlFlowGraph, override val fir: FirLoop, leve return visitor.visitLoopBlockExitNode(this, data) } } -class LoopConditionEnterNode(owner: ControlFlowGraph, override val fir: FirExpression, level: Int, id: Int) : CFGNode(owner, level, id), EnterNodeMarker { +class LoopConditionEnterNode(owner: ControlFlowGraph, override val fir: FirExpression, val loop: FirLoop, level: Int, id: Int) : CFGNode(owner, level, id), EnterNodeMarker { override fun accept(visitor: ControlFlowGraphVisitor, data: D): R { return visitor.visitLoopConditionEnterNode(this, data) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 699fcbefd99..ed489e40eda 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -133,6 +133,11 @@ object NormalPath : EdgeLabel(label = null) { get() = true } +object LoopBackPath : EdgeLabel(label = null) { + override val isNormal: Boolean + get() = true +} + object UncaughtExceptionPath : EdgeLabel(label = "onUncaughtException") // TODO: Label `return`ing edge with this. diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index e8d0a90a259..57a6ce22eb3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -657,7 +657,7 @@ class ControlFlowGraphBuilder { } loopExitNodes.push(createLoopExitNode(loop)) levelCounter++ - val conditionEnterNode = createLoopConditionEnterNode(loop.condition).also { + val conditionEnterNode = createLoopConditionEnterNode(loop.condition, loop).also { addNewSimpleNode(it) // put conditional node twice so we can refer it after exit from loop block lastNodes.push(it) @@ -686,7 +686,7 @@ class ControlFlowGraphBuilder { if (lastNodes.isNotEmpty) { val conditionEnterNode = lastNodes.pop() require(conditionEnterNode is LoopConditionEnterNode) { loop.render() } - addBackEdge(loopBlockExitNode, conditionEnterNode) + addBackEdge(loopBlockExitNode, conditionEnterNode, label = LoopBackPath) } val loopExitNode = loopExitNodes.pop() loopExitNode.updateDeadStatus() @@ -714,7 +714,7 @@ class ControlFlowGraphBuilder { fun enterDoWhileLoopCondition(loop: FirLoop): Pair { levelCounter-- val blockExitNode = createLoopBlockExitNode(loop).also { addNewSimpleNode(it) } - val conditionEnterNode = createLoopConditionEnterNode(loop.condition).also { addNewSimpleNode(it) } + val conditionEnterNode = createLoopConditionEnterNode(loop.condition, loop).also { addNewSimpleNode(it) } levelCounter++ return blockExitNode to conditionEnterNode } @@ -727,7 +727,7 @@ class ControlFlowGraphBuilder { popAndAddEdge(conditionExitNode) val blockEnterNode = lastNodes.pop() require(blockEnterNode is LoopBlockEnterNode) - addBackEdge(conditionExitNode, blockEnterNode, isDead = conditionBooleanValue == false) + addBackEdge(conditionExitNode, blockEnterNode, isDead = conditionBooleanValue == false, label = LoopBackPath) val loopExit = loopExitNodes.pop() addEdge(conditionExitNode, loopExit, propagateDeadness = false, isDead = conditionBooleanValue == true) loopExit.updateDeadStatus() @@ -1261,7 +1261,12 @@ class ControlFlowGraphBuilder { popAndAddEdge(node, preferredKind) if (targetNode != null) { if (isBack) { - addBackEdge(node, targetNode) + if (targetNode is LoopEnterNode) { + // `continue` to the loop header + addBackEdge(node, targetNode, label = LoopBackPath) + } else { + addBackEdge(node, targetNode) + } } else { addEdge(node, targetNode, propagateDeadness = false) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt index 551984f9e0c..79b029e3da9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphNodeBuilder.kt @@ -113,8 +113,8 @@ fun ControlFlowGraphBuilder.createWhenBranchResultEnterNode(fir: FirWhenBranch): fun ControlFlowGraphBuilder.createLoopConditionExitNode(fir: FirExpression): LoopConditionExitNode = LoopConditionExitNode(currentGraph, fir, levelCounter, createId()) -fun ControlFlowGraphBuilder.createLoopConditionEnterNode(fir: FirExpression): LoopConditionEnterNode = - LoopConditionEnterNode(currentGraph, fir, levelCounter, createId()) +fun ControlFlowGraphBuilder.createLoopConditionEnterNode(fir: FirExpression, loop: FirLoop): LoopConditionEnterNode = + LoopConditionEnterNode(currentGraph, fir, loop, levelCounter, createId()) fun ControlFlowGraphBuilder.createLoopBlockEnterNode(fir: FirLoop): LoopBlockEnterNode = LoopBlockEnterNode(currentGraph, fir, levelCounter, createId()) diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt index 816c8a0aec8..7b1b9cb4de2 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/UninitializedOrReassignedVariables.fir.kt @@ -63,7 +63,7 @@ fun t4(a: A) { fun t1() { val a : Int = 1 - a = 2 + a = 2 var b : Int = 1 b = 3 @@ -83,8 +83,8 @@ enum class ProtocolState { fun t3() { val x: ProtocolState = ProtocolState.WAITING - x = x.signal() - x = x.signal() //repeat for x + x = x.signal() + x = x.signal() //repeat for x } fun t4() { @@ -326,7 +326,7 @@ fun func() { val a = object { val x = b init { - b = 4 + b = 4 } } } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithCatch.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithCatch.fir.kt index 7bc3cbb4aae..8f3ae25690e 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithCatch.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithCatch.fir.kt @@ -24,7 +24,7 @@ fun assignedInTryAndCatch() { try { a = 42 } catch (e: Exception) { - a = 41 + a = 41 } finally { } a.hashCode() @@ -37,7 +37,7 @@ fun sideEffectBeforeAssignedInTryAndCatch(s: Any) { a = 42 } catch (e: Exception) { s as String // Potential cast exception - a = 41 + a = 41 } finally { } a.hashCode() @@ -48,9 +48,9 @@ fun assignedAtAll() { try { a = 42 } catch (e: Exception) { - a = 41 + a = 41 } finally { - a = 40 + a = 40 } a.hashCode() } @@ -62,9 +62,9 @@ fun sideEffectBeforeAssignedInTryCatchButNotFinally(s: Any) { a = 42 } catch (e: Exception) { s as String // Potential cast exception - a = 41 + a = 41 } finally { - a = 40 + a = 40 } a.hashCode() } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithoutCatch.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithoutCatch.fir.kt index 3e92f0d54c8..94e5c9abaad 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithoutCatch.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/assignedInTryWithoutCatch.fir.kt @@ -22,7 +22,7 @@ fun assignedInTryAndFinally() { try { a = 42 } finally { - a = 41 + a = 41 } a.hashCode() } @@ -33,7 +33,7 @@ fun sideEffectBeforeAssignmentInTryButNotFinally(s: Any) { s as String // Potential cast exception a = 42 } finally { - a = 41 + a = 41 } a.hashCode() } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.fir.kt deleted file mode 100644 index 08fc5a5616d..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.fir.kt +++ /dev/null @@ -1,23 +0,0 @@ -// See KT-15334: incorrect reassignment in do...while - -fun test() { - do { - val s: String - s = "" - } while (s == "") -} - -fun test2() { - do { - val s: String - s = "1" - s = s + "2" - } while (s == "1") -} - -fun test3() { - val s: String - do { - s = "" - } while (s != "") -} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt index d9c17b30497..94fbbd3da6a 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/doWhileAssignment.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // See KT-15334: incorrect reassignment in do...while fun test() { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.fir.kt index f13951b8340..f56399ce5c3 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/reassignmentInTryCatch.fir.kt @@ -8,7 +8,7 @@ fun f1() { } catch (e: Exception) { // KT-13612: reassignment - n = 2 + n = 2 } n.hashCode() } @@ -20,7 +20,7 @@ fun f2() { throw Exception() } finally { - n = 2 + n = 2 } n.hashCode() } @@ -33,7 +33,7 @@ fun g1(flag: Boolean) { } catch (e: Exception) { // KT-13612: ? reassignment or definite assignment ? - n = 2 + n = 2 } n.hashCode() } @@ -45,7 +45,7 @@ fun g2(flag: Boolean) { n = 1 } finally { - n = 2 + n = 2 } n.hashCode() } @@ -84,7 +84,7 @@ fun k1(flag: Boolean) { } catch (e: Exception) { // KT-13612: reassignment - n = 2 + n = 2 } n.hashCode() } @@ -96,7 +96,7 @@ fun k2(flag: Boolean) { j(flag) } finally { - n = 2 + n = 2 } n.hashCode() } diff --git a/compiler/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.fir.kt index f84f4933f5f..cd3b4ba4186 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.fir.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.fir.kt @@ -9,7 +9,7 @@ class A { fun foo(list: List) { for (var (c1, c2, c3) in list) { - c1 = 1 + c1 = 1 c3 + 1 } } diff --git a/compiler/testData/diagnostics/tests/reassignment/afterfor.fir.kt b/compiler/testData/diagnostics/tests/reassignment/afterfor.fir.kt index 17d208686ab..d394b33b4ff 100644 --- a/compiler/testData/diagnostics/tests/reassignment/afterfor.fir.kt +++ b/compiler/testData/diagnostics/tests/reassignment/afterfor.fir.kt @@ -3,8 +3,8 @@ fun foo(k: Int): Int { val i: Int for (j in 1..k) { - i = j + i = j } - i = 6 + i = 6 return i } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/reassignment/dowhile.fir.kt b/compiler/testData/diagnostics/tests/reassignment/dowhile.fir.kt deleted file mode 100644 index 8d76e571678..00000000000 --- a/compiler/testData/diagnostics/tests/reassignment/dowhile.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VALUE - -fun foo(): Int { - val i: Int - var j = 0 - do { - i = ++j - } while (j < 5) - return i -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/reassignment/dowhile.kt b/compiler/testData/diagnostics/tests/reassignment/dowhile.kt index c526242a8b1..472b211d528 100644 --- a/compiler/testData/diagnostics/tests/reassignment/dowhile.kt +++ b/compiler/testData/diagnostics/tests/reassignment/dowhile.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VALUE fun foo(): Int { diff --git a/compiler/testData/diagnostics/tests/reassignment/else.fir.kt b/compiler/testData/diagnostics/tests/reassignment/else.fir.kt deleted file mode 100644 index 3b3d5f2f6d2..00000000000 --- a/compiler/testData/diagnostics/tests/reassignment/else.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VALUE - -fun foo(f: Boolean): Int { - val i: Int - if (f) {} - else { - i = 2 - } - i = 3 - return i -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/reassignment/else.kt b/compiler/testData/diagnostics/tests/reassignment/else.kt index f9df6a8afba..24d95976b8c 100644 --- a/compiler/testData/diagnostics/tests/reassignment/else.kt +++ b/compiler/testData/diagnostics/tests/reassignment/else.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VALUE fun foo(f: Boolean): Int { diff --git a/compiler/testData/diagnostics/tests/reassignment/foronly.fir.kt b/compiler/testData/diagnostics/tests/reassignment/foronly.fir.kt deleted file mode 100644 index b5441ce1ff7..00000000000 --- a/compiler/testData/diagnostics/tests/reassignment/foronly.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VALUE - -fun foo(k: Int): Int { - val i: Int - for (j in 1..k) { - i = j - } - return i -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/reassignment/foronly.kt b/compiler/testData/diagnostics/tests/reassignment/foronly.kt index c7598dd026d..c42206e3db5 100644 --- a/compiler/testData/diagnostics/tests/reassignment/foronly.kt +++ b/compiler/testData/diagnostics/tests/reassignment/foronly.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VALUE fun foo(k: Int): Int { diff --git a/compiler/testData/diagnostics/tests/reassignment/if.fir.kt b/compiler/testData/diagnostics/tests/reassignment/if.fir.kt deleted file mode 100644 index 3b69ff4fb28..00000000000 --- a/compiler/testData/diagnostics/tests/reassignment/if.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VALUE - -fun foo(f: Boolean): Int { - val i: Int - if (f) { - i = 1 - } - i = 3 - return i -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/reassignment/if.kt b/compiler/testData/diagnostics/tests/reassignment/if.kt index 3d3edc4d1b7..aa5a748de2a 100644 --- a/compiler/testData/diagnostics/tests/reassignment/if.kt +++ b/compiler/testData/diagnostics/tests/reassignment/if.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VALUE fun foo(f: Boolean): Int { diff --git a/compiler/testData/diagnostics/tests/reassignment/ifelse.fir.kt b/compiler/testData/diagnostics/tests/reassignment/ifelse.fir.kt deleted file mode 100644 index 4a7acfd06f9..00000000000 --- a/compiler/testData/diagnostics/tests/reassignment/ifelse.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VALUE - -fun foo(f: Boolean): Int { - val i: Int - if (f) { - i = 1 - } - else { - i = 2 - } - i = 3 - return i -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/reassignment/ifelse.kt b/compiler/testData/diagnostics/tests/reassignment/ifelse.kt index abde9286ce4..a02614fda7a 100644 --- a/compiler/testData/diagnostics/tests/reassignment/ifelse.kt +++ b/compiler/testData/diagnostics/tests/reassignment/ifelse.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VALUE fun foo(f: Boolean): Int { diff --git a/compiler/testData/diagnostics/tests/reassignment/when.fir.kt b/compiler/testData/diagnostics/tests/reassignment/when.fir.kt deleted file mode 100644 index bdc46e59d62..00000000000 --- a/compiler/testData/diagnostics/tests/reassignment/when.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VALUE - -fun foo(f: Boolean): Int { - val i: Int - when (f) { - true -> i = 1 - } - i = 3 - return i -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/reassignment/when.kt b/compiler/testData/diagnostics/tests/reassignment/when.kt index 3a97cbdc4ca..2ced4c7f021 100644 --- a/compiler/testData/diagnostics/tests/reassignment/when.kt +++ b/compiler/testData/diagnostics/tests/reassignment/when.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VALUE fun foo(f: Boolean): Int { diff --git a/compiler/testData/diagnostics/tests/reassignment/whiletrue.fir.kt b/compiler/testData/diagnostics/tests/reassignment/whiletrue.fir.kt deleted file mode 100644 index a17eabfa441..00000000000 --- a/compiler/testData/diagnostics/tests/reassignment/whiletrue.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VALUE - -fun foo(): Int { - val i: Int - var j = 0 - while (true) { - i = ++j - if (j > 5) break - } - return i -} diff --git a/compiler/testData/diagnostics/tests/reassignment/whiletrue.kt b/compiler/testData/diagnostics/tests/reassignment/whiletrue.kt index b5867ab389a..4e4df2cd71a 100644 --- a/compiler/testData/diagnostics/tests/reassignment/whiletrue.kt +++ b/compiler/testData/diagnostics/tests/reassignment/whiletrue.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VALUE fun foo(): Int { diff --git a/compiler/testData/diagnostics/tests/when/ExhaustiveValOverConditionalInit.fir.kt b/compiler/testData/diagnostics/tests/when/ExhaustiveValOverConditionalInit.fir.kt index 314fffa5bc6..6cce80a0f93 100644 --- a/compiler/testData/diagnostics/tests/when/ExhaustiveValOverConditionalInit.fir.kt +++ b/compiler/testData/diagnostics/tests/when/ExhaustiveValOverConditionalInit.fir.kt @@ -16,8 +16,8 @@ fun foo(a: Boolean, b: Boolean): Int { x = 1 } when (b) { - true -> x = 2 - false -> x = 3 + true -> x = 2 + false -> x = 3 } return x } @@ -28,7 +28,7 @@ fun bar(a: Boolean, b: Boolean): Int { x = 1 } when (b) { - false -> x = 3 + false -> x = 3 } return x } diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.fir.kt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.fir.kt deleted file mode 100644 index f3aea651833..00000000000 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -// !LANGUAGE: +VariableDeclarationInWhenSubject -// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -UNUSED_VALUE - -fun foo(): Any = 42 - -fun test1(x: Any) { - when (val y = foo()) { - is String -> y = "" - } -} diff --git a/compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt b/compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt index 4a71925c5ee..091933b713d 100644 --- a/compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt +++ b/compiler/testData/diagnostics/tests/when/withSubjectVariable/reassignmentToWhenSubjectVariable.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +VariableDeclarationInWhenSubject // !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -UNUSED_VALUE diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.fir.kt index 7cd95e81dfe..3d2a16a0721 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.fir.kt @@ -33,12 +33,12 @@ fun test() { // Loop executed exectly once, initializing x myRun { x.inc() } - myRun { x = 42 } + myRun { x = 42 } break } - // x is I?D here because loop could've been execited + // x is ID? here because loop could've been execited // VAL_REASSIGNMENT isn't reported because of repeating diagnostic - x = 42 + x = 42 // x is ID now } else diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.kt index c0f84472f48..84bc6d11dc7 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/breakContinuesInInlinedLambda.kt @@ -36,7 +36,7 @@ fun test() { myRun { x = 42 } break } - // x is I?D here because loop could've been execited + // x is ID? here because loop could've been execited // VAL_REASSIGNMENT isn't reported because of repeating diagnostic x = 42 // x is ID now diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/labeledReturns.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/labeledReturns.fir.kt index 925335b16e3..dadefaabe00 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/labeledReturns.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/labeledReturns.fir.kt @@ -42,7 +42,7 @@ fun threeLevelsReturnNoInitialization(x: Int?): Int? { } } // Possible to report unreachable here - y = 54 + y = 54 } return y.inc() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchFinally.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchFinally.fir.kt index 80f20d5ba69..d014d268d74 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchFinally.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchFinally.fir.kt @@ -27,13 +27,13 @@ fun outerFinallyInitializes() { log() } // possible reassignment if innerComputation finished - x = 42 + x = 42 // x is ID here } // Definite reassignment here, cause can get here only if myRun finished // Not reported because of repeating diagnostic - x = outerComputation() + x = outerComputation() } catch (e: java.lang.Exception) { // can catch exception thrown by the inner, so x can be not initialized x.inc() @@ -41,7 +41,7 @@ fun outerFinallyInitializes() { } finally { // Possible reassignment (e.g. if everything finished) // Not reported because of repeating diagnostic - x = 42 + x = 42 } // Properly initialized diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchs.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchs.fir.kt index a942e8d5bfb..92731c8c587 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchs.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/nestedTryCatchs.fir.kt @@ -25,7 +25,7 @@ fun innerTryCatchInitializes() { } catch (e: java.lang.Exception) { // Potential reassignment because x.inc() could threw - x = 42 + x = 42 x.inc() } } @@ -40,7 +40,7 @@ fun innerTryCatchInitializes() { x.inc() // Potential reasignment - x = 42 + x = 42 } // Here x=I because outer try-catch either exited normally (x=I) or catched exception (x=I, with reassingment, though) x.inc() diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.fir.kt deleted file mode 100644 index b0914fa713b..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.fir.kt +++ /dev/null @@ -1,58 +0,0 @@ -// !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect -// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts -// !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER - -import kotlin.contracts.* - -fun myRun(block: () -> T): T { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - return block() -} - -fun someComputation(): Int = 42 - -fun tryCatchInlined() { - val x: Int - - myRun { - try { - x = someComputation() - x.inc() - } - catch (e: java.lang.Exception) { - x.inc() - } - } - x = 42 - x.inc() -} - -fun possibleReassignmentInTryCatch() { - val x: Int - - myRun { - try { - x = someComputation() - x.inc() - } - catch (e: java.lang.Exception) { - x = 42 - x.inc() - } - x.inc() - } - x.inc() -} - -fun tryCatchOuter() { - val x: Int - try { - myRun { x = someComputation() } - x.inc() - } - catch (e: java.lang.Exception) { - x.inc() - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.kt index c24e15d9c91..4bd16a3b1b9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatch.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect // !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts // !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatchFinally.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatchFinally.fir.kt index c62c45b5a24..d3a10206f10 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatchFinally.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/tryCatchFinally.fir.kt @@ -23,10 +23,10 @@ fun innerTryCatchFinally() { x = someComputation() report(x) } catch (e: java.lang.Exception) { - x = 42 + x = 42 report(x) } finally { - x = 0 + x = 0 } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/atLeastOnce/valDefiniteReassignment.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/atLeastOnce/valDefiniteReassignment.fir.kt index ad3579b333a..54576e9367e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/atLeastOnce/valDefiniteReassignment.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/atLeastOnce/valDefiniteReassignment.fir.kt @@ -22,7 +22,7 @@ fun runOnce(block: () -> T): T { fun valueReassignment() { val x: Int x.inc() - runTwice { x = 42 } + runTwice { x = 42 } x.inc() } @@ -36,7 +36,7 @@ fun branchingFlow(a: Any?) { val x: Int x.inc() if (a is String) { - runTwice { x = 42 } + runTwice { x = 42 } } else { x = 43 @@ -47,7 +47,7 @@ fun branchingFlow(a: Any?) { fun branchingFlowWithMissingBranches(a: Any?) { val x: Int if (a is String) { - runTwice { x = 42 } + runTwice { x = 42 } } x.inc() @@ -58,7 +58,7 @@ fun repeatingFlow(n: Int) { x.inc() for (i in 1..n) { - runTwice { x = 42 } + runTwice { x = 42 } } x.inc() @@ -68,7 +68,7 @@ fun repeatingFlow2(n: Int) { val x: Int for (i in 1..n) { - runTwice { x = 42 } + runTwice { x = 42 } } x.inc() diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.fir.kt deleted file mode 100644 index 0a493dfc1e7..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.fir.kt +++ /dev/null @@ -1,28 +0,0 @@ -// !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect -// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts -// !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER - -import kotlin.contracts.* - -fun myRun(block: () -> T): T { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - return block() -} - -fun reassignmentInUsualFlow() { - val x: Int - myRun { x = 42 } - x = 43 - x.inc() -} - -fun reassignment() { - val x = 42 - myRun { - x = 43 - } - x.inc() -} - diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.kt index c929eab5640..b3473388198 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valDefiniteReassignment.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect // !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts // !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valIndefiniteInitialization.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valIndefiniteInitialization.fir.kt index 885fd63904c..ee888f657d7 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valIndefiniteInitialization.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/exactlyOnce/valIndefiniteInitialization.fir.kt @@ -24,7 +24,7 @@ fun branchingIndetermineFlow(a: Any?) { if (a is String) { myRepeat(a.length) { // Val reassignment because we know that repeat's lambda called in-place - myRun { x = 42 } + myRun { x = 42 } } } else { @@ -45,7 +45,7 @@ fun multipleAssignments() { val x: Int myRepeat(42) { // Val reassignment because we know that repeat's lambda called in-place - myRun { x = 42 } + myRun { x = 42 } } x.inc() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.fir.kt deleted file mode 100644 index 4fd09584022..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -// !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect -// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts -// !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER - -import kotlin.contracts.* - -fun inPlace(block: () -> T): T { - contract { - callsInPlace(block) - } - return block() -} - -fun reassignmentAndNoInitializaiton() { - val x: Int - inPlace { x = 42 } - x.inc() -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.kt index bbab5b969b0..d480523d1ac 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/initialization/unknown/unknownInvocations.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +AllowContractsForCustomFunctions +UseCallsInPlaceEffect // !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts // !DIAGNOSTICS: -INVISIBLE_REFERENCE -INVISIBLE_MEMBER diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/fromStdlib/fromStandardKt.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/fromStdlib/fromStandardKt.fir.kt index 9bb13f9070d..23e8bc2e779 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/fromStdlib/fromStandardKt.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/fromStdlib/fromStandardKt.fir.kt @@ -86,7 +86,7 @@ fun testRepeatOnVal(x: Int) { val y: Int repeat(x) { // reassignment instead of captured val initialization - y = 42 + y = 42 } println(y) } diff --git a/compiler/tests-spec/testData/diagnostics/linked/declarations/property-declaration/local-property-declaration/p-1/neg/1.1.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/declarations/property-declaration/local-property-declaration/p-1/neg/1.1.fir.kt index 2b2abf3970a..1ba0d529541 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/declarations/property-declaration/local-property-declaration/p-1/neg/1.1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/declarations/property-declaration/local-property-declaration/p-1/neg/1.1.fir.kt @@ -26,7 +26,7 @@ fun case1() { fun case2() { val x: Boolean = false try { - x = (throw Exception()) || true //VAL_REASSIGNMENT should be + x = (throw Exception()) || true //VAL_REASSIGNMENT should be } catch (e: Exception) { } } diff --git a/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-2/neg/1.1.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-2/neg/1.1.fir.kt index 73180fdd925..849b63738a0 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-2/neg/1.1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-2/neg/1.1.fir.kt @@ -9,12 +9,12 @@ fun case1() { val x : Any x = "0" - x = 1 - x = 2.0 + x = 1 + x = 2.0 val y : Any = 0 - y = "0" - y = 1.0 + y = "0" + y = 1.0 } /* @@ -23,8 +23,8 @@ fun case1() { */ fun case2() { val x : Any - mutableListOf(0).forEach({ x = it }) + mutableListOf(0).forEach({ x = it }) val y : Any = 1 - mutableListOf(1).forEach({ y = it }) + mutableListOf(1).forEach({ y = it }) } 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 bc31ca37459..f7b700cd607 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 @@ -4,7 +4,7 @@ // TESTCASE NUMBER: 1 fun case_1() { val value_1: Int - funWithAtLeastOnceCallsInPlace { value_1 = 10 } + funWithAtLeastOnceCallsInPlace { value_1 = 10 } value_1.inc() } @@ -18,7 +18,7 @@ fun case_2() { // TESTCASE NUMBER: 3 fun case_3() { val value_1: Int - funWithUnknownCallsInPlace { value_1 = 10 } + funWithUnknownCallsInPlace { value_1 = 10 } value_1.inc() } @@ -52,7 +52,7 @@ class case_5 { fun case_6() { val value_1: Int for (i in 0..1) - funWithExactlyOnceCallsInPlace { value_1 = 10 } + funWithExactlyOnceCallsInPlace { value_1 = 10 } value_1.dec() } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/2.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/2.fir.kt index cb1e103b842..3f2c40f2f3a 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/2.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/2.fir.kt @@ -6,7 +6,7 @@ fun case_1() { val value_1: Int funWithAtLeastOnceCallsInPlace { funWithAtMostOnceCallsInPlace { - value_1 = 1 + value_1 = 1 funWithExactlyOnceCallsInPlace { value_1.inc() } 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 bb192f928b3..48ea8036dd0 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 @@ -121,7 +121,7 @@ fun case_8() { funWithExactlyOnceCallsInPlace outer@ { funWithAtMostOnceCallsInPlace { funWithUnknownCallsInPlace { - x = 42 + x = 42 } return@outer } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/4.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/4.fir.kt index 2e0b1721e68..426ee3c2fe6 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/4.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/neg/4.fir.kt @@ -79,7 +79,7 @@ fun case_5() { value_1.inc() } funWithUnknownCallsInPlace { - value_1 = 1 + value_1 = 1 } value_1.dec() } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/pos/3.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/pos/3.fir.kt index 78ab2981a0e..dfc40bb1848 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/pos/3.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/controlFlow/initialization/pos/3.fir.kt @@ -155,7 +155,7 @@ fun case_10() { val x: Int funWithExactlyOnceCallsInPlace outer@ { funWithAtLeastOnceCallsInPlace { - x = 42 + x = 42 return@outer } } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/neg/9.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/neg/9.fir.kt index d8c134af56f..d4eed4304fb 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/neg/9.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/neg/9.fir.kt @@ -23,6 +23,6 @@ fun case_3() { // TESTCASE NUMBER: 4 fun case_4() { val value_1: Boolean? - funWithUnknownCallsInPlace { value_1 = true } + funWithUnknownCallsInPlace { value_1 = true } value_1.not() } diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/12.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/12.fir.kt index f7cb961a66f..8d1c8c10de2 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/12.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/contracts/analysis/smartcasts/pos/12.fir.kt @@ -23,6 +23,6 @@ fun case_3() { // TESTCASE NUMBER: 4 fun case_4() { val value_1: Boolean? - funWithAtLeastOnceCallsInPlace { value_1 = true } + funWithAtLeastOnceCallsInPlace { value_1 = true } value_1.not() } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 18b7697b6c7..416a9f2a196 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate +import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -1013,6 +1014,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.VAL_REASSIGNMENT) { firDiagnostic -> + ValReassignmentImpl( + firSymbolBuilder.buildVariableSymbol(firDiagnostic.a.fir as FirProperty), + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.WRONG_INVOCATION_KIND) { firDiagnostic -> WrongInvocationKindImpl( firSymbolBuilder.buildSymbol(firDiagnostic.a.fir as FirDeclaration), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 52a7d860d88..ba273c43063 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate +import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -708,11 +709,16 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val componentFunctionName: Name } - abstract class UninitializedVariable : KtFirDiagnostic() { + abstract class UninitializedVariable : KtFirDiagnostic() { override val diagnosticClass get() = UninitializedVariable::class abstract val variable: KtVariableSymbol } + abstract class ValReassignment : KtFirDiagnostic() { + override val diagnosticClass get() = ValReassignment::class + abstract val variable: KtVariableSymbol + } + abstract class WrongInvocationKind : KtFirDiagnostic() { override val diagnosticClass get() = WrongInvocationKind::class abstract val declaration: KtSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 828b9423071..cd6c39f9402 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate +import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -1148,7 +1149,15 @@ internal class UninitializedVariableImpl( override val variable: KtVariableSymbol, firDiagnostic: FirPsiDiagnostic<*>, override val token: ValidityToken, -) : KtFirDiagnostic.UninitializedVariable(), KtAbstractFirDiagnostic { +) : KtFirDiagnostic.UninitializedVariable(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class ValReassignmentImpl( + override val variable: KtVariableSymbol, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.ValReassignment(), KtAbstractFirDiagnostic { override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) }