diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt index 0824fe03eb8..47f4e31092e 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/AnnotationTypeQualifierResolver.kt @@ -17,141 +17,93 @@ package org.jetbrains.kotlin.load.java 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.KotlinTarget import org.jetbrains.kotlin.load.java.components.JavaAnnotationTargetMapper import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.EnumValue 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 +typealias TypeQualifierWithApplicability = Pair> + 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 = - storageManager.createMemoizedFunctionWithNullableValues(this::computeTypeQualifierNickname) - - private fun computeTypeQualifierNickname(classDescriptor: ClassDescriptor): AnnotationDescriptor? { - if (!classDescriptor.annotations.hasAnnotation(TYPE_QUALIFIER_NICKNAME_FQNAME)) return 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 + storageManager.createMemoizedFunctionWithNullableValues { klass: ClassDescriptor -> + if (klass.annotations.hasAnnotation(TYPE_QUALIFIER_NICKNAME_FQNAME)) + klass.annotations.firstNotNullOfOrNull(this::resolveTypeQualifierAnnotation) + else + null } - val annotationClass = annotationDescriptor.annotationClass ?: return null - if (annotationClass.isAnnotatedWithTypeQualifier) return annotationDescriptor - - return resolveTypeQualifierNickname(annotationClass) + fun resolveTypeQualifierAnnotation(annotation: AnnotationDescriptor): AnnotationDescriptor? { + if (javaTypeEnhancementState.jsr305.isDisabled) return null + val annotationClass = annotation.annotationClass ?: return null + 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) { return null } - return BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS[annotationDescriptor.fqName]?.let { qualifierForDefaultingAnnotation -> - val state = resolveDefaultAnnotationState(annotationDescriptor).takeIf { it != ReportLevel.IGNORE } ?: return null + return BUILT_IN_TYPE_QUALIFIER_DEFAULT_ANNOTATIONS[annotation.fqName]?.let { qualifierForDefaultingAnnotation -> + val state = resolveDefaultAnnotationState(annotation).takeIf { it != ReportLevel.IGNORE } ?: return null qualifierForDefaultingAnnotation.copy( nullabilityQualifier = qualifierForDefaultingAnnotation.nullabilityQualifier.copy(isForWarningOnly = state.isWarning) ) } } - private fun resolveDefaultAnnotationState(annotationDescriptor: AnnotationDescriptor): ReportLevel { - val annotationFqname = annotationDescriptor.fqName + private fun resolveDefaultAnnotationState(annotation: AnnotationDescriptor): ReportLevel { + val annotationFqname = annotation.fqName if (annotationFqname != null && annotationFqname in JSPECIFY_DEFAULT_ANNOTATIONS) { return javaTypeEnhancementState.getReportLevelForAnnotation(annotationFqname) } - - return resolveJsr305AnnotationState(annotationDescriptor) + return resolveJsr305AnnotationState(annotation) } - fun resolveTypeQualifierDefaultAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? { - if (javaTypeEnhancementState.jsr305.isDisabled) { - return null - } + // 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. + private fun Set.allIfTypeUse(): Set = + if (AnnotationQualifierApplicabilityType.TYPE_USE in this) + AnnotationQualifierApplicabilityType.values().toSet() - AnnotationQualifierApplicabilityType.TYPE_PARAMETER_BOUNDS + this + else + this - val typeQualifierDefaultAnnotatedClass = - annotationDescriptor.annotationClass?.takeIf { it.annotations.hasAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) } - ?: return null - - val elementTypesMask = - annotationDescriptor.annotationClass!! - .annotations.findAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME)!! - .allValueArguments - .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 resolveTypeQualifierDefaultAnnotation(annotation: AnnotationDescriptor): TypeQualifierWithApplicability? { + if (javaTypeEnhancementState.jsr305.isDisabled) return null + val annotationClass = annotation.annotationClass ?: return null + val typeQualifierDefault = annotationClass.annotations.findAnnotation(TYPE_QUALIFIER_DEFAULT_FQNAME) ?: return null + val typeQualifier = annotationClass.annotations.firstOrNull { resolveTypeQualifierAnnotation(it) != null } ?: return null + val applicability = typeQualifierDefault.enumArguments(onlyValue = true) + .mapNotNullTo(mutableSetOf()) { JAVA_APPLICABILITY_TYPES[it] } + return TypeQualifierWithApplicability(typeQualifier, applicability.allIfTypeUse()) } - fun resolveAnnotation(annotationDescriptor: AnnotationDescriptor): TypeQualifierWithApplicability? { - val annotatedClass = annotationDescriptor.annotationClass ?: return null - val target = annotatedClass.annotations.findAnnotation(JvmAnnotationNames.TARGET_ANNOTATION) ?: return null - val elementTypesMask = target.allValueArguments - .flatMap { (_, argument) -> argument.mapKotlinConstantToQualifierApplicabilityTypes() } - .fold(0) { acc: Int, applicabilityType -> acc or (1 shl applicabilityType.ordinal) } - - return TypeQualifierWithApplicability(annotationDescriptor, elementTypesMask) + fun isTypeUseAnnotation(annotation: AnnotationDescriptor): Boolean { + val annotatedClass = annotation.annotationClass ?: return false + val target = annotatedClass.annotations.findAnnotation(JvmAnnotationNames.TARGET_ANNOTATION) ?: return false + return target.enumArguments(onlyValue = false).any { it == KotlinTarget.TYPE.name } } - fun resolveJsr305AnnotationState(annotationDescriptor: AnnotationDescriptor): ReportLevel { - resolveJsr305CustomState(annotationDescriptor)?.let { return it } + fun resolveJsr305AnnotationState(annotation: AnnotationDescriptor): ReportLevel { + resolveJsr305CustomState(annotation)?.let { return it } return javaTypeEnhancementState.jsr305.globalLevel } - fun resolveJsr305CustomState(annotationDescriptor: AnnotationDescriptor): ReportLevel? { - javaTypeEnhancementState.jsr305.userDefinedLevelForSpecificAnnotation[annotationDescriptor.fqName]?.let { return it } - return annotationDescriptor.annotationClass?.migrationAnnotationStatus() + fun resolveJsr305CustomState(annotation: AnnotationDescriptor): ReportLevel? { + javaTypeEnhancementState.jsr305.userDefinedLevelForSpecificAnnotation[annotation.fqName]?.let { return it } + return annotation.annotationClass?.migrationAnnotationStatus() } 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 - - javaTypeEnhancementState.jsr305.migrationLevel?.let { return it } - - return when (enumValue.enumEntryName.asString()) { + return javaTypeEnhancementState.jsr305.migrationLevel ?: when (enumValue) { "STRICT" -> ReportLevel.STRICT "WARN" -> ReportLevel.WARN "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 = + allValueArguments.flatMap { (parameter, argument) -> + if (!onlyValue || parameter == JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME) + argument.toEnumNames() + else + emptyList() + } - private fun ConstantValue<*>.mapConstantToQualifierApplicabilityTypes( - findPredicate: EnumValue.(AnnotationQualifierApplicabilityType) -> Boolean - ): List = + private fun ConstantValue<*>.toEnumNames(): List = when (this) { - is ArrayValue -> value.flatMap { it.mapConstantToQualifierApplicabilityTypes(findPredicate) } - is EnumValue -> listOfNotNull(AnnotationQualifierApplicabilityType.values().find { findPredicate(it) }) + is ArrayValue -> value.flatMap { it.toEnumNames() } + is EnumValue -> listOf(enumEntryName.identifier) else -> emptyList() } - private fun ConstantValue<*>.mapJavaConstantToQualifierApplicabilityTypes(): List = - mapConstantToQualifierApplicabilityTypes { enumEntryName.identifier == it.javaTarget } + @OptIn(ExperimentalStdlibApi::class) + private companion object { + val JAVA_APPLICABILITY_TYPES = buildMap { + for (type in AnnotationQualifierApplicabilityType.values()) { + getOrPut(type.javaTarget) { type } + } + } - private fun ConstantValue<*>.mapKotlinConstantToQualifierApplicabilityTypes(): List = - mapConstantToQualifierApplicabilityTypes { enumEntryName.identifier in it.javaTarget.toKotlinTargetNames() } + val KOTLIN_APPLICABILITY_TYPES = buildMap { + 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) diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt index 1670e714487..9f7d550949b 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/typeEnhancement/signatureEnhancement.kt @@ -338,8 +338,6 @@ class SignatureEnhancement( val composedAnnotation = if (isHeadTypeConstructor && typeContainer != null && typeContainer !is TypeParameterDescriptor && areImprovementsInStrictMode) { 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: * @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. * See KT-24392 for more details. */ - AnnotationQualifierApplicabilityType.TYPE_USE !in targets + !annotationTypeQualifierResolver.isTypeUseAnnotation(it) } composeAnnotations(Annotations.create(filteredContainerAnnotations), annotations) } else if (isHeadTypeConstructor && typeContainer != null) {