diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt index 8bddba5fd35..e139998b7a1 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt @@ -220,11 +220,13 @@ object LowLevelFirApiFacadeForResolveOnAir { fileAnnotation = annotationEntry, replacement = replacement ) + val fileAnnotationsContainer = buildFileAnnotationsContainer { moduleData = firFile.moduleData containingFileSymbol = firFile.symbol annotations += annotationCall } + val llFirResolvableSession = firFile.llFirResolvableSession ?: buildErrorWithAttachment("FirFile session expected to be a resolvable session but was ${firFile.llFirSession::class.java}") { withEntry("firSession", firFile.llFirSession) { it.toString() } @@ -239,7 +241,8 @@ object LowLevelFirApiFacadeForResolveOnAir { collector?.addFileContext(firFile, firFile.createTowerDataContext(firResolveSession.getScopeSessionFor(llFirResolvableSession))) - return annotationCall + // We should return annotation from fileAnnotationsContainer because the original annotation can be replaced + return fileAnnotationsContainer.annotations.single() } private fun runBodyResolveOnAir( diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt index 8be5d3fa60b..ec48a44054a 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/lazy/resolve/FirLazyBodiesCalculator.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.fir.visitors.FirTransformer import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.name.StandardClassIds.Annotations.SinceKotlin import org.jetbrains.kotlin.fir.types.customAnnotations import org.jetbrains.kotlin.fir.types.forEachType @@ -57,6 +56,18 @@ internal object FirLazyBodiesCalculator { FirLazyAnnotationTransformerData(firElement.moduleData.session, FirLazyAnnotationTransformerScope.COMPILER_ONLY) ) } + + fun calculateLazyArgumentsForAnnotation(annotationCall: FirAnnotationCall, session: FirSession): FirArgumentList { + require(needCalculatingAnnotationCall(annotationCall)) + val builder = RawFirBuilder(session, baseScopeProvider = session.kotlinScopeProvider) + val ktAnnotationEntry = annotationCall.psi as KtAnnotationEntry + builder.context.packageFqName = ktAnnotationEntry.containingKtFile.packageFqName + val newAnnotationCall = builder.buildAnnotationCall(ktAnnotationEntry) + return newAnnotationCall.argumentList + } + + fun needCalculatingAnnotationCall(firAnnotationCall: FirAnnotationCall): Boolean = + firAnnotationCall.argumentList.arguments.any { it is FirLazyExpression } } private fun replaceValueParameterDefaultValues(valueParameters: List, newValueParameters: List) { @@ -68,15 +79,6 @@ private fun replaceValueParameterDefaultValues(valueParameters: List checkDeprecationProviderIsResolved(target, target.deprecationsProvider) + is FirCallableDeclaration -> checkDeprecationProviderIsResolved(target, target.deprecationsProvider) + } + checkNestedDeclarationsAreResolved(target) - // TODO add proper check that COMPILER_REQUIRED_ANNOTATIONS are resolved } } @@ -54,7 +67,7 @@ private class LLFirCompilerRequiredAnnotationsTargetResolver( session: FirSession, scopeSession: ScopeSession, computationSession: LLFirCompilerRequiredAnnotationsComputationSession? = null, -) : LLFirTargetResolver(target, lockProvider, FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS, isJumpingPhase = true) { +) : LLFirTargetResolver(target, lockProvider, FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS, isJumpingPhase = false) { inner class LLFirCompilerRequiredAnnotationsComputationSession : CompilerRequiredAnnotationsComputationSession() { override fun resolveAnnotationSymbol(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) { val regularClass = symbol.fir @@ -80,6 +93,10 @@ private class LLFirCompilerRequiredAnnotationsTargetResolver( computationSession ?: LLFirCompilerRequiredAnnotationsComputationSession(), ) + @OptIn(PrivateForInline::class) + private val llFirComputationSession: LLFirCompilerRequiredAnnotationsComputationSession + get() = transformer.annotationTransformer.computationSession as LLFirCompilerRequiredAnnotationsComputationSession + override fun withFile(firFile: FirFile, action: () -> Unit) { transformer.annotationTransformer.withFileAndFileScopes(firFile) { action() @@ -93,25 +110,211 @@ private class LLFirCompilerRequiredAnnotationsTargetResolver( } } - override fun doLazyResolveUnderLock(target: FirElementWithResolveState) { - FirLazyBodiesCalculator.calculateCompilerAnnotations(target) + override fun doResolveWithoutLock(target: FirElementWithResolveState): Boolean { + if (target !is FirRegularClass && !target.isRegularDeclarationWithAnnotation) { + throwUnexpectedFirElementError(target) + } - when { - target is FirRegularClass -> { - transformer.annotationTransformer.resolveRegularClass( - target, - transformChildren = {}, - afterChildrenTransform = { - transformer.annotationTransformer.calculateDeprecations(target) - } - ) + requireIsInstance(target) + resolveTargetDeclaration(target) + + return true + } + + private fun resolveTargetDeclaration(target: T) where T : FirAnnotationContainer, T : FirElementWithResolveState { + // 1. Check that we should process this target + if (llFirComputationSession.annotationsAreResolved(target, treatNonSourceDeclarationsAsResolved = false)) return + + // 2. Mark this target as resolved to avoid cycle + llFirComputationSession.recordThatAnnotationsAreResolved(target) + + // 3. Create annotation transformer for targets we want to transform (under read lock) + // Exit if another thread is already resolved this target + val annotationTransformer = target.createAnnotationTransformer() ?: return + + // 4. Exit if there are no applicable annotations, so we can just update the phase + if (annotationTransformer.isNothingToResolve()) { + return performCustomResolveUnderLock(target) { + // just update deprecations + annotationTransformer.publishResult(target) } + } - target.isRegularDeclarationWithAnnotation -> { - target.transformSingle(transformer.annotationTransformer, null) - } + // 5. Transform annotations in the air + annotationTransformer.transformAnnotations() - else -> throwUnexpectedFirElementError(target) + // 6. Move some annotations to the proper positions + annotationTransformer.balanceAnnotations(target) + + // 7. Calculate deprecations in the air + annotationTransformer.calculateDeprecations(target) + + // 8. Publish result + performCustomResolveUnderLock(target) { + annotationTransformer.publishResult(target) } } + + private fun T.createAnnotationTransformer(): AnnotationTransformer? where T : FirAnnotationContainer, T : FirElementWithResolveState { + if (!hasAnnotationsToResolve()) { + return (AnnotationTransformer(mutableMapOf())) + } + + val map = hashMapOf>() + var isUnresolved = false + withReadLock(this) { + isUnresolved = true + annotationsForTransformationTo(map) + } + + return map.takeIf { isUnresolved }?.let(::AnnotationTransformer) + } + + private inner class AnnotationTransformer( + private val annotationMap: MutableMap>, + ) { + private val deprecations: MutableMap = hashMapOf() + + fun isNothingToResolve(): Boolean = annotationMap.isEmpty() + + fun transformAnnotations() { + for (annotations in annotationMap.values) { + for (annotation in annotations) { + if (annotation !is FirAnnotationCall) continue + val typeRef = annotation.annotationTypeRef as? FirUserTypeRef ?: continue + if (!transformer.annotationTransformer.shouldRunAnnotationResolve(typeRef)) continue + transformer.annotationTransformer.transformAnnotationCall(annotation, typeRef) + } + } + } + + fun balanceAnnotations(target: FirElementWithResolveState) { + if (target !is FirProperty) return + val backingField = target.backingField ?: return + val updatedAnnotations = transformer.annotationTransformer.extractBackingFieldAnnotationsFromProperty( + target, + annotationMap[target].orEmpty(), + annotationMap[backingField].orEmpty(), + ) ?: return + + annotationMap[target] = updatedAnnotations.propertyAnnotations + annotationMap[backingField] = updatedAnnotations.backingFieldAnnotations + } + + fun calculateDeprecations(target: FirElementWithResolveState) { + val session = target.llFirSession + val cacheFactory = session.firCachesFactory + if (target is FirProperty) { + deprecations[target] = target.extractDeprecationInfoPerUseSite( + session = session, + customAnnotations = annotationMap[target].orEmpty(), + getterAnnotations = target.getter?.let(annotationMap::get).orEmpty(), + setterAnnotations = target.setter?.let(annotationMap::get).orEmpty(), + ).toDeprecationsProvider(cacheFactory) + } + + for ((declaration, annotations) in annotationMap) { + if (declaration is FirProperty || declaration is FirFileAnnotationsContainer) continue + + requireIsInstance(declaration) + deprecations[declaration] = declaration.extractDeprecationInfoPerUseSite( + session = session, + customAnnotations = annotations, + ).toDeprecationsProvider(cacheFactory) + } + } + + fun publishResult(target: T) where T : FirElementWithResolveState, T : FirAnnotationContainer { + val newAnnotations = annotationMap[target] + if (newAnnotations != null) { + target.replaceAnnotations(newAnnotations) + } + + val deprecationProvider = deprecations[target] ?: EmptyDeprecationsProvider + when (target) { + is FirClassLikeDeclaration -> target.replaceDeprecationsProvider(deprecationProvider) + is FirCallableDeclaration -> target.replaceDeprecationsProvider(deprecationProvider) + } + + when (target) { + is FirFunction -> target.valueParameters.forEach(::publishResult) + is FirProperty -> { + target.getter?.let(::publishResult) + target.setter?.let(::publishResult) + target.backingField?.let(::publishResult) + } + } + } + } + + /** + * @return true if at least one applicable annotation is present + */ + private fun T.annotationsForTransformationTo( + map: MutableMap>, + ) where T : FirAnnotationContainer, T : FirElementWithResolveState { + when (this) { + is FirFunction -> { + valueParameters.forEach { + it.annotationsForTransformationTo(map) + } + } + + is FirProperty -> { + getter?.annotationsForTransformationTo(map) + setter?.annotationsForTransformationTo(map) + backingField?.annotationsForTransformationTo(map) + } + } + + if (annotations.isEmpty()) return + + var hasApplicableAnnotation = false + val containerForAnnotations = ArrayList(annotations.size) + for (annotation in annotations) { + val userTypeRef = (annotation as? FirAnnotationCall)?.annotationTypeRef as? FirUserTypeRef + containerForAnnotations += if (userTypeRef != null && transformer.annotationTransformer.shouldRunAnnotationResolve(userTypeRef)) { + hasApplicableAnnotation = true + buildAnnotationCallCopy(annotation) { + /** + * CRA transformer won't modify this type reference, + * but we create a deep copy + * to be sure that no one another thread can't modify this reference during another phase, + * while we do deep copy during + * [org.jetbrains.kotlin.fir.resolve.transformers.plugin.AbstractFirSpecificAnnotationResolveTransformer.transformAnnotationCall] + * without lock + */ + annotationTypeRef = transformer.annotationTransformer.createDeepCopyOfTypeRef(userTypeRef) + + // We assume that non-empty argument must be a lazy expression, or it is a copied declaration inside an on-air session + if (FirLazyBodiesCalculator.needCalculatingAnnotationCall(annotation)) { + argumentList = FirLazyBodiesCalculator.calculateLazyArgumentsForAnnotation(annotation, llFirSession) + } + } + } else { + annotation + } + } + + if (hasApplicableAnnotation) { + map[this] = containerForAnnotations + } + } + + override fun doLazyResolveUnderLock(target: FirElementWithResolveState) { + throwUnexpectedFirElementError(target) + } } + +private fun FirAnnotationContainer.hasAnnotationsToResolve(): Boolean { + if (annotations.isNotEmpty()) return true + + return when (this) { + is FirFunction -> valueParameters.any(FirAnnotationContainer::hasAnnotationsToResolve) + is FirProperty -> this.getter?.hasAnnotationsToResolve() == true || + this.setter?.hasAnnotationsToResolve() == true || + this.backingField?.hasAnnotationsToResolve() == true + + else -> false + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/firCheckResolvedUtils.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/firCheckResolvedUtils.kt index daef8f5e003..6748d9937bb 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/firCheckResolvedUtils.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/firCheckResolvedUtils.kt @@ -105,6 +105,15 @@ internal fun checkDefaultValueIsResolved(parameter: FirValueParameter) { } } +internal fun checkDeprecationProviderIsResolved(declaration: FirDeclaration, provider: DeprecationsProvider) { + checkWithAttachmentBuilder( + condition = provider !is UnresolvedDeprecationProvider, + message = { "Unresolved deprecation provider found for ${declaration::class.simpleName}" } + ) { + withFirEntry("declaration", declaration) + } +} + internal fun checkReturnTypeRefIsResolved(declaration: FirCallableDeclaration, acceptImplicitTypeRef: Boolean = false) { checkTypeRefIsResolved(declaration.returnTypeRef, typeRefName = "return type", declaration, acceptImplicitTypeRef) } diff --git a/analysis/low-level-api-fir/testdata/lazyResolve/fileAnnotations.txt b/analysis/low-level-api-fir/testdata/lazyResolve/fileAnnotations.txt index 1a3de4126a5..3df6df8c088 100644 --- a/analysis/low-level-api-fir/testdata/lazyResolve/fileAnnotations.txt +++ b/analysis/low-level-api-fir/testdata/lazyResolve/fileAnnotations.txt @@ -30,7 +30,7 @@ FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt COMPILER_REQUIRED_ANNOTATIONS: FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt - @FILE:Deprecated[Unresolved](String()) + @FILE:R|kotlin/Deprecated|[CompilerRequiredAnnotations](String()) @FILE:Anno[Unresolved](LAZY_EXPRESSION) [ResolvedTo(COMPILER_REQUIRED_ANNOTATIONS)] annotations container @Target[Unresolved](LAZY_EXPRESSION) public? final? [ResolvedTo(RAW_FIR)] annotation class Anno : R|kotlin/Annotation| { @@ -45,7 +45,7 @@ FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt COMPANION_GENERATION: FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt - @FILE:Deprecated[Unresolved](String()) + @FILE:R|kotlin/Deprecated|[CompilerRequiredAnnotations](String()) @FILE:Anno[Unresolved](LAZY_EXPRESSION) [ResolvedTo(COMPANION_GENERATION)] annotations container @Target[Unresolved](LAZY_EXPRESSION) public? final? [ResolvedTo(RAW_FIR)] annotation class Anno : R|kotlin/Annotation| { @@ -60,7 +60,7 @@ FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt SUPER_TYPES: FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt - @FILE:Deprecated[Unresolved](String()) + @FILE:R|kotlin/Deprecated|[CompilerRequiredAnnotations](String()) @FILE:Anno[Unresolved](LAZY_EXPRESSION) [ResolvedTo(SUPER_TYPES)] annotations container @Target[Unresolved](LAZY_EXPRESSION) public? final? [ResolvedTo(RAW_FIR)] annotation class Anno : R|kotlin/Annotation| { 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 f211d6da00f..65095b1358c 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 @@ -1,5 +1,5 @@ /* - * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2023 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. */ @@ -95,13 +95,25 @@ fun FirAnnotationContainer.getDeprecationsProvider(session: FirSession): Depreca return extractDeprecationInfoPerUseSite(session).toDeprecationsProvider(session.firCachesFactory) } -fun FirAnnotationContainer.extractDeprecationInfoPerUseSite(session: FirSession): DeprecationAnnotationInfoPerUseSiteStorage { +fun FirAnnotationContainer.extractDeprecationInfoPerUseSite( + session: FirSession, + customAnnotations: List? = annotations, + getterAnnotations: List? = null, + setterAnnotations: List? = null, +): DeprecationAnnotationInfoPerUseSiteStorage { val fromJava = this is FirDeclaration && this.isJavaOrEnhancement - return buildDeprecationAnnotationInfoPerUseSiteStorage { - add(annotations.extractDeprecationAnnotationInfoPerUseSite(session, fromJava)) + add((customAnnotations ?: annotations).extractDeprecationAnnotationInfoPerUseSite(session, fromJava)) if (this@extractDeprecationInfoPerUseSite is FirProperty) { - add(getDeprecationsAnnotationInfoByUseSiteFromAccessors(session, getter, setter)) + add( + getDeprecationsAnnotationInfoByUseSiteFromAccessors( + session = session, + getter = getter, + getterAnnotations = getterAnnotations, + setter = setter, + setterAnnotations = setterAnnotations, + ) + ) } } } @@ -110,31 +122,34 @@ fun getDeprecationsProviderFromAccessors( session: FirSession, getter: FirFunction?, setter: FirFunction? -): DeprecationsProvider { - return getDeprecationsAnnotationInfoByUseSiteFromAccessors(session, getter, setter).toDeprecationsProvider(session.firCachesFactory) -} +): DeprecationsProvider = getDeprecationsAnnotationInfoByUseSiteFromAccessors( + session = session, + getter = getter, + setter = setter, +).toDeprecationsProvider(session.firCachesFactory) fun getDeprecationsAnnotationInfoByUseSiteFromAccessors( session: FirSession, getter: FirFunction?, - setter: FirFunction? -): DeprecationAnnotationInfoPerUseSiteStorage { - return buildDeprecationAnnotationInfoPerUseSiteStorage { - val setterDeprecations = setter?.extractDeprecationInfoPerUseSite(session) - setterDeprecations?.storage?.forEach { (useSite, infos) -> - if (useSite == null) { - add(AnnotationUseSiteTarget.PROPERTY_SETTER, infos) - } else { - add(useSite, infos) - } + getterAnnotations: List? = getter?.annotations, + setter: FirFunction?, + setterAnnotations: List? = setter?.annotations, +): DeprecationAnnotationInfoPerUseSiteStorage = buildDeprecationAnnotationInfoPerUseSiteStorage { + val setterDeprecations = setter?.extractDeprecationInfoPerUseSite(session, customAnnotations = setterAnnotations) + setterDeprecations?.storage?.forEach { (useSite, infos) -> + if (useSite == null) { + add(AnnotationUseSiteTarget.PROPERTY_SETTER, infos) + } else { + add(useSite, infos) } - val getterDeprecations = getter?.extractDeprecationInfoPerUseSite(session) - getterDeprecations?.storage?.forEach { (useSite, infos) -> - if (useSite == null) { - add(AnnotationUseSiteTarget.PROPERTY_GETTER, infos) - } else { - add(useSite, infos) - } + } + + val getterDeprecations = getter?.extractDeprecationInfoPerUseSite(session, customAnnotations = getterAnnotations) + getterDeprecations?.storage?.forEach { (useSite, infos) -> + if (useSite == null) { + add(AnnotationUseSiteTarget.PROPERTY_GETTER, infos) + } else { + add(useSite, infos) } } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt index 05dc8c99564..4fd7a91778f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt @@ -226,27 +226,28 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer( override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: Nothing?): FirStatement { val annotationTypeRef = annotationCall.annotationTypeRef if (annotationTypeRef !is FirUserTypeRef) return annotationCall - val name = annotationTypeRef.qualifier.last().name - - if (!shouldRunAnnotationResolve(name)) return annotationCall + if (!shouldRunAnnotationResolve(annotationTypeRef)) return annotationCall + transformAnnotationCall(annotationCall, annotationTypeRef) + return annotationCall + } + fun transformAnnotationCall(annotationCall: FirAnnotationCall, typeRef: FirUserTypeRef) { val transformedAnnotationType = typeResolverTransformer.transformUserTypeRef( - annotationTypeRef.createDeepCopy(), - ScopeClassDeclaration(scopes.asReversed(), classDeclarationsStack) - ) as? FirResolvedTypeRef ?: return annotationCall + userTypeRef = createDeepCopyOfTypeRef(typeRef), + data = ScopeClassDeclaration(scopes.asReversed(), classDeclarationsStack), + ) as? FirResolvedTypeRef ?: return resolveAnnotationsOnAnnotationIfNeeded(transformedAnnotationType) - if (!transformedAnnotationType.requiredToSave()) return annotationCall + if (!transformedAnnotationType.requiredToSave()) return annotationCall.replaceAnnotationTypeRef(transformedAnnotationType) annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.CompilerRequiredAnnotations) + // TODO: what if we have type alias here? if (transformedAnnotationType.coneTypeSafe()?.lookupTag?.classId in REQUIRED_ANNOTATIONS_WITH_ARGUMENTS) { argumentsTransformer.transformAnnotation(annotationCall, ResolutionMode.ContextDependent) } - - return annotationCall } private fun resolveAnnotationsOnAnnotationIfNeeded(annotationTypeRef: FirResolvedTypeRef) { @@ -258,7 +259,8 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer( error("Should not be there") } - private fun shouldRunAnnotationResolve(name: Name): Boolean { + fun shouldRunAnnotationResolve(typeRef: FirUserTypeRef): Boolean { + val name = typeRef.qualifier.last().name if (metaAnnotationsFromPlugins.isNotEmpty()) return true return name in REQUIRED_ANNOTATION_NAMES || annotationsFromPlugins.any { it.shortName() == name } } @@ -420,20 +422,31 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer( } private fun FirProperty.moveJavaDeprecatedAnnotationToBackingField() { - val annotations = this.annotations - val backingField = this.backingField - if (annotations.isNotEmpty() && backingField != null) { - val (backingFieldAnnotations, propertyAnnotations) = annotations.partition { - it.toAnnotationClassIdSafe(session) == Java.Deprecated - } - - if (backingFieldAnnotations.isNotEmpty()) { - this.replaceAnnotations(propertyAnnotations) - backingField.replaceAnnotations(backingField.annotations + backingFieldAnnotations) - } - } + val newPosition = extractBackingFieldAnnotationsFromProperty(this) ?: return + this.replaceAnnotations(newPosition.propertyAnnotations) + backingField?.replaceAnnotations(newPosition.backingFieldAnnotations) } + fun extractBackingFieldAnnotationsFromProperty( + property: FirProperty, + propertyAnnotations: List = property.annotations, + backingFieldAnnotations: List = property.backingField?.annotations.orEmpty(), + ): AnnotationsPosition? { + if (propertyAnnotations.isEmpty() || property.backingField == null) return null + + val (newBackingFieldAnnotations, newPropertyAnnotations) = propertyAnnotations.partition { + it.toAnnotationClassIdSafe(session) == Java.Deprecated + } + + if (newBackingFieldAnnotations.isEmpty()) return null + return AnnotationsPosition( + propertyAnnotations = newPropertyAnnotations, + backingFieldAnnotations = backingFieldAnnotations + newBackingFieldAnnotations, + ) + } + + class AnnotationsPosition(val backingFieldAnnotations: List, val propertyAnnotations: List) + override fun transformSimpleFunction( simpleFunction: FirSimpleFunction, data: Nothing? @@ -525,20 +538,18 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer( } } - private fun FirUserTypeRef.createDeepCopy(): FirUserTypeRef { - val original = this - return buildUserTypeRef { - source = original.source - isMarkedNullable = original.isMarkedNullable - annotations.addAll(original.annotations) - original.qualifier.mapTo(qualifier) { it.createDeepCopy() } - } + fun createDeepCopyOfTypeRef(original: FirUserTypeRef): FirUserTypeRef = buildUserTypeRef { + source = original.source + isMarkedNullable = original.isMarkedNullable + annotations.addAll(original.annotations) + original.qualifier.mapTo(qualifier) { it.createDeepCopy() } } private fun FirQualifierPart.createDeepCopy(): FirQualifierPart { val newArgumentList = FirTypeArgumentListImpl(typeArgumentList.source).apply { typeArgumentList.typeArguments.mapTo(typeArguments) { it.createDeepCopy() } } + return FirQualifierPartImpl( source, name, @@ -551,7 +562,7 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer( is FirTypeProjectionWithVariance -> buildTypeProjectionWithVariance { source = original.source typeRef = when (val originalTypeRef = original.typeRef) { - is FirUserTypeRef -> originalTypeRef.createDeepCopy() + is FirUserTypeRef -> createDeepCopyOfTypeRef(originalTypeRef) else -> originalTypeRef } variance = original.variance diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirCompilerRequiredAnnotationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirCompilerRequiredAnnotationsResolveTransformer.kt index 4d2168d33cc..162a46fc59f 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirCompilerRequiredAnnotationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/FirCompilerRequiredAnnotationsResolveTransformer.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.resolve.transformers.plugin +import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.PrivateForInline @@ -141,15 +142,18 @@ open class CompilerRequiredAnnotationsComputationSession { } } - private val declarationsWithResolvedAnnotations = mutableSetOf() + private val declarationsWithResolvedAnnotations = mutableSetOf() - fun annotationsAreResolved(declaration: FirDeclaration): Boolean { + fun annotationsAreResolved(declaration: FirAnnotationContainer, treatNonSourceDeclarationsAsResolved: Boolean): Boolean { if (declaration is FirFile) return false - if (declaration.origin != FirDeclarationOrigin.Source) return true + if (treatNonSourceDeclarationsAsResolved && declaration is FirDeclaration && declaration.origin != FirDeclarationOrigin.Source) { + return true + } + return declaration in declarationsWithResolvedAnnotations } - fun recordThatAnnotationsAreResolved(declaration: FirDeclaration) { + fun recordThatAnnotationsAreResolved(declaration: FirAnnotationContainer) { if (!declarationsWithResolvedAnnotations.add(declaration)) { error("Annotations are resolved twice") } @@ -157,7 +161,7 @@ open class CompilerRequiredAnnotationsComputationSession { fun resolveAnnotationsOnAnnotationIfNeeded(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) { val regularClass = symbol.fir - if (annotationsAreResolved(regularClass)) return + if (annotationsAreResolved(regularClass, treatNonSourceDeclarationsAsResolved = true)) return resolveAnnotationSymbol(symbol, scopeSession) } @@ -199,7 +203,7 @@ class FirSpecificAnnotationResolveTransformer( ) : AbstractFirSpecificAnnotationResolveTransformer(session, scopeSession, computationSession) { override fun shouldTransformDeclaration(declaration: FirDeclaration): Boolean { @OptIn(PrivateForInline::class) - return !computationSession.annotationsAreResolved(declaration) + return !computationSession.annotationsAreResolved(declaration, treatNonSourceDeclarationsAsResolved = true) } }