FE: refactor AnnotationTypeQualifierResolver

Preparing for commonization with FIR
This commit is contained in:
pyos
2021-08-06 15:16:15 +02:00
committed by teamcityserver
parent ae2c8a3540
commit 29a240b261
2 changed files with 72 additions and 110 deletions
@@ -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)
@@ -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) {