[FIR] fix false negative MUST_BE_INITIALIZED for deferred init + custom setter case
^KT-58346 Fixed Review: https://jetbrains.team/p/kt/reviews/9967 BTW this commit accidentaly and partially fixes KT-57553 for K2, because of a cleaner K2 architecture. I will unify MUST_BE_INITIALIZED behaviour in K1 and K2 in the next commits
This commit is contained in:
+4
-4
@@ -35,8 +35,8 @@ object FirMemberPropertiesChecker : FirClassChecker() {
|
||||
for (innerDeclaration in declaration.declarations) {
|
||||
if (innerDeclaration is FirProperty) {
|
||||
val symbol = innerDeclaration.symbol
|
||||
val isInitialized = innerDeclaration.initializer != null || info?.get(symbol)?.isDefinitelyVisited() == true
|
||||
checkProperty(declaration, innerDeclaration, isInitialized, context, reporter, !reachedDeadEnd)
|
||||
val isDefinitelyAssignedInConstructor = info?.get(symbol)?.isDefinitelyVisited() == true
|
||||
checkProperty(declaration, innerDeclaration, isDefinitelyAssignedInConstructor, context, reporter, !reachedDeadEnd)
|
||||
}
|
||||
// Can't just look at each property's graph's enterNode because they may have no graph if there is no initializer.
|
||||
reachedDeadEnd = reachedDeadEnd ||
|
||||
@@ -59,7 +59,7 @@ object FirMemberPropertiesChecker : FirClassChecker() {
|
||||
private fun checkProperty(
|
||||
containingDeclaration: FirClass,
|
||||
property: FirProperty,
|
||||
isInitialized: Boolean,
|
||||
isDefinitelyAssignedInConstructor: Boolean,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter,
|
||||
reachable: Boolean
|
||||
@@ -74,7 +74,7 @@ object FirMemberPropertiesChecker : FirClassChecker() {
|
||||
containingDeclaration,
|
||||
property,
|
||||
modifierList,
|
||||
isInitialized,
|
||||
isDefinitelyAssignedInConstructor,
|
||||
reporter,
|
||||
context,
|
||||
reachable
|
||||
|
||||
+25
-10
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
@@ -42,7 +44,7 @@ object FirTopLevelPropertiesChecker : FirPropertyChecker() {
|
||||
containingClass = null,
|
||||
declaration,
|
||||
modifierList,
|
||||
isInitialized = declaration.initializer != null,
|
||||
isDefinitelyAssignedInConstructor = false, // Only member properties can be assigned in constructors
|
||||
reporter,
|
||||
context
|
||||
)
|
||||
@@ -72,7 +74,7 @@ internal fun checkPropertyInitializer(
|
||||
containingClass: FirClass?,
|
||||
property: FirProperty,
|
||||
modifierList: FirModifierList?,
|
||||
isInitialized: Boolean,
|
||||
isDefinitelyAssignedInConstructor: Boolean,
|
||||
reporter: DiagnosticReporter,
|
||||
context: CheckerContext,
|
||||
reachable: Boolean = true
|
||||
@@ -94,7 +96,7 @@ internal fun checkPropertyInitializer(
|
||||
}
|
||||
|
||||
val backingFieldRequired = property.hasBackingField
|
||||
if (inInterface && backingFieldRequired && property.hasAccessorImplementation) {
|
||||
if (inInterface && backingFieldRequired && property.hasAnyAccessorImplementation) {
|
||||
property.source?.let {
|
||||
reporter.reportOn(it, FirErrors.BACKING_FIELD_IN_INTERFACE, context)
|
||||
}
|
||||
@@ -136,19 +138,22 @@ internal fun checkPropertyInitializer(
|
||||
else -> {
|
||||
val propertySource = property.source ?: return
|
||||
val isExternal = property.isEffectivelyExternal(containingClass, context)
|
||||
val isCorrectlyInitialized =
|
||||
property.initializer != null || isDefinitelyAssignedInConstructor && !property.hasSetterAccessorImplementation &&
|
||||
property.getEffectiveModality(containingClass, context.languageVersionSettings) != Modality.OPEN
|
||||
if (
|
||||
backingFieldRequired &&
|
||||
!inInterface &&
|
||||
!property.isLateInit &&
|
||||
!isExpect &&
|
||||
!isInitialized &&
|
||||
!isCorrectlyInitialized &&
|
||||
!isExternal &&
|
||||
!property.hasExplicitBackingField
|
||||
) {
|
||||
if (property.receiverParameter != null && !property.hasAccessorImplementation) {
|
||||
if (property.receiverParameter != null && !property.hasAnyAccessorImplementation) {
|
||||
reporter.reportOn(propertySource, FirErrors.EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, context)
|
||||
} else if (reachable) { // TODO: can be suppressed not to report diagnostics about no body
|
||||
if (containingClass == null || property.hasAccessorImplementation) {
|
||||
if (containingClass == null || property.hasAnyAccessorImplementation) {
|
||||
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED, context)
|
||||
} else {
|
||||
reporter.reportOn(propertySource, FirErrors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT, context)
|
||||
@@ -160,7 +165,7 @@ internal fun checkPropertyInitializer(
|
||||
reporter.reportOn(propertySource, FirErrors.EXPECTED_LATEINIT_PROPERTY, context)
|
||||
}
|
||||
// TODO: like [BindingContext.MUST_BE_LATEINIT], we should consider variable with uninitialized error.
|
||||
if (backingFieldRequired && !inInterface && isInitialized) {
|
||||
if (backingFieldRequired && !inInterface && isCorrectlyInitialized) {
|
||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.EnableDfaWarningsInK2)) {
|
||||
reporter.reportOn(propertySource, FirErrors.UNNECESSARY_LATEINIT, context)
|
||||
}
|
||||
@@ -170,6 +175,16 @@ internal fun checkPropertyInitializer(
|
||||
}
|
||||
}
|
||||
|
||||
private val FirProperty.hasAccessorImplementation: Boolean
|
||||
get() = (getter !is FirDefaultPropertyAccessor && getter?.hasBody == true) ||
|
||||
(setter !is FirDefaultPropertyAccessor && setter?.hasBody == true)
|
||||
private val FirPropertyAccessor?.hasImplementation: Boolean
|
||||
get() = (this !is FirDefaultPropertyAccessor && this?.hasBody == true)
|
||||
private val FirProperty.hasSetterAccessorImplementation: Boolean
|
||||
get() = setter.hasImplementation
|
||||
private val FirProperty.hasAnyAccessorImplementation: Boolean
|
||||
get() = getter.hasImplementation || setter.hasImplementation
|
||||
|
||||
private fun FirProperty.getEffectiveModality(containingClass: FirClass?, languageVersionSettings: LanguageVersionSettings): Modality? =
|
||||
when (languageVersionSettings.supportsFeature(LanguageFeature.TakeIntoAccountEffectivelyFinalInMustBeInitializedCheck) &&
|
||||
status.modality == Modality.OPEN && containingClass?.status?.modality == Modality.FINAL) {
|
||||
true -> Modality.FINAL
|
||||
false -> status.modality
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user