[FIR] Add type-related diagnostics for the backing field

This commit is contained in:
Nikolay Lunyak
2021-08-13 16:49:16 +03:00
committed by TeamCityServer
parent d2124e1ec6
commit d4d43b9907
14 changed files with 260 additions and 25 deletions
@@ -873,6 +873,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<ConeKotlinType>("actualType")
}
val ACCESSOR_FOR_DELEGATED_PROPERTY by error<KtPropertyAccessor>()
val PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION by error<KtExpression>()
val PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER by error<KtBackingField>()
val PROPERTY_MUST_HAVE_GETTER by error<KtProperty>()
val PROPERTY_MUST_HAVE_SETTER by error<KtProperty>()
val ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS by error<KtModifierListOwner>(PositioningStrategy.ABSTRACT_MODIFIER)
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning<KtProperty>(PositioningStrategy.TYPE_PARAMETERS_LIST)
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error<KtProperty>(PositioningStrategy.TYPE_PARAMETERS_LIST)
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBackingField
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
@@ -479,6 +480,10 @@ object FirErrors {
val WRONG_SETTER_RETURN_TYPE by error0<KtTypeReference>()
val WRONG_GETTER_RETURN_TYPE by error2<KtTypeReference, ConeKotlinType, ConeKotlinType>()
val ACCESSOR_FOR_DELEGATED_PROPERTY by error0<KtPropertyAccessor>()
val PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION by error0<KtExpression>()
val PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER by error0<KtBackingField>()
val PROPERTY_MUST_HAVE_GETTER by error0<KtProperty>()
val PROPERTY_MUST_HAVE_SETTER by error0<KtProperty>()
val ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS by error0<KtModifierListOwner>(SourceElementPositioningStrategies.ABSTRACT_MODIFIER)
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning0<KtProperty>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error0<KtProperty>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
@@ -54,6 +54,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirPropertyTypeParametersChecker,
FirInitializerTypeMismatchChecker,
FirDelegatedPropertyChecker,
FirPropertyFieldTypeChecker,
FirInlinePropertyChecker,
FirPropertyFromParameterChecker,
FirLocalVariableTypeParametersSyntaxChecker,
@@ -162,7 +162,15 @@ internal fun checkPropertyInitializer(
else -> {
val propertySource = property.source ?: return
val isExternal = property.isEffectivelyExternal(containingClass, context)
if (backingFieldRequired && !inInterface && !property.isLateInit && !isExpect && !isInitialized && !isExternal) {
if (
backingFieldRequired &&
!inInterface &&
!property.isLateInit &&
!isExpect &&
!isInitialized &&
!isExternal &&
property.backingField == null
) {
if (property.receiverTypeRef != null && !property.hasAccessorImplementation) {
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
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
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.FirProperty
import org.jetbrains.kotlin.fir.expressions.FirErrorExpression
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.isSubtypeOf
object FirPropertyFieldTypeChecker : FirPropertyChecker() {
override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) {
val backingField = declaration.backingField ?: return
val typeCheckerContext = context.session.typeContext.newBaseTypeCheckerContext(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
if (declaration.initializer != null) {
reporter.reportOn(declaration.initializer?.source, FirErrors.PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION, context)
}
if (backingField.initializer is FirErrorExpression) {
reporter.reportOn(backingField.source, FirErrors.PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER, context)
}
if (backingField.isSubtypeOf(declaration, typeCheckerContext)) {
checkAsPropertyNotSubtype(declaration, context, reporter)
} else if (declaration.isSubtypeOf(backingField, typeCheckerContext)) {
checkAsFieldNotSubtype(declaration, context, reporter)
} else {
checkAsIndependentTypes(declaration, context, reporter)
}
}
private fun checkAsPropertyNotSubtype(
property: FirProperty,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (property.isVar && property.setter == null) {
reporter.reportOn(property.source, FirErrors.PROPERTY_MUST_HAVE_SETTER, context)
}
}
private fun checkAsFieldNotSubtype(
property: FirProperty,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (property.getter == null) {
reporter.reportOn(property.source, FirErrors.PROPERTY_MUST_HAVE_GETTER, context)
}
}
private fun checkAsIndependentTypes(
property: FirProperty,
context: CheckerContext,
reporter: DiagnosticReporter
) {
checkAsPropertyNotSubtype(property, context, reporter)
checkAsFieldNotSubtype(property, context, reporter)
}
}
@@ -333,8 +333,12 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_SETTER_FO
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_AS_OPERATOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_INITIALIZER_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_INITIALIZER_NO_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_MUST_HAVE_GETTER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_MUST_HAVE_SETTER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_TYPE_MISMATCH_BY_DELEGATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_TYPE_MISMATCH_ON_INHERITANCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_TYPE_MISMATCH_ON_OVERRIDE
@@ -1267,6 +1271,22 @@ class FirDefaultErrorMessages {
RENDER_TYPE
)
map.put(ACCESSOR_FOR_DELEGATED_PROPERTY, "Delegated property cannot have accessors with non-default implementations")
map.put(
PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION,
"Property initializers are not allowed for properties with an explicit backing field declaration"
)
map.put(
PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER,
"Property backing field declaration must have an initializer"
)
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"
)
map.put(
PROPERTY_MUST_HAVE_SETTER,
"This property needs a custom setter, because it's type is not a subtype of the backing field's type"
)
map.put(ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS, "This property cannot be declared abstract")
map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING, "Type parameters for local variables are deprecated")
map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS, "Local variables are not allowed to have type parameters")
@@ -524,7 +524,7 @@ class ControlFlowGraphBuilder {
// ----------------------------------- Property -----------------------------------
fun enterProperty(property: FirProperty): PropertyInitializerEnterNode? {
if (property.initializer == null && property.delegate == null) return null
if (property.initializer == null && property.delegate == null && property.backingField == null) return null
val graph = ControlFlowGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer)
pushGraph(graph, Mode.PropertyInitializer)
@@ -542,7 +542,7 @@ class ControlFlowGraphBuilder {
}
fun exitProperty(property: FirProperty): Pair<PropertyInitializerExitNode, ControlFlowGraph>? {
if (property.initializer == null && property.delegate == null) return null
if (property.initializer == null && property.delegate == null && property.backingField == null) return null
val exitNode = exitTargetsForTry.pop() as PropertyInitializerExitNode
popAndAddEdge(exitNode)
val graph = popGraph()
@@ -110,6 +110,7 @@ open class FirTypeResolveTransformer(
.transformReceiverTypeRef(this, data)
.transformGetter(this, data)
.transformSetter(this, data)
.transformBackingField(this, data)
.transformAnnotations(this, data)
if (property.isFromVararg == true) {
property.transformTypeToArrayType()
@@ -266,6 +266,13 @@ open class FirBodyResolveTransformer(
return declarationsTransformer.transformProperty(property, data)
}
override fun transformBackingField(
backingField: FirBackingField,
data: ResolutionMode
): FirStatement {
return declarationsTransformer.transformBackingField(backingField, data)
}
override fun transformField(field: FirField, data: ResolutionMode): FirField {
return declarationsTransformer.transformField(field, data)
}
@@ -121,6 +121,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
property.transformStatus(this, property.resolveStatus().mode())
property.getter?.let { it.transformStatus(this, it.resolveStatus(containingProperty = property).mode()) }
property.setter?.let { it.transformStatus(this, it.resolveStatus(containingProperty = property).mode()) }
property.backingField?.let { it.transformStatus(this, it.resolveStatus(containingProperty = property).mode()) }
return transformLocalVariable(property)
}
@@ -137,9 +138,10 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
val initializerIsAlreadyResolved = bodyResolveState >= FirPropertyBodyResolveState.INITIALIZER_RESOLVED
context.withProperty(property) {
context.forPropertyInitializer {
property.transformBackingField(transformer, ResolutionMode.ContextDependent)
property.transformDelegate(transformer, ResolutionMode.ContextDependentDelegate)
if (!initializerIsAlreadyResolved) {
property.transformChildrenWithoutAccessors(returnTypeRef)
property.transformChildrenWithoutComponents(returnTypeRef)
property.replaceBodyResolveState(FirPropertyBodyResolveState.INITIALIZER_RESOLVED)
}
if (property.initializer != null) {
@@ -301,7 +303,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
return variable
}
private fun FirProperty.transformChildrenWithoutAccessors(returnTypeRef: FirTypeRef): FirProperty {
private fun FirProperty.transformChildrenWithoutComponents(returnTypeRef: FirTypeRef): FirProperty {
val data = withExpectedType(returnTypeRef)
return transformReturnTypeRef(transformer, data)
.transformInitializer(transformer, data)
@@ -817,6 +819,46 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
return this
}
override fun transformBackingField(
backingField: FirBackingField,
data: ResolutionMode,
): FirStatement {
if (backingField.returnTypeRef is FirErrorTypeRef) {
return backingField
}
backingField.transformInitializer(
transformer,
withExpectedType(backingField.returnTypeRef)
)
if (backingField.returnTypeRef !is FirImplicitTypeRef) {
return backingField
}
val resultType = backingField.initializer
?.unwrapSmartcastExpression()?.typeRef
?: return backingField.transformReturnTypeRef(
transformer,
withExpectedType(
buildErrorTypeRef {
diagnostic = ConeSimpleDiagnostic(
"Cannot infer variable type without an initializer",
DiagnosticKind.InferenceError,
)
},
)
)
val expectedType = resultType.toExpectedTypeRef()
return backingField.transformReturnTypeRef(
transformer,
withExpectedType(
expectedType.approximatedIfNeededOrSelf(
inferenceComponents.approximator,
backingField.visibilityForApproximation(),
inferenceComponents.session.typeContext,
)
)
)
}
private fun storeVariableReturnType(variable: FirVariable) {
val initializer = variable.initializer
if (variable.returnTypeRef is FirImplicitTypeRef) {
@@ -829,26 +871,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
else -> null
}
if (resultType != null) {
val expectedType = when (resultType) {
is FirImplicitTypeRef -> buildErrorTypeRef {
diagnostic = ConeSimpleDiagnostic("No result type for initializer", DiagnosticKind.InferenceError)
}
is FirErrorTypeRef -> buildErrorTypeRef {
diagnostic = resultType.diagnostic
resultType.source?.fakeElement(FirFakeSourceElementKind.ImplicitTypeRef)?.let {
source = it
}
}
else -> {
buildResolvedTypeRef {
type = resultType.coneType
annotations.addAll(resultType.annotations)
resultType.source?.fakeElement(FirFakeSourceElementKind.PropertyFromParameter)?.let {
source = it
}
}
}
}
val expectedType = resultType.toExpectedTypeRef()
variable.transformReturnTypeRef(
transformer,
withExpectedType(
@@ -876,6 +899,29 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
}
}
private fun FirTypeRef.toExpectedTypeRef(): FirResolvedTypeRef {
return when (this) {
is FirImplicitTypeRef -> buildErrorTypeRef {
diagnostic = ConeSimpleDiagnostic("No result type for initializer", DiagnosticKind.InferenceError)
}
is FirErrorTypeRef -> buildErrorTypeRef {
diagnostic = this@toExpectedTypeRef.diagnostic
this@toExpectedTypeRef.source?.fakeElement(FirFakeSourceElementKind.ImplicitTypeRef)?.let {
source = it
}
}
else -> {
buildResolvedTypeRef {
type = this@toExpectedTypeRef.coneType
annotations.addAll(this@toExpectedTypeRef.annotations)
this@toExpectedTypeRef.source?.fakeElement(FirFakeSourceElementKind.PropertyFromParameter)?.let {
source = it
}
}
}
}
}
private object ImplicitToErrorTypeTransformer : FirTransformer<Any?>() {
override fun <E : FirElement> transformElement(element: E, data: Any?): E {
return element
@@ -47,6 +47,7 @@ val FirProperty.hasBackingField: Boolean
get() {
if (isAbstract) return false
if (delegate != null) return false
if (backingField != null) return true
when (origin) {
FirDeclarationOrigin.SubstitutionOverride -> return false
FirDeclarationOrigin.IntersectionOverride -> return false
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBackingField
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
@@ -2405,6 +2406,30 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.PROPERTY_INITIALIZER_WITH_EXPLICIT_FIELD_DECLARATION) { firDiagnostic ->
PropertyInitializerWithExplicitFieldDeclarationImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER) { firDiagnostic ->
PropertyFieldDeclarationMissingInitializerImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.PROPERTY_MUST_HAVE_GETTER) { firDiagnostic ->
PropertyMustHaveGetterImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.PROPERTY_MUST_HAVE_SETTER) { firDiagnostic ->
PropertyMustHaveSetterImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS) { firDiagnostic ->
AbstractPropertyInPrimaryConstructorParametersImpl(
firDiagnostic as FirPsiDiagnostic,
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBackingField
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
@@ -1702,6 +1703,22 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = AccessorForDelegatedProperty::class
}
abstract class PropertyInitializerWithExplicitFieldDeclaration : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = PropertyInitializerWithExplicitFieldDeclaration::class
}
abstract class PropertyFieldDeclarationMissingInitializer : KtFirDiagnostic<KtBackingField>() {
override val diagnosticClass get() = PropertyFieldDeclarationMissingInitializer::class
}
abstract class PropertyMustHaveGetter : KtFirDiagnostic<KtProperty>() {
override val diagnosticClass get() = PropertyMustHaveGetter::class
}
abstract class PropertyMustHaveSetter : KtFirDiagnostic<KtProperty>() {
override val diagnosticClass get() = PropertyMustHaveSetter::class
}
abstract class AbstractPropertyInPrimaryConstructorParameters : KtFirDiagnostic<KtModifierListOwner>() {
override val diagnosticClass get() = AbstractPropertyInPrimaryConstructorParameters::class
}
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBackingField
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
@@ -2738,6 +2739,34 @@ internal class AccessorForDelegatedPropertyImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class PropertyInitializerWithExplicitFieldDeclarationImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.PropertyInitializerWithExplicitFieldDeclaration(), KtAbstractFirDiagnostic<KtExpression> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class PropertyFieldDeclarationMissingInitializerImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.PropertyFieldDeclarationMissingInitializer(), KtAbstractFirDiagnostic<KtBackingField> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class PropertyMustHaveGetterImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.PropertyMustHaveGetter(), KtAbstractFirDiagnostic<KtProperty> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class PropertyMustHaveSetterImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.PropertyMustHaveSetter(), KtAbstractFirDiagnostic<KtProperty> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class AbstractPropertyInPrimaryConstructorParametersImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,