[FIR] Fix top-level property initialization checks in scripts

- While `collectionInitializationInfo` unwrapped a script's top-level
  declarations, `check` forgot to do it, so a script effectively had no
  top-level properties in the mind of the checker.

^KT-63286 fixed
This commit is contained in:
Marco Pennekamp
2023-11-07 22:51:13 +01:00
committed by Space Team
parent 1cc2390af6
commit 730f98ba38
3 changed files with 23 additions and 20 deletions
@@ -34,29 +34,32 @@ import org.jetbrains.kotlin.lexer.KtTokens
// See old FE's [DeclarationsChecker] // See old FE's [DeclarationsChecker]
object FirTopLevelPropertiesChecker : FirFileChecker() { object FirTopLevelPropertiesChecker : FirFileChecker() {
override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) {
val info = declaration.collectionInitializationInfo(context, reporter) val topLevelProperties = declaration.effectiveTopLevelProperties
for (innerDeclaration in declaration.declarations) { val info = declaration.collectionInitializationInfo(topLevelProperties, context, reporter)
if (innerDeclaration is FirProperty) { for (topLevelProperty in topLevelProperties) {
val symbol = innerDeclaration.symbol val symbol = topLevelProperty.symbol
val isDefinitelyAssigned = info?.get(symbol)?.isDefinitelyVisited() == true val isDefinitelyAssigned = info?.get(symbol)?.isDefinitelyVisited() == true
checkProperty(containingDeclaration = null, innerDeclaration, isDefinitelyAssigned, context, reporter, reachable = true) checkProperty(containingDeclaration = null, topLevelProperty, isDefinitelyAssigned, context, reporter, reachable = true)
}
} }
} }
/**
* Scripts are nested as a single declaration under [FirFile]s and contain their own statements. To properly check all "top-level"
* properties, script statements need to be unwrapped.
*/
private val FirFile.effectiveTopLevelProperties: List<FirProperty>
get() = when (val script = declarations.singleOrNull()) {
is FirScript -> script.statements.filterIsInstance<FirProperty>()
else -> declarations.filterIsInstance<FirProperty>()
}
private fun FirFile.collectionInitializationInfo( private fun FirFile.collectionInitializationInfo(
topLevelProperties: List<FirProperty>,
context: CheckerContext, context: CheckerContext,
reporter: DiagnosticReporter, reporter: DiagnosticReporter,
): PropertyInitializationInfo? { ): PropertyInitializationInfo? {
val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null val graph = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return null
// Scripts are nested as a single declaration under FirFiles and contain their own statements. To properly check all "top-level"
// properties, script statements need to be unwrapped.
val topLevelProperties = when (val script = declarations.singleOrNull()) {
is FirScript -> script.statements.filterIsInstance<FirProperty>()
else -> declarations.filterIsInstance<FirProperty>()
}
val propertySymbols = topLevelProperties.mapNotNullTo(mutableSetOf()) { declaration -> val propertySymbols = topLevelProperties.mapNotNullTo(mutableSetOf()) { declaration ->
declaration.symbol.takeIf { it.requiresInitialization(isForInitialization = true) } declaration.symbol.takeIf { it.requiresInitialization(isForInitialization = true) }
} }
@@ -16,10 +16,10 @@ get() = 42
val testValLineBreakNoType val testValLineBreakNoType
get() = 42 get() = 42
val testValLineBreakSemi: Int; <!MUST_BE_INITIALIZED!>val testValLineBreakSemi: Int<!>;
<!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42 <!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42
val testValLineBreakSemiNoType; <!MUST_BE_INITIALIZED!>val testValLineBreakSemiNoType<!>;
<!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42 <!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42
var testVarLineBreak: Int var testVarLineBreak: Int
@@ -30,10 +30,10 @@ var String.testExtVarLineBreak: Int
get() = 42 get() = 42
set(value) {} set(value) {}
var testVarLineBreakSemi: Int; <!MUST_BE_INITIALIZED!>var testVarLineBreakSemi: Int<!>;
<!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42 <!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42
<!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {} <!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {}
var String.testExtVarLineBreakSemi: Int; <!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.testExtVarLineBreakSemi: Int<!>;
<!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42 <!VARIABLE_EXPECTED!><!UNRESOLVED_REFERENCE!>get<!>()<!> = 42
<!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {} <!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {}
@@ -32,9 +32,9 @@ val d: String by simpleDelegate(d)
val e: String by inPlaceDelegate { e } val e: String by inPlaceDelegate { e }
val f: String by notInPlaceDelegate { f } val f: String by notInPlaceDelegate { f }
val g: Int <!MUST_BE_INITIALIZED!>val g: Int<!>
val h = 1.also { <!VAL_REASSIGNMENT!>g<!> = 2 } val h = 1.also { <!VAL_REASSIGNMENT!>g<!> = 2 }
val i: Int <!MUST_BE_INITIALIZED!>val i: Int<!>
val j by lazy { <!VAL_REASSIGNMENT!>i<!> = 2; 1 } val j by lazy { <!VAL_REASSIGNMENT!>i<!> = 2; 1 }
val k: Int val k: Int
get() { get() {