FE: refactor AnnotationTypeQualifierResolver
Preparing for commonization with FIR
This commit is contained in:
+71
-107
@@ -17,141 +17,93 @@
|
|||||||
package org.jetbrains.kotlin.load.java
|
package org.jetbrains.kotlin.load.java
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||||
import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper
|
import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper
|
||||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
|
|
||||||
|
typealias TypeQualifierWithApplicability = Pair<AnnotationDescriptor, Set<AnnotationQualifierApplicabilityType>>
|
||||||
|
|
||||||
class AnnotationTypeQualifierResolver(storageManager: StorageManager, private val javaTypeEnhancementState: JavaTypeEnhancementState) {
|
class AnnotationTypeQualifierResolver(storageManager: StorageManager, private val javaTypeEnhancementState: JavaTypeEnhancementState) {
|
||||||
class TypeQualifierWithApplicability(
|
|
||||||
private val typeQualifier: AnnotationDescriptor,
|
|
||||||
private val applicability: Int
|
|
||||||
) {
|
|
||||||
operator fun component1() = typeQualifier
|
|
||||||
operator fun component2() = AnnotationQualifierApplicabilityType.values().filter(this::isApplicableTo)
|
|
||||||
|
|
||||||
private fun isApplicableTo(elementType: AnnotationQualifierApplicabilityType): Boolean {
|
|
||||||
if (isApplicableConsideringMask(elementType)) return true
|
|
||||||
|
|
||||||
// We explicitly state that while JSR-305 TYPE_USE annotations effectively should be applied to every type
|
|
||||||
// they are not applicable for type parameter bounds because it would be a breaking change otherwise.
|
|
||||||
// Only defaulting annotations from jspecify are applicable
|
|
||||||
return isApplicableConsideringMask(AnnotationQualifierApplicabilityType.TYPE_USE) &&
|
|
||||||
elementType != AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isApplicableConsideringMask(elementType: AnnotationQualifierApplicabilityType) =
|
|
||||||
(applicability and (1 shl elementType.ordinal)) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private val resolvedNicknames =
|
private val resolvedNicknames =
|
||||||
storageManager.createMemoizedFunctionWithNullableValues(this::computeTypeQualifierNickname)
|
storageManager.createMemoizedFunctionWithNullableValues { klass: ClassDescriptor ->
|
||||||
|
if (klass.annotations.hasAnnotation(TYPE_QUALIFIER_NICKNAME_FQNAME))
|
||||||
private fun computeTypeQualifierNickname(classDescriptor: ClassDescriptor): AnnotationDescriptor? {
|
klass.annotations.firstNotNullOfOrNull(this::resolveTypeQualifierAnnotation)
|
||||||
if (!classDescriptor.annotations.hasAnnotation(TYPE_QUALIFIER_NICKNAME_FQNAME)) return null
|
else
|
||||||
|
null
|
||||||
return classDescriptor.annotations.firstNotNullOfOrNull(this::resolveTypeQualifierAnnotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveTypeQualifierNickname(classDescriptor: ClassDescriptor): AnnotationDescriptor? {
|
|
||||||
if (classDescriptor.kind != ClassKind.ANNOTATION_CLASS) return null
|
|
||||||
|
|
||||||
return resolvedNicknames(classDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resolveTypeQualifierAnnotation(annotationDescriptor: AnnotationDescriptor): AnnotationDescriptor? {
|
|
||||||
if (javaTypeEnhancementState.jsr305.isDisabled) {
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val annotationClass = annotationDescriptor.annotationClass ?: return null
|
fun resolveTypeQualifierAnnotation(annotation: AnnotationDescriptor): AnnotationDescriptor? {
|
||||||
if (annotationClass.isAnnotatedWithTypeQualifier) return annotationDescriptor
|
if (javaTypeEnhancementState.jsr305.isDisabled) return null
|
||||||
|
val annotationClass = annotation.annotationClass ?: return null
|
||||||
return resolveTypeQualifierNickname(annotationClass)
|
if (annotation.fqName in BUILT_IN_TYPE_QUALIFIER_FQ_NAMES || annotationClass.annotations.hasAnnotation(TYPE_QUALIFIER_FQNAME))
|
||||||
|
return annotation
|
||||||
|
return resolvedNicknames(annotationClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveQualifierBuiltInDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): JavaDefaultQualifiers? {
|
fun resolveQualifierBuiltInDefaultAnnotation(annotation: AnnotationDescriptor): JavaDefaultQualifiers? {
|
||||||
if (javaTypeEnhancementState.disabledDefaultAnnotations) {
|
if (javaTypeEnhancementState.disabledDefaultAnnotations) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS[annotationDescriptor.fqName]?.let { qualifierForDefaultingAnnotation ->
|
return BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS[annotation.fqName]?.let { qualifierForDefaultingAnnotation ->
|
||||||
val state = resolveDefaultAnnotationState(annotationDescriptor).takeIf { it != ReportLevel.IGNORE } ?: return null
|
val state = resolveDefaultAnnotationState(annotation).takeIf { it != ReportLevel.IGNORE } ?: return null
|
||||||
qualifierForDefaultingAnnotation.copy(
|
qualifierForDefaultingAnnotation.copy(
|
||||||
nullabilityQualifier = qualifierForDefaultingAnnotation.nullabilityQualifier.copy(isForWarningOnly = state.isWarning)
|
nullabilityQualifier = qualifierForDefaultingAnnotation.nullabilityQualifier.copy(isForWarningOnly = state.isWarning)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveDefaultAnnotationState(annotationDescriptor: AnnotationDescriptor): ReportLevel {
|
private fun resolveDefaultAnnotationState(annotation: AnnotationDescriptor): ReportLevel {
|
||||||
val annotationFqname = annotationDescriptor.fqName
|
val annotationFqname = annotation.fqName
|
||||||
if (annotationFqname != null && annotationFqname in JSPECIFY_DEFAULT_ANNOTATIONS) {
|
if (annotationFqname != null && annotationFqname in JSPECIFY_DEFAULT_ANNOTATIONS) {
|
||||||
return javaTypeEnhancementState.getReportLevelForAnnotation(annotationFqname)
|
return javaTypeEnhancementState.getReportLevelForAnnotation(annotationFqname)
|
||||||
}
|
}
|
||||||
|
return resolveJsr305AnnotationState(annotation)
|
||||||
return resolveJsr305AnnotationState(annotationDescriptor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
|
// We explicitly state that while JSR-305 TYPE_USE annotations effectively should be applied to every type.
|
||||||
if (javaTypeEnhancementState.jsr305.isDisabled) {
|
// They are not applicable for type parameter bounds because it would be a breaking change otherwise.
|
||||||
return null
|
private fun Set<AnnotationQualifierApplicabilityType>.allIfTypeUse(): Set<AnnotationQualifierApplicabilityType> =
|
||||||
}
|
if (AnnotationQualifierApplicabilityType.TYPE_USE in this)
|
||||||
|
AnnotationQualifierApplicabilityType.values().toSet() - AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS + this
|
||||||
|
else
|
||||||
|
this
|
||||||
|
|
||||||
val typeQualifierDefaultAnnotatedClass =
|
fun resolveTypeQualifierDefaultAnnotation(annotation: AnnotationDescriptor): TypeQualifierWithApplicability? {
|
||||||
annotationDescriptor.annotationClass?.takeIf { it.annotations.hasAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) }
|
if (javaTypeEnhancementState.jsr305.isDisabled) return null
|
||||||
?: return null
|
val annotationClass = annotation.annotationClass ?: return null
|
||||||
|
val typeQualifierDefault = annotationClass.annotations.findAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) ?: return null
|
||||||
val elementTypesMask =
|
val typeQualifier = annotationClass.annotations.firstOrNull { resolveTypeQualifierAnnotation(it) != null } ?: return null
|
||||||
annotationDescriptor.annotationClass!!
|
val applicability = typeQualifierDefault.enumArguments(onlyValue = true)
|
||||||
.annotations.findAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME)!!
|
.mapNotNullTo(mutableSetOf()) { JAVA_APPLICABILITY_TYPES[it] }
|
||||||
.allValueArguments
|
return TypeQualifierWithApplicability(typeQualifier, applicability.allIfTypeUse())
|
||||||
.flatMap { (parameter, argument) ->
|
|
||||||
if (parameter == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME)
|
|
||||||
argument.mapJavaConstantToQualifierApplicabilityTypes()
|
|
||||||
else
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
.fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) }
|
|
||||||
|
|
||||||
val typeQualifier = typeQualifierDefaultAnnotatedClass.annotations.firstOrNull { resolveTypeQualifierAnnotation(it) != null }
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
return TypeQualifierWithApplicability(typeQualifier, elementTypesMask)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? {
|
fun isTypeUseAnnotation(annotation: AnnotationDescriptor): Boolean {
|
||||||
val annotatedClass = annotationDescriptor.annotationClass ?: return null
|
val annotatedClass = annotation.annotationClass ?: return false
|
||||||
val target = annotatedClass.annotations.findAnnotation(JvmAnnotationNames.TARGET_ANNOTATION) ?: return null
|
val target = annotatedClass.annotations.findAnnotation(JvmAnnotationNames.TARGET_ANNOTATION) ?: return false
|
||||||
val elementTypesMask = target.allValueArguments
|
return target.enumArguments(onlyValue = false).any { it == KotlinTarget.TYPE.name }
|
||||||
.flatMap { (_, argument) -> argument.mapKotlinConstantToQualifierApplicabilityTypes() }
|
|
||||||
.fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) }
|
|
||||||
|
|
||||||
return TypeQualifierWithApplicability(annotationDescriptor, elementTypesMask)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveJsr305AnnotationState(annotationDescriptor: AnnotationDescriptor): ReportLevel {
|
fun resolveJsr305AnnotationState(annotation: AnnotationDescriptor): ReportLevel {
|
||||||
resolveJsr305CustomState(annotationDescriptor)?.let { return it }
|
resolveJsr305CustomState(annotation)?.let { return it }
|
||||||
return javaTypeEnhancementState.jsr305.globalLevel
|
return javaTypeEnhancementState.jsr305.globalLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveJsr305CustomState(annotationDescriptor: AnnotationDescriptor): ReportLevel? {
|
fun resolveJsr305CustomState(annotation: AnnotationDescriptor): ReportLevel? {
|
||||||
javaTypeEnhancementState.jsr305.userDefinedLevelForSpecificAnnotation[annotationDescriptor.fqName]?.let { return it }
|
javaTypeEnhancementState.jsr305.userDefinedLevelForSpecificAnnotation[annotation.fqName]?.let { return it }
|
||||||
return annotationDescriptor.annotationClass?.migrationAnnotationStatus()
|
return annotation.annotationClass?.migrationAnnotationStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ClassDescriptor.migrationAnnotationStatus(): ReportLevel? {
|
private fun ClassDescriptor.migrationAnnotationStatus(): ReportLevel? {
|
||||||
val enumValue = annotations.findAnnotation(MIGRATION_ANNOTATION_FQNAME)?.firstArgument() as? EnumValue
|
val enumValue = annotations.findAnnotation(MIGRATION_ANNOTATION_FQNAME)?.enumArguments(onlyValue = false)?.firstOrNull()
|
||||||
?: return null
|
?: return null
|
||||||
|
return javaTypeEnhancementState.jsr305.migrationLevel ?: when (enumValue) {
|
||||||
javaTypeEnhancementState.jsr305.migrationLevel?.let { return it }
|
|
||||||
|
|
||||||
return when (enumValue.enumEntryName.asString()) {
|
|
||||||
"STRICT" -> ReportLevel.STRICT
|
"STRICT" -> ReportLevel.STRICT
|
||||||
"WARN" -> ReportLevel.WARN
|
"WARN" -> ReportLevel.WARN
|
||||||
"IGNORE" -> ReportLevel.IGNORE
|
"IGNORE" -> ReportLevel.IGNORE
|
||||||
@@ -159,23 +111,35 @@ class AnnotationTypeQualifierResolver(storageManager: StorageManager, private va
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.toKotlinTargetNames() = JavaAnnotationTargetMapper.mapJavaTargetArgumentByName(this).map { it.name }
|
private fun AnnotationDescriptor.enumArguments(onlyValue: Boolean): Iterable<String> =
|
||||||
|
allValueArguments.flatMap { (parameter, argument) ->
|
||||||
|
if (!onlyValue || parameter == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME)
|
||||||
|
argument.toEnumNames()
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
private fun ConstantValue<*>.mapConstantToQualifierApplicabilityTypes(
|
private fun ConstantValue<*>.toEnumNames(): List<String> =
|
||||||
findPredicate: EnumValue.(AnnotationQualifierApplicabilityType) -> Boolean
|
|
||||||
): List<AnnotationQualifierApplicabilityType> =
|
|
||||||
when (this) {
|
when (this) {
|
||||||
is ArrayValue -> value.flatMap { it.mapConstantToQualifierApplicabilityTypes(findPredicate) }
|
is ArrayValue -> value.flatMap { it.toEnumNames() }
|
||||||
is EnumValue -> listOfNotNull(AnnotationQualifierApplicabilityType.values().find { findPredicate(it) })
|
is EnumValue -> listOf(enumEntryName.identifier)
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ConstantValue<*>.mapJavaConstantToQualifierApplicabilityTypes(): List<AnnotationQualifierApplicabilityType> =
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
mapConstantToQualifierApplicabilityTypes { enumEntryName.identifier == it.javaTarget }
|
private companion object {
|
||||||
|
val JAVA_APPLICABILITY_TYPES = buildMap<String, AnnotationQualifierApplicabilityType> {
|
||||||
|
for (type in AnnotationQualifierApplicabilityType.values()) {
|
||||||
|
getOrPut(type.javaTarget) { type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun ConstantValue<*>.mapKotlinConstantToQualifierApplicabilityTypes(): List<AnnotationQualifierApplicabilityType> =
|
val KOTLIN_APPLICABILITY_TYPES = buildMap<String, AnnotationQualifierApplicabilityType> {
|
||||||
mapConstantToQualifierApplicabilityTypes { enumEntryName.identifier in it.javaTarget.toKotlinTargetNames() }
|
for (type in AnnotationQualifierApplicabilityType.values()) {
|
||||||
|
for (target in JavaAnnotationTargetMapper.mapJavaTargetArgumentByName(type.javaTarget)) {
|
||||||
|
getOrPut(target.name) { type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ClassDescriptor.isAnnotatedWithTypeQualifier: Boolean
|
|
||||||
get() = fqNameSafe in BUILT_IN_TYPE_QUALIFIER_FQ_NAMES || annotations.hasAnnotation(TYPE_QUALIFIER_FQNAME)
|
|
||||||
|
|||||||
+1
-3
@@ -338,8 +338,6 @@ class SignatureEnhancement(
|
|||||||
val composedAnnotation =
|
val composedAnnotation =
|
||||||
if (isHeadTypeConstructor && typeContainer != null && typeContainer !is TypeParameterDescriptor && areImprovementsInStrictMode) {
|
if (isHeadTypeConstructor && typeContainer != null && typeContainer !is TypeParameterDescriptor && areImprovementsInStrictMode) {
|
||||||
val filteredContainerAnnotations = typeContainer.annotations.filter {
|
val filteredContainerAnnotations = typeContainer.annotations.filter {
|
||||||
val (_, targets) = annotationTypeQualifierResolver.resolveAnnotation(it)
|
|
||||||
?: return@filter true // don't exclude annotations without specified target or unresolved
|
|
||||||
/*
|
/*
|
||||||
* We don't apply container type use annotations to avoid double applying them like with arrays:
|
* We don't apply container type use annotations to avoid double applying them like with arrays:
|
||||||
* @NotNull Integer [] f15();
|
* @NotNull Integer [] f15();
|
||||||
@@ -348,7 +346,7 @@ class SignatureEnhancement(
|
|||||||
* In other words, we prefer TYPE_USE target of an annotation, and apply the annotation only according to it, if it's present.
|
* In other words, we prefer TYPE_USE target of an annotation, and apply the annotation only according to it, if it's present.
|
||||||
* See KT-24392 for more details.
|
* See KT-24392 for more details.
|
||||||
*/
|
*/
|
||||||
AnnotationQualifierApplicabilityType.TYPE_USE !in targets
|
!annotationTypeQualifierResolver.isTypeUseAnnotation(it)
|
||||||
}
|
}
|
||||||
composeAnnotations(Annotations.create(filteredContainerAnnotations), annotations)
|
composeAnnotations(Annotations.create(filteredContainerAnnotations), annotations)
|
||||||
} else if (isHeadTypeConstructor && typeContainer != null) {
|
} else if (isHeadTypeConstructor && typeContainer != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user