FIR: check assignments and references to members in constructors

I.e. emit VAL_REASSIGNMENT on repeated assignments to `this.something`,
UNINITIALIZED_VARIABLE on reads of it before any assignment if there is
no initializer, and CAPTURED_MEMBER_VAL_INITIALIZATION on assignments
inside non-called-in-place functions and named classes.

^KT-55528 Fixed
This commit is contained in:
pyos
2023-01-19 12:21:53 +01:00
committed by teamcity
parent 8f45acd71d
commit 99e51f6940
24 changed files with 199 additions and 211 deletions
@@ -10,7 +10,7 @@ class Some {
init {
x = "ok"
x = "error"
<!VAL_REASSIGNMENT!>x<!> = "error"
<!VAL_REASSIGNMENT!>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"
<!VAL_REASSIGNMENT!>x<!> = "error"
y = "ok"
y = "error"
<!VAL_REASSIGNMENT!>y<!> = "error"
<!VAL_REASSIGNMENT!>z<!> = "error"
"there"
}
init {
x = "error"
y = "error"
<!VAL_REASSIGNMENT!>x<!> = "error"
<!VAL_REASSIGNMENT!>y<!> = "error"
<!VAL_REASSIGNMENT!>z<!> = "error"
}
}
@@ -19,7 +19,7 @@ fun test2() {
}
fun localFunc() {
y = 0
<!CAPTURED_VAL_INITIALIZATION!>y<!> = 0
}
}
@@ -33,11 +33,11 @@ fun test3() {
class A {
init {
x = 0
<!CAPTURED_VAL_INITIALIZATION!>x<!> = 0
}
fun localFunc() {
y = 0
<!CAPTURED_VAL_INITIALIZATION!>y<!> = 0
}
}
@@ -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<FirPropertySymbol>,
capturedWrites: Set<FirVariableAssignment>,
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<FirPropertySymbol>,
val capturedWrites: Set<FirVariableAssignment>,
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)
}
@@ -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<FirPropertySymbol>, graph: ControlFlowGraph) {
class PropertyInitializationInfoData(properties: Set<FirPropertySymbol>, 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<PropertyInitializationInfo>() {
fun getData(graph: ControlFlowGraph) =
graph.collectDataForNode(TraverseDirection.Forward, this)
companion object {
private val EMPTY_INFO: PathAwarePropertyInitializationInfo = persistentMapOf(NormalPath to PropertyInitializationInfo.EMPTY)
}
@@ -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<FirPropertySymbol>,
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<FirPropertySymbol>,
receiver: FirBasedSymbol<*>?,
context: CheckerContext,
reporter: DiagnosticReporter,
data: PropertyInitializationInfoData,
scope: FirDeclaration?,
scopes: MutableMap<FirPropertySymbol, FirDeclaration?>,
) {
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)
}
}
}
}
}
@@ -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
}
}
@@ -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) }
}
}
@@ -134,7 +134,7 @@ class AnonymousInitializers(var a: String, val b: String) {
get() = 20
init {
i = 13
<!VAL_REASSIGNMENT!>i<!> = 13
<!VAL_REASSIGNMENT!>j<!> = 34
}
@@ -174,7 +174,7 @@ class AnonymousInitializers(var a: String, val b: String) {
val n: Int
init {
while (n == 0) {
while (<!UNINITIALIZED_VARIABLE!>n<!> == 0) {
}
n = 10
while (n == 0) {
@@ -7,7 +7,7 @@ class Test {
val t = object {
fun some() {
// See KT-13597
a = "12"
<!VAL_REASSIGNMENT!>a<!> = "12"
}
}
@@ -20,7 +20,7 @@ class Test2 {
init {
val t = object {
fun some() {
a = "12"
<!VAL_REASSIGNMENT!>a<!> = "12"
}
}
@@ -41,7 +41,7 @@ class Test4 {
init {
exec {
// See KT-14381
a = "12"
<!CAPTURED_MEMBER_VAL_INITIALIZATION!>a<!> = "12"
}
a = "34"
}
@@ -27,7 +27,7 @@ fun gav() {
class B {
init {
// Error! See KT-10445
x = ""
<!CAPTURED_VAL_INITIALIZATION!>x<!> = ""
}
}
// Error! See KT-10042
@@ -36,7 +36,7 @@ fun gav() {
class C(val s: String) {
constructor(): this("") {
// Error!
y = s
<!CAPTURED_VAL_INITIALIZATION!>y<!> = s
}
}
<!UNINITIALIZED_VARIABLE!>y<!>.length
@@ -77,7 +77,7 @@ class My {
class Your {
init {
// Error! See KT-10445
x = ""
<!CAPTURED_VAL_INITIALIZATION!>x<!> = ""
}
}
}
@@ -10,9 +10,9 @@ class Test {
run {
// Not sure do we need diagnostic also here
this@Test.str = "B"
this@Test.<!VAL_REASSIGNMENT!>str<!> = "B"
}
str = "C"
<!VAL_REASSIGNMENT!>str<!> = "C"
}
}
}
@@ -10,9 +10,9 @@ class Test {
run {
// Not sure do we need diagnostic also here
this@Test.str = "B"
this@Test.<!VAL_REASSIGNMENT!>str<!> = "B"
}
str = "C"
<!VAL_REASSIGNMENT!>str<!> = "C"
}
}
}
@@ -3,9 +3,9 @@ class A(val next: A? = null) {
init {
next?.<!VAL_REASSIGNMENT!>x<!> = "a"
x = "b"
this.x = "c"
x = "d" // don't repeat the same diagnostic again with this receiver
this.x = "e"
this.<!VAL_REASSIGNMENT!>x<!> = "c"
<!VAL_REASSIGNMENT!>x<!> = "d" // don't repeat the same diagnostic again with this receiver
this.<!VAL_REASSIGNMENT!>x<!> = "e"
next?.<!VAL_REASSIGNMENT!>x<!> = "f"
}
@@ -11,9 +11,9 @@ class Outer {
outer.<!VAL_REASSIGNMENT!>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.<!VAL_REASSIGNMENT!>innerProp<!> = "5"
<!VAL_REASSIGNMENT!>innerProp<!> = "6" // do not repeat the same diagnostic with this receiver
this@Inner.<!VAL_REASSIGNMENT!>innerProp<!> = "7"
inner.<!VAL_REASSIGNMENT!>innerProp<!> = "8"
}
@@ -3,9 +3,9 @@ class A(val next: A? = null) {
init {
next?.<!VAL_REASSIGNMENT!>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.<!VAL_REASSIGNMENT!>x<!> = "c"
<!VAL_REASSIGNMENT!>x<!> = "d" // don't repeat the same diagnostic again with this receiver
this@A.<!VAL_REASSIGNMENT!>x<!> = "e"
next?.<!VAL_REASSIGNMENT!>x<!> = "f"
}
@@ -11,7 +11,7 @@ class A(val w: Char) {
constructor(): this('a') {
y = 1
overinitialized = 2
<!VAL_REASSIGNMENT!>overinitialized<!> = 2
uninitialized = 3
}
@@ -1,39 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class A {
val x: Int
var y: Int
val z: Int
val v = -1
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>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
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
class A {
val x: Int
@@ -1,8 +1,8 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class A(val w: Int) {
val x: Int
val useUnitialized = x +
y +
val useUnitialized = <!UNINITIALIZED_VARIABLE!>x<!> +
<!UNINITIALIZED_VARIABLE!>y<!> +
v
var y: Int
val v = -1
@@ -11,20 +11,20 @@ class A(val w: Int) {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val uninitialized: Int<!>
constructor(): this(1) {
x + y + v + uninitialized + w
x + y + v + <!UNINITIALIZED_VARIABLE!>uninitialized<!> + w
}
// anonymous
init {
x + y + v + uninitialized + w
<!UNINITIALIZED_VARIABLE!>x<!> + <!UNINITIALIZED_VARIABLE!>y<!> + v + <!UNINITIALIZED_VARIABLE!>uninitialized<!> + w
x = 1
x + y + v + uninitialized + w
x + <!UNINITIALIZED_VARIABLE!>y<!> + v + <!UNINITIALIZED_VARIABLE!>uninitialized<!> + w
}
// anonymous
init {
x + y + v + uninitialized + w
x + <!UNINITIALIZED_VARIABLE!>y<!> + v + <!UNINITIALIZED_VARIABLE!>uninitialized<!> + w
y = 7
x + y + v + uninitialized + w
x + y + v + <!UNINITIALIZED_VARIABLE!>uninitialized<!> + w
}
}
@@ -1,8 +1,8 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class A {
val x: Int
val useUnitialized = x + // reported on each secondary constructor
y +
val useUnitialized = <!UNINITIALIZED_VARIABLE!>x<!> + // reported on each secondary constructor
<!UNINITIALIZED_VARIABLE!>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_VARIABLE!>uninitialized<!>
uninitialized = 3
@@ -27,16 +27,16 @@ class A {
}
constructor(a: Int): super() {
x + y + v + uninitialized
<!UNINITIALIZED_VARIABLE!>x<!> + y + v + <!UNINITIALIZED_VARIABLE!>uninitialized<!>
x = 4
y = 5
x + y + v + uninitialized
x + y + v + <!UNINITIALIZED_VARIABLE!>uninitialized<!>
}
//anonymous
init {
y
<!UNINITIALIZED_VARIABLE!>y<!>
}
// anonymous
@@ -87,7 +87,7 @@ class DefiniteInitializationInInitSection {
init {
myRun { x = 42 }
unknownRun { y = 239 }
unknownRun { <!CAPTURED_MEMBER_VAL_INITIALIZATION!>y<!> = 239 }
}
}
@@ -41,8 +41,8 @@ class case_5 {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var value_5: Int<!>
init {
funWithAtMostOnceCallsInPlace { value_1 = 1 }
funWithUnknownCallsInPlace { value_2 = 1 }
funWithAtLeastOnceCallsInPlace { value_3 = 1 }
funWithUnknownCallsInPlace { <!VAL_REASSIGNMENT!>value_2<!> = 1 }
funWithAtLeastOnceCallsInPlace { <!VAL_REASSIGNMENT!>value_3<!> = 1 }
funWithAtMostOnceCallsInPlace { value_4 = 2 }
funWithUnknownCallsInPlace { value_5 = 3 }
}
@@ -39,7 +39,7 @@ class case_3(value_1: Any?) {
init {
if (value_1 is String) {
funWithUnknownCallsInPlace { value_2 = 0 }
value_2.div(10)
<!UNINITIALIZED_VARIABLE!>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)
<!UNINITIALIZED_VARIABLE!>value_2<!>.div(10)
}
}
@@ -35,7 +35,7 @@ fun case_4() {
fun case_5() {
val value_1: Int
val o = object {
fun l() { value_1 = 10 }
fun l() { <!CAPTURED_VAL_INITIALIZATION!>value_1<!> = 10 }
}
funWithExactlyOnceCallsInPlace(o::l)
<!UNINITIALIZED_VARIABLE!>value_1<!>.inc()