[FIR] Don't report MUST_BE_INITIALIZED* on unreachable properties
This commit is contained in:
committed by
TeamCityServer
parent
8308f5d7d3
commit
84a7bdffe5
+3
-2
@@ -87,7 +87,8 @@ internal fun checkPropertyInitializer(
|
|||||||
modifierList: FirModifierList?,
|
modifierList: FirModifierList?,
|
||||||
isInitialized: Boolean,
|
isInitialized: Boolean,
|
||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
context: CheckerContext
|
context: CheckerContext,
|
||||||
|
reachable: Boolean = true
|
||||||
) {
|
) {
|
||||||
val inInterface = containingClass?.isInterface == true
|
val inInterface = containingClass?.isInterface == true
|
||||||
val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList
|
val hasAbstractModifier = KtTokens.ABSTRACT_KEYWORD in modifierList
|
||||||
@@ -147,7 +148,7 @@ internal fun checkPropertyInitializer(
|
|||||||
if (backingFieldRequired && !inInterface && !property.isLateInit && !isExpect && !isInitialized && !isExternal) {
|
if (backingFieldRequired && !inInterface && !property.isLateInit && !isExpect && !isInitialized && !isExternal) {
|
||||||
if (property.receiverTypeRef != null && !property.hasAccessorImplementation) {
|
if (property.receiverTypeRef != null && !property.hasAccessorImplementation) {
|
||||||
reporter.reportOn(propertySource, FirErrors.EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, context)
|
reporter.reportOn(propertySource, FirErrors.EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, context)
|
||||||
} else { // TODO: can be suppressed not to report diagnostics about no body
|
} else if (reachable) { // TODO: can be suppressed not to report diagnostics about no body
|
||||||
if (containingClass == null || property.hasAccessorImplementation) {
|
if (containingClass == null || property.hasAccessorImplementation) {
|
||||||
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED, context)
|
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED, context)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+42
-9
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
|||||||
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
||||||
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfo
|
import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfo
|
||||||
import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfoCollector
|
import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfoCollector
|
||||||
@@ -20,6 +21,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.BlockExitNode
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.NormalPath
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.NormalPath
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
|
import org.jetbrains.kotlin.fir.resolve.dfa.controlFlowGraph
|
||||||
@@ -40,13 +43,18 @@ object FirMemberPropertiesChecker : FirRegularClassChecker() {
|
|||||||
collectPropertyInitialization(declaration, memberPropertySymbols, initializedInConstructor, initializedInInitOrOtherProperty)
|
collectPropertyInitialization(declaration, memberPropertySymbols, initializedInConstructor, initializedInInitOrOtherProperty)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (propertySymbol in memberPropertySymbols) {
|
val deadEnds = declaration.collectDeadEndDeclarations()
|
||||||
val property = propertySymbol.fir
|
var reachedDeadEnd = false
|
||||||
val isInitialized =
|
for (innerDeclaration in declaration.declarations) {
|
||||||
property.initializer != null ||
|
if (innerDeclaration is FirProperty) {
|
||||||
initializedInConstructor.getValue(propertySymbol).isDefinitelyVisited() ||
|
val symbol = innerDeclaration.symbol
|
||||||
initializedInInitOrOtherProperty.getValue(propertySymbol).isDefinitelyVisited()
|
val isInitialized =
|
||||||
checkProperty(declaration, property, isInitialized, context, reporter)
|
innerDeclaration.initializer != null ||
|
||||||
|
initializedInConstructor.getValue(symbol).isDefinitelyVisited() ||
|
||||||
|
initializedInInitOrOtherProperty.getValue(symbol).isDefinitelyVisited()
|
||||||
|
checkProperty(declaration, innerDeclaration, isInitialized, context, reporter, !reachedDeadEnd)
|
||||||
|
}
|
||||||
|
reachedDeadEnd = reachedDeadEnd || deadEnds.contains(innerDeclaration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +148,8 @@ object FirMemberPropertiesChecker : FirRegularClassChecker() {
|
|||||||
property: FirProperty,
|
property: FirProperty,
|
||||||
isInitialized: Boolean,
|
isInitialized: Boolean,
|
||||||
context: CheckerContext,
|
context: CheckerContext,
|
||||||
reporter: DiagnosticReporter
|
reporter: DiagnosticReporter,
|
||||||
|
reachable: Boolean
|
||||||
) {
|
) {
|
||||||
val source = property.source ?: return
|
val source = property.source ?: return
|
||||||
if (source.kind is FirFakeSourceElementKind) return
|
if (source.kind is FirFakeSourceElementKind) return
|
||||||
@@ -155,7 +164,8 @@ object FirMemberPropertiesChecker : FirRegularClassChecker() {
|
|||||||
modifierList,
|
modifierList,
|
||||||
isInitialized,
|
isInitialized,
|
||||||
reporter,
|
reporter,
|
||||||
context
|
context,
|
||||||
|
reachable
|
||||||
)
|
)
|
||||||
checkExpectDeclarationVisibilityAndBody(property, source, reporter, context)
|
checkExpectDeclarationVisibilityAndBody(property, source, reporter, context)
|
||||||
|
|
||||||
@@ -205,4 +215,27 @@ object FirMemberPropertiesChecker : FirRegularClassChecker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun FirRegularClass.collectDeadEndDeclarations(): Set<FirElement> {
|
||||||
|
val cfg = controlFlowGraphReference?.controlFlowGraph ?: return emptySet()
|
||||||
|
return cfg.exitNode.incomingEdges.keys
|
||||||
|
.map { it.fir }
|
||||||
|
.filter { it.isDeadEnd() }
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The idea is to find presumably exit node is dead one
|
||||||
|
* 1. in the case of block expression, it is BlockExitNode
|
||||||
|
* 2. in other cases treat any dead node that leads to exitNode as evidence of deadness
|
||||||
|
* This is all a workaround because ideally exit node itself should be dead if it is unreachable
|
||||||
|
*/
|
||||||
|
private fun FirElement.isDeadEnd(): Boolean {
|
||||||
|
val cfg = (this as? FirControlFlowGraphOwner)?.controlFlowGraphReference?.controlFlowGraph ?: return false
|
||||||
|
cfg.exitNode.incomingEdges.keys.find { it is BlockExitNode }?.let {
|
||||||
|
return it.isDead
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.exitNode.incomingEdges.keys.any { it.isDead }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-4
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// IGNORE_BACKEND: WASM
|
// IGNORE_BACKEND: WASM
|
||||||
// WASM_MUTE_REASON: EXCEPTIONS_NOT_IMPLEMENTED
|
// WASM_MUTE_REASON: EXCEPTIONS_NOT_IMPLEMENTED
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
@@ -12,10 +11,49 @@ class C {
|
|||||||
var uninitializedVar: String
|
var uninitializedVar: String
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String =
|
class Foo {
|
||||||
|
init {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
val uninitializedVal: String
|
||||||
|
|
||||||
|
var uninitializedVar: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar {
|
||||||
|
val initializedVal = 43
|
||||||
|
|
||||||
|
init {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
val uninitializedVal: String
|
||||||
|
|
||||||
|
var uninitializedVar: String
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
try {
|
try {
|
||||||
C()
|
C()
|
||||||
"Fail"
|
return "Fail"
|
||||||
} catch (e: NotImplementedError) {
|
} catch (e: NotImplementedError) {
|
||||||
"OK"
|
//OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Foo()
|
||||||
|
return "Fail"
|
||||||
|
} catch (e: NotImplementedError) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Bar()
|
||||||
|
return "Fail"
|
||||||
|
} catch (e: NotImplementedError) {
|
||||||
|
//OK
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user