From 6b532ccd0de88d48e2878dd2aad55c32e29747ce Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Wed, 8 Sep 2021 11:47:45 +0300 Subject: [PATCH] [FIR] Allow lateinit FirBackingField's --- .../diagnostics/FirDiagnosticsList.kt | 3 + .../fir/analysis/diagnostics/FirErrors.kt | 3 + .../FirInapplicableLateinitChecker.kt | 4 ++ .../FirPropertyFieldDeclarationChecker.kt | 22 +++++- .../diagnostics/FirDefaultErrorMessages.kt | 15 ++++ .../converter/DeclarationsConverter.kt | 1 + .../kotlin/fir/builder/RawFirBuilder.kt | 1 + .../kotlin/resolve/ModifierCheckerHelpers.kt | 7 +- .../diagnostics/KtFirDataClassConverters.kt | 18 +++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 12 ++++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 72 +++++++++---------- 11 files changed, 119 insertions(+), 39 deletions(-) diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index ec3b60f53e1..6057fa9a4e8 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -875,6 +875,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val ACCESSOR_FOR_DELEGATED_PROPERTY by error() val PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION by error() val PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER by error() + val LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER by error(PositioningStrategy.LATEINIT_MODIFIER) + val LATEINIT_FIELD_IN_VAL_PROPERTY by error(PositioningStrategy.LATEINIT_MODIFIER) + val LATEINIT_NULLABLE_BACKING_FIELD by error(PositioningStrategy.LATEINIT_MODIFIER) val PROPERTY_MUST_HAVE_GETTER by error() val PROPERTY_MUST_HAVE_SETTER by error() val EXPLICIT_BACKING_FIELD_IN_INTERFACE by error(PositioningStrategy.FIELD_KEYWORD) diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 19e15220786..44b43259ed0 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -482,6 +482,9 @@ object FirErrors { val ACCESSOR_FOR_DELEGATED_PROPERTY by error0() val PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION by error0() val PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER by error0() + val LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER by error0(SourceElementPositioningStrategies.LATEINIT_MODIFIER) + val LATEINIT_FIELD_IN_VAL_PROPERTY by error0(SourceElementPositioningStrategies.LATEINIT_MODIFIER) + val LATEINIT_NULLABLE_BACKING_FIELD by error0(SourceElementPositioningStrategies.LATEINIT_MODIFIER) val PROPERTY_MUST_HAVE_GETTER by error0() val PROPERTY_MUST_HAVE_SETTER by error0() val EXPLICIT_BACKING_FIELD_IN_INTERFACE by error0(SourceElementPositioningStrategies.FIELD_KEYWORD) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInapplicableLateinitChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInapplicableLateinitChecker.kt index 44c08f49b3a..b39e6dc9928 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInapplicableLateinitChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInapplicableLateinitChecker.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter +import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField import org.jetbrains.kotlin.fir.declarations.utils.isLateInit import org.jetbrains.kotlin.fir.types.* @@ -44,6 +45,9 @@ object FirInapplicableLateinitChecker : FirPropertyChecker() { reporter.reportError(declaration.source, "is not allowed on properties of primitive types", context) } + declaration.hasExplicitBackingField -> + reporter.reportError(declaration.source, "must be moved to the field declaration", context) + declaration.hasGetter() || declaration.hasSetter() -> reporter.reportError(declaration.source, "is not allowed on properties with a custom getter or setter", context) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyFieldDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyFieldDeclarationChecker.kt index 7a19d308d1a..f07a2a4351a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyFieldDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPropertyFieldDeclarationChecker.kt @@ -9,13 +9,17 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.FirBackingField import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField +import org.jetbrains.kotlin.fir.declarations.utils.isLateInit import org.jetbrains.kotlin.fir.expressions.FirErrorExpression import org.jetbrains.kotlin.fir.typeContext +import org.jetbrains.kotlin.fir.types.ConeTypeParameterType import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.isNullable import org.jetbrains.kotlin.fir.types.isSubtypeOf object FirPropertyFieldTypeChecker : FirPropertyChecker() { @@ -35,8 +39,18 @@ object FirPropertyFieldTypeChecker : FirPropertyChecker() { reporter.reportOn(declaration.initializer?.source, FirErrors.PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION, context) } - if (backingField.initializer == null) { + if (backingField.isLateInit && declaration.isVal) { + reporter.reportOn(backingField.source, FirErrors.LATEINIT_FIELD_IN_VAL_PROPERTY, context) + } + + if (backingField.initializer == null && !backingField.isLateInit) { reporter.reportOn(backingField.source, FirErrors.PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER, context) + } else if (backingField.initializer != null && backingField.isLateInit) { + reporter.reportOn(backingField.source, FirErrors.LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER, context) + } + + if (backingField.isLateInit && backingField.isNullable) { + reporter.reportOn(backingField.source, FirErrors.LATEINIT_NULLABLE_BACKING_FIELD, context) } if (backingField.returnTypeRef.coneType == declaration.returnTypeRef.coneType) { @@ -53,6 +67,12 @@ object FirPropertyFieldTypeChecker : FirPropertyChecker() { } } + private val FirBackingField.isNullable + get() = when (val type = returnTypeRef.coneType) { + is ConeTypeParameterType -> type.isNullable || type.lookupTag.typeParameterSymbol.resolvedBounds.any { it.coneType.isNullable } + else -> type.isNullable + } + private val FirPropertyAccessor?.isNotExplicit get() = this == null || this is FirDefaultPropertyAccessor diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 94f6da17c06..d0518fdb852 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -257,6 +257,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_REFEREN import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IS_ENUM_ENTRY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_FIELD_IN_VAL_PROPERTY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_NULLABLE_BACKING_FIELD +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_ANNOTATION_CLASS_ERROR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_INTERFACE_NOT_ALLOWED @@ -1283,6 +1286,18 @@ class FirDefaultErrorMessages { PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER, "Property backing field declaration must have an initializer" ) + map.put( + LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER, + "Lateinit backing field cannot have an initializer" + ) + map.put( + LATEINIT_FIELD_IN_VAL_PROPERTY, + "Only mutable properties can have a mutable backing field. Consider changing ''val'' to ''var''" + ) + map.put( + LATEINIT_NULLABLE_BACKING_FIELD, + "Lateinit modifier is not allowed on backing fields of a type with nullable upper bound" + ) map.put( PROPERTY_MUST_HAVE_GETTER, "This property needs a custom getter, because it's type is not a supertype of the backing field's type" diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt index d65ce879afe..043a4ec4d32 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt @@ -1403,6 +1403,7 @@ class DeclarationsConverter( return FirDeclarationStatusImpl(componentVisibility, modifiers.getModality(isClassOrObject = false)).apply { isInline = propertyModifiers.hasInline() || modifiers.hasInline() isExternal = propertyModifiers.hasExternal() || modifiers.hasExternal() + isLateInit = modifiers.hasLateinit() } } diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index 13573a816a3..d480df911dd 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -463,6 +463,7 @@ open class RawFirBuilder( declaration?.hasModifier(INLINE_KEYWORD) == true isExternal = property.hasModifier(EXTERNAL_KEYWORD) || declaration?.hasModifier(EXTERNAL_KEYWORD) == true + isLateInit = declaration?.hasModifier(LATEINIT_KEYWORD) == true } } diff --git a/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt b/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt index 6dc0e25ea32..9215c557061 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt @@ -198,7 +198,12 @@ val possibleTargetMap = mapOf( REIFIED_KEYWORD to EnumSet.of(KotlinTarget.TYPE_PARAMETER), VARARG_KEYWORD to EnumSet.of(KotlinTarget.VALUE_PARAMETER, KotlinTarget.PROPERTY_PARAMETER), COMPANION_KEYWORD to EnumSet.of(KotlinTarget.OBJECT), - LATEINIT_KEYWORD to EnumSet.of(KotlinTarget.MEMBER_PROPERTY, KotlinTarget.TOP_LEVEL_PROPERTY, KotlinTarget.LOCAL_VARIABLE), + LATEINIT_KEYWORD to EnumSet.of( + KotlinTarget.MEMBER_PROPERTY, + KotlinTarget.TOP_LEVEL_PROPERTY, + KotlinTarget.LOCAL_VARIABLE, + KotlinTarget.BACKING_FIELD + ), DATA_KEYWORD to EnumSet.of(KotlinTarget.CLASS_ONLY, KotlinTarget.LOCAL_CLASS), INLINE_KEYWORD to EnumSet.of( KotlinTarget.FUNCTION, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 9ba84adfe46..3bb99d41798 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -2418,6 +2418,24 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER) { firDiagnostic -> + LateinitPropertyFieldDeclarationWithInitializerImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.LATEINIT_FIELD_IN_VAL_PROPERTY) { firDiagnostic -> + LateinitFieldInValPropertyImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.LATEINIT_NULLABLE_BACKING_FIELD) { firDiagnostic -> + LateinitNullableBackingFieldImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.PROPERTY_MUST_HAVE_GETTER) { firDiagnostic -> PropertyMustHaveGetterImpl( firDiagnostic as FirPsiDiagnostic, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index bcab44a501a..d31d5fc2d59 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1711,6 +1711,18 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = PropertyFieldDeclarationMissingInitializer::class } + abstract class LateinitPropertyFieldDeclarationWithInitializer : KtFirDiagnostic() { + override val diagnosticClass get() = LateinitPropertyFieldDeclarationWithInitializer::class + } + + abstract class LateinitFieldInValProperty : KtFirDiagnostic() { + override val diagnosticClass get() = LateinitFieldInValProperty::class + } + + abstract class LateinitNullableBackingField : KtFirDiagnostic() { + override val diagnosticClass get() = LateinitNullableBackingField::class + } + abstract class PropertyMustHaveGetter : KtFirDiagnostic() { override val diagnosticClass get() = PropertyMustHaveGetter::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 5c3edb5847c..b4d37736601 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -2753,6 +2753,27 @@ internal class PropertyFieldDeclarationMissingInitializerImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class LateinitPropertyFieldDeclarationWithInitializerImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.LateinitPropertyFieldDeclarationWithInitializer(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class LateinitFieldInValPropertyImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.LateinitFieldInValProperty(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class LateinitNullableBackingFieldImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.LateinitNullableBackingField(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class PropertyMustHaveGetterImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, @@ -2767,43 +2788,6 @@ internal class PropertyMustHaveSetterImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } -internal class AbstractPropertyInPrimaryConstructorParametersImpl( - firDiagnostic: FirPsiDiagnostic, - override val token: ValidityToken, -) : KtFirDiagnostic.AbstractPropertyInPrimaryConstructorParameters(), KtAbstractFirDiagnostic { - override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) -} - -internal class LocalVariableWithTypeParametersWarningImpl( - firDiagnostic: FirPsiDiagnostic, - override val token: ValidityToken, -) : KtFirDiagnostic.LocalVariableWithTypeParametersWarning(), KtAbstractFirDiagnostic { - override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) -} - -internal class LocalVariableWithTypeParametersImpl( - firDiagnostic: FirPsiDiagnostic, - override val token: ValidityToken, -) : KtFirDiagnostic.LocalVariableWithTypeParameters(), KtAbstractFirDiagnostic { - override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) -} - -internal class InapplicableBackingFieldVisibilityImpl( - override val modifier: KtModifierKeywordToken, - firDiagnostic: FirPsiDiagnostic, - override val token: ValidityToken, -) : KtFirDiagnostic.InapplicableBackingFieldVisibility(), KtAbstractFirDiagnostic { - override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) -} - -internal class InapplicableBackingFieldModifierImpl( - override val modifier: KtModifierKeywordToken, - firDiagnostic: FirPsiDiagnostic, - override val token: ValidityToken, -) : KtFirDiagnostic.InapplicableBackingFieldModifier(), KtAbstractFirDiagnostic { - override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) -} - internal class ExplicitBackingFieldInInterfaceImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, @@ -2839,6 +2823,20 @@ internal class AbstractPropertyInPrimaryConstructorParametersImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class LocalVariableWithTypeParametersWarningImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.LocalVariableWithTypeParametersWarning(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class LocalVariableWithTypeParametersImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.LocalVariableWithTypeParameters(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class ExpectedDeclarationWithBodyImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,