From 3fba3b703e20d517e2a4c31ed0d87f07205c7802 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 17 Jan 2024 18:10:42 +0200 Subject: [PATCH] [FIR] Update call-sites which are interested in annotation arguments After the previous commit, enum entry access to the annotation may be presented by two different nodes ^KT-64975 --- .../analysis/checkers/FirAnnotationHelpers.kt | 3 +- .../expression/FirOptInUsageBaseChecker.kt | 10 +-- .../constant/FirToConstantValueTransformer.kt | 8 ++ .../kotlin/fir/java/FirJavaElementFinder.kt | 2 +- .../fir/declarations/FirAnnotationUtils.kt | 89 +++++-------------- .../FirRetentionAnnotationHelpers.kt | 12 ++- .../fir/declarations/deprecationUtils.kt | 5 +- .../fir/declarations/AnnotationTargetUtils.kt | 66 ++++++++++++++ 8 files changed, 111 insertions(+), 84 deletions(-) create mode 100644 compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/AnnotationTargetUtils.kt diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt index 19a617c06ab..32baa65af40 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirAnnotationHelpers.kt @@ -61,8 +61,7 @@ fun FirClassLikeSymbol<*>.getAllowedAnnotationTargets(session: FirSession): Set< val arguments = targetAnnotation.findArgumentByName(ParameterNames.targetAllowedTargets)?.unwrapAndFlattenArgument(flattenArrays = true).orEmpty() return arguments.mapNotNullTo(mutableSetOf()) { argument -> - val targetExpression = argument as? FirQualifiedAccessExpression - val calleeReference = targetExpression?.calleeReference + val calleeReference = argument.toReference(session) val targetName = calleeReference?.resolved?.name?.asString() //for java annotations mappings: if java annotation is found in sdk and no kotlin dependency there is provided diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt index a3fcd6fdc9c..80d6c2097ba 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.correspondingValueParameterFr import org.jetbrains.kotlin.fir.declarations.utils.isData import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName import org.jetbrains.kotlin.fir.expressions.FirConstExpression -import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.toReference import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirNamedReference import org.jetbrains.kotlin.fir.resolve.fullyExpandedType @@ -276,13 +276,9 @@ object FirOptInUsageBaseChecker { val experimental = getAnnotationByClassId(OptInNames.REQUIRES_OPT_IN_CLASS_ID, session) ?: return null - val levelArgument = experimental.findArgumentByName(LEVEL) as? FirQualifiedAccessExpression + val levelArgument = experimental.findArgumentByName(LEVEL) - val levelName = when (val calleeReference = levelArgument?.calleeReference) { - is FirErrorNamedReference -> null - is FirNamedReference -> calleeReference.name.asString() - else -> null - } + val levelName = levelArgument?.extractEnumValueArgumentInfo()?.enumEntryName?.asString() val severity = Experimentality.Severity.entries.firstOrNull { it.name == levelName } ?: Experimentality.DEFAULT_SEVERITY val message = (experimental.findArgumentByName(MESSAGE) as? FirConstExpression<*>)?.value as? String diff --git a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/constant/FirToConstantValueTransformer.kt b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/constant/FirToConstantValueTransformer.kt index 6367660a77b..e2393bcce89 100644 --- a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/constant/FirToConstantValueTransformer.kt +++ b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/constant/FirToConstantValueTransformer.kt @@ -199,6 +199,14 @@ internal object FirToConstantValueTransformer : FirDefaultVisitor { + val element = enumEntryDeserializedAccessExpression + return EnumValue(element.enumClassId, element.enumEntryName) + } + override fun visitFunctionCall( functionCall: FirFunctionCall, data: FirToConstantValueTransformerData diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt index d6b3f384664..ee90360c2f3 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaElementFinder.kt @@ -211,7 +211,7 @@ class FirJavaElementFinder( } private fun FirAnnotation.findTargets(): List = buildList { - forEachAnnotationTarget { add(it.identifier) } + forEachAnnotationTarget(session) { add(it.identifier) } } private fun buildFieldStubForConst(firProperty: FirProperty, classStub: PsiClassStubImpl) { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index 3910b10cb10..0a83c797ca8 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -5,14 +5,10 @@ package org.jetbrains.kotlin.fir.declarations -import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.references.FirErrorNamedReference -import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag @@ -43,17 +39,9 @@ fun FirAnnotation.toAnnotationClassIdSafe(session: FirSession): ClassId? = fun FirAnnotation.toAnnotationClassLikeSymbol(session: FirSession): FirClassLikeSymbol<*>? = toAnnotationLookupTag(session)?.toSymbol(session) -private fun FirAnnotation.toAnnotationClass(session: FirSession): FirRegularClass? = +fun FirAnnotation.toAnnotationClass(session: FirSession): FirRegularClass? = toAnnotationClassLikeSymbol(session)?.fir as? FirRegularClass -// TODO: this is temporary solution, we need something better -private val FirExpression.callableNameOfMetaAnnotationArgument: Name? - get() = - (this as? FirQualifiedAccessExpression)?.let { - val callableSymbol = it.calleeReference.toResolvedCallableSymbol() - callableSymbol?.callableId?.callableName - } - private val sourceName = Name.identifier("SOURCE") fun List.nonSourceAnnotations(session: FirSession): List = @@ -62,66 +50,13 @@ fun List.nonSourceAnnotations(session: FirSession): List meta.toAnnotationClassId(session) == StandardClassIds.Annotations.Retention && meta.findArgumentByName(StandardClassIds.Annotations.ParameterNames.retentionValue) - ?.callableNameOfMetaAnnotationArgument == sourceName + ?.extractEnumValueArgumentInfo()?.enumEntryName == sourceName } } fun FirAnnotationContainer.nonSourceAnnotations(session: FirSession): List = annotations.nonSourceAnnotations(session) -fun FirAnnotation.useSiteTargetsFromMetaAnnotation(session: FirSession): Set { - return toAnnotationClass(session) - ?.annotations - ?.find { it.toAnnotationClassIdSafe(session) == StandardClassIds.Annotations.Target } - ?.findUseSiteTargets() - ?: DEFAULT_USE_SITE_TARGETS -} - -private fun FirAnnotation.findUseSiteTargets(): Set = buildSet { - forEachAnnotationTarget { - USE_SITE_TARGET_NAME_MAP[it.identifier]?.let { addAll(it) } - } -} - -fun FirAnnotation.forEachAnnotationTarget(action: (Name) -> Unit) { - fun take(arg: FirExpression) { - if (arg !is FirQualifiedAccessExpression) return@take - val callableSymbol = arg.calleeReference.toResolvedCallableSymbol() ?: return@take - if (callableSymbol.containingClassLookupTag()?.classId == StandardClassIds.AnnotationTarget) { - action(callableSymbol.callableId.callableName) - } - } - - if (this is FirAnnotationCall) { - for (arg in argumentList.arguments) { - arg.unwrapAndFlattenArgument(flattenArrays = true).forEach(::take) - } - } else { - argumentMapping.mapping[StandardClassIds.Annotations.ParameterNames.targetAllowedTargets] - ?.unwrapAndFlattenArgument(flattenArrays = true) - ?.forEach(::take) - } -} - - -// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.USE_SITE_MAPPING] (it's in reverse) -private val USE_SITE_TARGET_NAME_MAP = mapOf( - "FIELD" to setOf(AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD), - "FILE" to setOf(AnnotationUseSiteTarget.FILE), - "PROPERTY" to setOf(AnnotationUseSiteTarget.PROPERTY), - "PROPERTY_GETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_GETTER), - "PROPERTY_SETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_SETTER), - "VALUE_PARAMETER" to setOf( - AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, - AnnotationUseSiteTarget.RECEIVER, - AnnotationUseSiteTarget.SETTER_PARAMETER, - ), -) - -// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget] (the second argument of each entry) -private val DEFAULT_USE_SITE_TARGETS: Set = - USE_SITE_TARGET_NAME_MAP.values.fold(setOf()) { a, b -> a + b } - setOf(AnnotationUseSiteTarget.FILE) - fun FirDeclaration.hasAnnotation(classId: ClassId, session: FirSession): Boolean { return annotations.hasAnnotation(classId, session) } @@ -238,3 +173,21 @@ fun hasLowPriorityAnnotation(annotations: List) = annotations.any val lookupTag = it.annotationTypeRef.coneTypeSafe()?.lookupTag ?: return@any false lookupTag.classId == LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_CLASS_ID } + +data class EnumValueArgumentInfo(val enumClassId: ClassId?, val enumEntryName: Name) + +fun FirExpression.extractEnumValueArgumentInfo(): EnumValueArgumentInfo? { + return when (this) { + is FirPropertyAccessExpression -> { + if (isResolved) { + val entrySymbol = calleeReference.toResolvedEnumEntrySymbol() ?: return null + EnumValueArgumentInfo(entrySymbol.callableId.classId!!, entrySymbol.callableId.callableName) + } else { + val enumEntryName = (calleeReference as? FirNamedReference)?.name ?: return null + EnumValueArgumentInfo(null, enumEntryName) + } + } + is FirEnumEntryDeserializedAccessExpression -> EnumValueArgumentInfo(enumClassId, enumEntryName) + else -> null + } +} diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirRetentionAnnotationHelpers.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirRetentionAnnotationHelpers.kt index d4c34b8b6c8..fe61f2bb4fd 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirRetentionAnnotationHelpers.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirRetentionAnnotationHelpers.kt @@ -7,10 +7,13 @@ package org.jetbrains.kotlin.fir.declarations import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.FirEnumEntryDeserializedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.references.toResolvedEnumEntrySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.StandardClassIds fun FirRegularClass.getRetention(session: FirSession): AnnotationRetention { @@ -22,14 +25,15 @@ fun FirRegularClassSymbol.getRetention(session: FirSession): AnnotationRetention } fun FirAnnotation.getRetention(): AnnotationRetention? { - val propertyAccess = findArgumentByName(StandardClassIds.Annotations.ParameterNames.retentionValue) as? FirQualifiedAccessExpression - val callableId = propertyAccess?.calleeReference?.toResolvedEnumEntrySymbol()?.callableId ?: return null + val (enumId, entryName) = findArgumentByName(StandardClassIds.Annotations.ParameterNames.retentionValue) + ?.extractEnumValueArgumentInfo() + ?: return null - if (callableId.classId != StandardClassIds.AnnotationRetention) { + if (enumId != StandardClassIds.AnnotationRetention) { return null } - return AnnotationRetention.entries.firstOrNull { it.name == callableId.callableName.asString() } + return AnnotationRetention.entries.firstOrNull { it.name == entryName.asString() } } fun FirDeclaration.getRetentionAnnotation(session: FirSession): FirAnnotation? { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt index 9d8f9f8d1ee..2fadd17cdfe 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt @@ -245,8 +245,9 @@ private fun FirAnnotation.getDeprecationLevel(): DeprecationLevelValue? { ?.unwrapArgument() ?: arguments.lastOrNull() } ?: return null - val targetExpression = argument as? FirQualifiedAccessExpression ?: return null - val targetName = (targetExpression.calleeReference as? FirNamedReference)?.name?.asString() ?: return null + + val targetName = argument.extractEnumValueArgumentInfo()?.enumEntryName?.asString() ?: return null + return DeprecationLevelValue.values().find { it.name == targetName } } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/AnnotationTargetUtils.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/AnnotationTargetUtils.kt new file mode 100644 index 00000000000..48f45dc3650 --- /dev/null +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/declarations/AnnotationTargetUtils.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2024 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.fir.declarations + +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.containingClassLookupTag +import org.jetbrains.kotlin.fir.expressions.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds + +fun FirAnnotation.useSiteTargetsFromMetaAnnotation(session: FirSession): Set { + return toAnnotationClass(session) + ?.annotations + ?.find { it.toAnnotationClassIdSafe(session) == StandardClassIds.Annotations.Target } + ?.findUseSiteTargets(session) + ?: DEFAULT_USE_SITE_TARGETS +} + +private fun FirAnnotation.findUseSiteTargets(session: FirSession): Set { + return buildSet { + forEachAnnotationTarget(session) { + USE_SITE_TARGET_NAME_MAP[it.identifier]?.let { addAll(it) } + } + } +} + +// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.USE_SITE_MAPPING] (it's in reverse) +private val USE_SITE_TARGET_NAME_MAP = mapOf( + "FIELD" to setOf(AnnotationUseSiteTarget.FIELD, AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD), + "FILE" to setOf(AnnotationUseSiteTarget.FILE), + "PROPERTY" to setOf(AnnotationUseSiteTarget.PROPERTY), + "PROPERTY_GETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_GETTER), + "PROPERTY_SETTER" to setOf(AnnotationUseSiteTarget.PROPERTY_SETTER), + "VALUE_PARAMETER" to setOf( + AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, + AnnotationUseSiteTarget.RECEIVER, + AnnotationUseSiteTarget.SETTER_PARAMETER, + ), +) + +// See [org.jetbrains.kotlin.descriptors.annotations.KotlinTarget] (the second argument of each entry) +private val DEFAULT_USE_SITE_TARGETS: Set = + USE_SITE_TARGET_NAME_MAP.values.fold(setOf()) { a, b -> a + b } - setOf(AnnotationUseSiteTarget.FILE) + +fun FirAnnotation.forEachAnnotationTarget(session: FirSession, action: (Name) -> Unit) { + fun take(arg: FirExpression) { + val callableSymbol = arg.toResolvedCallableSymbol(session) ?: return + if (callableSymbol.containingClassLookupTag()?.classId == StandardClassIds.AnnotationTarget) { + action(callableSymbol.callableId.callableName) + } + } + + if (this is FirAnnotationCall) { + for (arg in argumentList.arguments) { + arg.unwrapAndFlattenArgument(flattenArrays = true).forEach(::take) + } + } else { + argumentMapping.mapping[StandardClassIds.Annotations.ParameterNames.targetAllowedTargets] + ?.unwrapAndFlattenArgument(flattenArrays = true) + ?.forEach(::take) + } +}