[FE 1.0] Report errors on cycles in annotation parameter types
^KT-47932 ^KT-50753 Fixed
This commit is contained in:
committed by
teamcity
parent
139a800ff7
commit
e9ac24dc33
@@ -300,6 +300,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtAnnotatedExpression> ANNOTATIONS_ON_BLOCK_LEVEL_EXPRESSION_ON_THE_SAME_LINE = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<KtAnnotationEntry> ANNOTATION_USED_AS_ANNOTATION_ARGUMENT = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtExpression> ANNOTATION_ARGUMENT_IS_NON_CONST = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactoryForDeprecation0<KtParameter> CYCLE_IN_ANNOTATION_PARAMETER = DiagnosticFactoryForDeprecation0.create(LanguageFeature.ProhibitCyclesInAnnotations);
|
||||
|
||||
DiagnosticFactoryForDeprecation0<PsiElement> RESTRICTED_RETENTION_FOR_EXPRESSION_ANNOTATION =
|
||||
DiagnosticFactoryForDeprecation0.create(LanguageFeature.RestrictRetentionForExpressionAnnotations);
|
||||
|
||||
+1
@@ -1001,6 +1001,7 @@ public class DefaultErrorMessages {
|
||||
|
||||
MAP.put(ANNOTATION_USED_AS_ANNOTATION_ARGUMENT, "An annotation can't be used as the annotations argument");
|
||||
MAP.put(ANNOTATION_ARGUMENT_IS_NON_CONST, "An annotation argument must be a compile-time constant");
|
||||
MAP.put(CYCLE_IN_ANNOTATION_PARAMETER, "Type of this parameter is cyclic");
|
||||
|
||||
MAP.put(RESTRICTED_RETENTION_FOR_EXPRESSION_ANNOTATION,
|
||||
"Expression annotations with retention other than SOURCE are prohibited");
|
||||
|
||||
@@ -49,6 +49,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
EnumCompanionInEnumConstructorCallChecker,
|
||||
ContextualDeclarationChecker,
|
||||
ValueParameterUsageInDefaultArgumentChecker,
|
||||
CyclicAnnotationsChecker,
|
||||
)
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.CYCLE_IN_ANNOTATION_PARAMETER
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
|
||||
object CyclicAnnotationsChecker : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (
|
||||
declaration !is KtClass || !declaration.isAnnotation() ||
|
||||
descriptor !is ClassDescriptor || descriptor.kind != ClassKind.ANNOTATION_CLASS
|
||||
) return
|
||||
|
||||
val primaryConstructor = declaration.primaryConstructor ?: return
|
||||
val primaryConstructorDescriptor = descriptor.unsubstitutedPrimaryConstructor ?: return
|
||||
|
||||
val checker = Checker(descriptor)
|
||||
|
||||
for ((parameter, parameterDescriptor) in primaryConstructor.valueParameters.zip(primaryConstructorDescriptor.valueParameters)) {
|
||||
if (checker.parameterHasCycle(descriptor, parameterDescriptor)) {
|
||||
context.trace.report(CYCLE_IN_ANNOTATION_PARAMETER.on(context.languageVersionSettings, parameter))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Checker(val targetAnnotation: ClassDescriptor) {
|
||||
private val visitedAnnotationDescriptors = mutableSetOf(targetAnnotation)
|
||||
private val annotationDescriptorsWithCycle = mutableSetOf(targetAnnotation)
|
||||
|
||||
fun annotationHasCycle(annotationDescriptor: ClassDescriptor): Boolean {
|
||||
val constructorDescriptor = annotationDescriptor.unsubstitutedPrimaryConstructor ?: return false
|
||||
|
||||
for (parameterDescriptor in constructorDescriptor.valueParameters) {
|
||||
if (parameterHasCycle(annotationDescriptor, parameterDescriptor)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun parameterHasCycle(ownedAnnotation: ClassDescriptor, parameterDescriptor: ValueParameterDescriptor): Boolean {
|
||||
val returnType = parameterDescriptor.returnType?.unwrap() ?: return false
|
||||
return when {
|
||||
returnType.arguments.isNotEmpty() && !ReflectionTypes.isKClassType(returnType) -> {
|
||||
for (argument in returnType.arguments) {
|
||||
if (!argument.isStarProjection) {
|
||||
if (typeHasCycle(ownedAnnotation, argument.type.unwrap())) return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
else -> typeHasCycle(ownedAnnotation, returnType)
|
||||
}
|
||||
}
|
||||
|
||||
fun typeHasCycle(ownedAnnotation: ClassDescriptor, type: UnwrappedType): Boolean {
|
||||
val referencedAnnotationDescriptor = (type.constructor.declarationDescriptor as? ClassDescriptor)
|
||||
?.takeIf { it.kind == ClassKind.ANNOTATION_CLASS }
|
||||
?: return false
|
||||
if (!visitedAnnotationDescriptors.add(referencedAnnotationDescriptor)) {
|
||||
return (referencedAnnotationDescriptor in annotationDescriptorsWithCycle).also {
|
||||
if (it) {
|
||||
annotationDescriptorsWithCycle += ownedAnnotation
|
||||
}
|
||||
}
|
||||
}
|
||||
if (referencedAnnotationDescriptor == targetAnnotation) {
|
||||
annotationDescriptorsWithCycle += ownedAnnotation
|
||||
return true
|
||||
}
|
||||
return annotationHasCycle(referencedAnnotationDescriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user