[FIR] Report errors on cycles in annotation parameter types
^KT-47932
This commit is contained in:
committed by
teamcity
parent
e9ac24dc33
commit
9f870b0549
+12
@@ -737,6 +737,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.CYCLE_IN_ANNOTATION_PARAMETER.errorFactory) { firDiagnostic ->
|
||||
CycleInAnnotationParameterErrorImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.CYCLE_IN_ANNOTATION_PARAMETER.warningFactory) { firDiagnostic ->
|
||||
CycleInAnnotationParameterWarningImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.ANNOTATION_CLASS_CONSTRUCTOR_CALL) { firDiagnostic ->
|
||||
AnnotationClassConstructorCallImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
|
||||
+8
@@ -538,6 +538,14 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = NonConstValUsedInConstantExpression::class
|
||||
}
|
||||
|
||||
abstract class CycleInAnnotationParameterError : KtFirDiagnostic<KtParameter>() {
|
||||
override val diagnosticClass get() = CycleInAnnotationParameterError::class
|
||||
}
|
||||
|
||||
abstract class CycleInAnnotationParameterWarning : KtFirDiagnostic<KtParameter>() {
|
||||
override val diagnosticClass get() = CycleInAnnotationParameterWarning::class
|
||||
}
|
||||
|
||||
abstract class AnnotationClassConstructorCall : KtFirDiagnostic<KtCallExpression>() {
|
||||
override val diagnosticClass get() = AnnotationClassConstructorCall::class
|
||||
}
|
||||
|
||||
+10
@@ -641,6 +641,16 @@ internal class NonConstValUsedInConstantExpressionImpl(
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.NonConstValUsedInConstantExpression(), KtAbstractFirDiagnostic<KtExpression>
|
||||
|
||||
internal class CycleInAnnotationParameterErrorImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.CycleInAnnotationParameterError(), KtAbstractFirDiagnostic<KtParameter>
|
||||
|
||||
internal class CycleInAnnotationParameterWarningImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.CycleInAnnotationParameterWarning(), KtAbstractFirDiagnostic<KtParameter>
|
||||
|
||||
internal class AnnotationClassConstructorCallImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
|
||||
+1
@@ -237,6 +237,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val LOCAL_ANNOTATION_CLASS_ERROR by error<KtClassOrObject>()
|
||||
val MISSING_VAL_ON_ANNOTATION_PARAMETER by error<KtParameter>()
|
||||
val NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION by error<KtExpression>()
|
||||
val CYCLE_IN_ANNOTATION_PARAMETER by deprecationError<KtParameter>(LanguageFeature.ProhibitCyclesInAnnotations)
|
||||
val ANNOTATION_CLASS_CONSTRUCTOR_CALL by error<KtCallExpression>()
|
||||
val NOT_AN_ANNOTATION_CLASS by error<PsiElement> {
|
||||
parameter<String>("annotationName")
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.config.LanguageFeature.ForbidExposingTypesInPrimaryC
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ForbidUsingExtensionPropertyTypeParameterInDelegate
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitAssigningSingleElementsToVarargsInNamedForm
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitConfusingSyntaxInWhenBranches
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitCyclesInAnnotations
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitInvisibleAbstractMethodsInSuperclasses
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitNonReifiedArraysAsReifiedTypeArguments
|
||||
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitUseSiteTargetAnnotationsOnSuperTypes
|
||||
@@ -222,6 +223,7 @@ object FirErrors {
|
||||
val LOCAL_ANNOTATION_CLASS_ERROR by error0<KtClassOrObject>()
|
||||
val MISSING_VAL_ON_ANNOTATION_PARAMETER by error0<KtParameter>()
|
||||
val NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION by error0<KtExpression>()
|
||||
val CYCLE_IN_ANNOTATION_PARAMETER by deprecationError0<KtParameter>(ProhibitCyclesInAnnotations)
|
||||
val ANNOTATION_CLASS_CONSTRUCTOR_CALL by error0<KtCallExpression>()
|
||||
val NOT_AN_ANNOTATION_CLASS by error1<PsiElement, String>()
|
||||
val NULLABLE_TYPE_OF_ANNOTATION_MEMBER by error0<KtTypeReference>()
|
||||
|
||||
+72
@@ -18,10 +18,14 @@ import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLE_IN_ANNOTATION_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
@@ -47,6 +51,8 @@ object FirAnnotationClassDeclarationChecker : FirRegularClassChecker() {
|
||||
val target = declaration.getRetentionAnnotation() ?: declaration.getTargetAnnotation() ?: declaration
|
||||
reporter.reportOn(target.source, FirErrors.RESTRICTED_RETENTION_FOR_EXPRESSION_ANNOTATION, context)
|
||||
}
|
||||
|
||||
checkCyclesInParameters(declaration.symbol, context, reporter)
|
||||
}
|
||||
|
||||
private fun checkAnnotationClassMember(member: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -160,4 +166,70 @@ object FirAnnotationClassDeclarationChecker : FirRegularClassChecker() {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun checkCyclesInParameters(annotation: FirRegularClassSymbol, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val primaryConstructor = annotation.primaryConstructorSymbol() ?: return
|
||||
val checker = CycleChecker(annotation, context.session)
|
||||
for (valueParameter in primaryConstructor.valueParameterSymbols) {
|
||||
if (checker.parameterHasCycle(annotation, valueParameter)) {
|
||||
reporter.reportOn(valueParameter.source, CYCLE_IN_ANNOTATION_PARAMETER, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CycleChecker(val targetAnnotation: FirRegularClassSymbol, val session: FirSession) {
|
||||
private val visitedAnnotations = mutableSetOf(targetAnnotation)
|
||||
private val annotationsWithCycle = mutableSetOf(targetAnnotation)
|
||||
|
||||
fun annotationHasCycle(annotation: FirRegularClassSymbol): Boolean {
|
||||
val primaryConstructor = annotation.primaryConstructorSymbol() ?: return false
|
||||
for (valueParameter in primaryConstructor.valueParameterSymbols) {
|
||||
if (parameterHasCycle(annotation, valueParameter)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun parameterHasCycle(ownedAnnotation: FirRegularClassSymbol, parameter: FirValueParameterSymbol): Boolean {
|
||||
val returnType = parameter.resolvedReturnTypeRef.coneType
|
||||
return when {
|
||||
returnType.typeArguments.isNotEmpty() -> {
|
||||
if (returnType.classId == StandardClassIds.KClass) return false
|
||||
for (argument in returnType.typeArguments) {
|
||||
if (typeHasCycle(ownedAnnotation, argument.type ?: continue)) return true
|
||||
}
|
||||
false
|
||||
}
|
||||
else -> typeHasCycle(ownedAnnotation, returnType)
|
||||
}
|
||||
}
|
||||
|
||||
fun typeHasCycle(ownedAnnotation: FirRegularClassSymbol, type: ConeKotlinType): Boolean {
|
||||
val referencedAnnotation = type.fullyExpandedType(session)
|
||||
.toRegularClassSymbol(session)
|
||||
?.takeIf { it.classKind == ANNOTATION_CLASS }
|
||||
?: return false
|
||||
if (!visitedAnnotations.add(referencedAnnotation)) {
|
||||
return (referencedAnnotation in annotationsWithCycle).also {
|
||||
if (it) {
|
||||
annotationsWithCycle += ownedAnnotation
|
||||
}
|
||||
}
|
||||
}
|
||||
if (referencedAnnotation == targetAnnotation) {
|
||||
annotationsWithCycle += ownedAnnotation
|
||||
return true
|
||||
}
|
||||
return annotationHasCycle(referencedAnnotation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun FirRegularClassSymbol.primaryConstructorSymbol(): FirConstructorSymbol? {
|
||||
for (declarationSymbol in this.declarationSymbols) {
|
||||
if (declarationSymbol is FirConstructorSymbol && declarationSymbol.isPrimary) {
|
||||
return declarationSymbol
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// WITH_REFLECT
|
||||
// LANGUAGE: +ProhibitCyclesInAnnotations
|
||||
// ISSUE: KT-47932
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
annotation class X(val value: X) // error
|
||||
annotation class Y(val value: Array<Y>) // error
|
||||
|
||||
annotation class Z1(val a: Z2, val b: Z2) // error
|
||||
annotation class Z2(val value: Z1) // error
|
||||
|
||||
annotation class A(val x: KClass<A>) // OK
|
||||
annotation class B(val x: KClass<B>) // OK
|
||||
annotation class C(val b: B) // OK
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_REFLECT
|
||||
// LANGUAGE: +ProhibitCyclesInAnnotations
|
||||
// ISSUE: KT-47932
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// WITH_REFLECT
|
||||
// LANGUAGE: -ProhibitCyclesInAnnotations
|
||||
// ISSUE: KT-47932
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
annotation class X(val value: X) // error
|
||||
annotation class Y(val value: Array<Y>) // error
|
||||
|
||||
annotation class Z1(val a: Z2, val b: Z2) // error
|
||||
annotation class Z2(val value: Z1) // error
|
||||
|
||||
annotation class A(val x: KClass<A>) // OK
|
||||
annotation class B(val x: KClass<B>) // OK
|
||||
annotation class C(val b: B) // OK
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_REFLECT
|
||||
// LANGUAGE: -ProhibitCyclesInAnnotations
|
||||
// ISSUE: KT-47932
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ annotation class C6(val irrelevant: Double, val value: Array<A6> = [])
|
||||
|
||||
// Should be an error after fixing KT-47932.
|
||||
@R(A7::class)
|
||||
annotation class A7(val value: Array<A7>)
|
||||
annotation class A7(<!CYCLE_IN_ANNOTATION_PARAMETER_WARNING!>val value: Array<A7><!>)
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ annotation class C6(val irrelevant: Double, val value: Array<A6> = [])
|
||||
|
||||
// Should be an error after fixing KT-47932.
|
||||
@R(A7::class)
|
||||
annotation class A7(val value: Array<A7>)
|
||||
annotation class A7(<!CYCLE_IN_ANNOTATION_PARAMETER_WARNING!>val value: Array<A7><!>)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user