[FIR] Property accessors are not part of class initialization

When checking for class val property reassignment diagnostic, property
initializers should be treated as part of the class initialization.
However, property accessors should not. Previously, only the property
itself was checked for both of these situations and resulted in not
reporting diagnostic within property accessors.

#KT-59744 Fixed
This commit is contained in:
Brian Norman
2023-08-16 07:15:29 -05:00
committed by Space Team
parent e68cf38e99
commit 7db551c452
3 changed files with 76 additions and 11 deletions
@@ -28,6 +28,33 @@ FILE: reassignOfNonLocalProperty_initializedProperties.kt
) )
public get(): R|kotlin/String| public get(): R|kotlin/String|
public final var b: R|kotlin/String| = String(hello)
public get(): R|kotlin/String| {
this@R|/Some|.R|/Some.x| = String(error)
this@R|/Some|.R|/y| = String(error)
R|/z| = String(error)
^ this@R|/Some|.F|/Some.b|
}
public set(value: R|kotlin/String|): R|kotlin/Unit| {
this@R|/Some|.R|/Some.x| = R|<local>/value|
this@R|/Some|.R|/y| = R|<local>/value|
R|/z| = R|<local>/value|
this@R|/Some|.F|/Some.b| = R|<local>/value|
}
public final var c: R|kotlin/String|
public get(): R|kotlin/String| {
this@R|/Some|.R|/Some.x| = String(error)
this@R|/Some|.R|/y| = String(error)
R|/z| = String(error)
^ String(hello)
}
public set(value: R|kotlin/String|): R|kotlin/Unit| {
this@R|/Some|.R|/Some.x| = R|<local>/value|
this@R|/Some|.R|/y| = R|<local>/value|
R|/z| = R|<local>/value|
}
public final fun test_1(): R|kotlin/Unit| { public final fun test_1(): R|kotlin/Unit| {
this@R|/Some|.R|/Some.x| = String(error) this@R|/Some|.R|/Some.x| = String(error)
this@R|/Some|.R|/y| = String(error) this@R|/Some|.R|/y| = String(error)
@@ -1,4 +1,4 @@
// ISSUE: KT-55493 // ISSUE: KT-55493, KT-59744
// WITH_STDLIB // WITH_STDLIB
val z: String = "ok" val z: String = "ok"
@@ -22,6 +22,33 @@ class Some {
"hello" "hello"
} }
var b: String = "hello"
get() {
<!VAL_REASSIGNMENT!>x<!> = "error"
<!VAL_REASSIGNMENT!>y<!> = "error"
<!VAL_REASSIGNMENT!>z<!> = "error"
return field
}
set(value) {
<!VAL_REASSIGNMENT!>x<!> = value
<!VAL_REASSIGNMENT!>y<!> = value
<!VAL_REASSIGNMENT!>z<!> = value
field = value
}
var c: String
get() {
<!VAL_REASSIGNMENT!>x<!> = "error"
<!VAL_REASSIGNMENT!>y<!> = "error"
<!VAL_REASSIGNMENT!>z<!> = "error"
return "hello"
}
set(value) {
<!VAL_REASSIGNMENT!>x<!> = value
<!VAL_REASSIGNMENT!>y<!> = value
<!VAL_REASSIGNMENT!>z<!> = value
}
fun test_1() { fun test_1() {
<!VAL_REASSIGNMENT!>x<!> = "error" <!VAL_REASSIGNMENT!>x<!> = "error"
<!VAL_REASSIGNMENT!>y<!> = "error" <!VAL_REASSIGNMENT!>y<!> = "error"
@@ -148,18 +148,29 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
private fun isInOwnersInitializer(receiver: FirExpression, context: CheckerContext): Boolean { private fun isInOwnersInitializer(receiver: FirExpression, context: CheckerContext): Boolean {
val uninitializedThisSymbol = (receiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol ?: return false val uninitializedThisSymbol = (receiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol ?: return false
var foundInitializer = false val containingDeclarations = context.containingDeclarations
for ((i, declaration) in context.containingDeclarations.withIndex()) {
if (declaration is FirClass) { val index = containingDeclarations.indexOfFirst { it is FirClass && it.symbol == uninitializedThisSymbol }
foundInitializer = if (context.containingDeclarations.getOrNull(i + 1)?.evaluatedInPlace == false) { if (index == -1) return false
// In member function of a class, assume all outer classes are already initialized
// by the time this function is called. for (i in index until containingDeclarations.size) {
false if (containingDeclarations[i] is FirClass) {
} else { // Properties need special consideration as some parts are evaluated in-place (initializers) and others are not (accessors).
foundInitializer || declaration.symbol == uninitializedThisSymbol // So it is not enough to just check the FirProperty - which is treated as in-place - but the following declaration needs to
// be checked if and only if it is a property accessor.
val container = when (val next = containingDeclarations.getOrNull(i + 1)) {
is FirProperty -> containingDeclarations.getOrNull(i + 2)?.takeIf { it is FirPropertyAccessor } ?: next
else -> next
}
// In member function of a class, assume all outer classes are already initialized
// by the time this function is called.
if (container?.evaluatedInPlace == false) {
return false
} }
} }
} }
return foundInitializer
return true
} }
} }