[LL FIR] rewrite compiler required annotations transformer to avoid lazy resolve under locks

^KT-56551
This commit is contained in:
Dmitrii Gridin
2023-05-18 13:59:31 +02:00
committed by Space Team
parent 4b1dc4eee6
commit 8f82649529
8 changed files with 344 additions and 99 deletions
@@ -220,11 +220,13 @@ object LowLevelFirApiFacadeForResolveOnAir {
fileAnnotation = annotationEntry, fileAnnotation = annotationEntry,
replacement = replacement replacement = replacement
) )
val fileAnnotationsContainer = buildFileAnnotationsContainer { val fileAnnotationsContainer = buildFileAnnotationsContainer {
moduleData = firFile.moduleData moduleData = firFile.moduleData
containingFileSymbol = firFile.symbol containingFileSymbol = firFile.symbol
annotations += annotationCall annotations += annotationCall
} }
val llFirResolvableSession = firFile.llFirResolvableSession val llFirResolvableSession = firFile.llFirResolvableSession
?: buildErrorWithAttachment("FirFile session expected to be a resolvable session but was ${firFile.llFirSession::class.java}") { ?: buildErrorWithAttachment("FirFile session expected to be a resolvable session but was ${firFile.llFirSession::class.java}") {
withEntry("firSession", firFile.llFirSession) { it.toString() } withEntry("firSession", firFile.llFirSession) { it.toString() }
@@ -239,7 +241,8 @@ object LowLevelFirApiFacadeForResolveOnAir {
collector?.addFileContext(firFile, firFile.createTowerDataContext(firResolveSession.getScopeSessionFor(llFirResolvableSession))) 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( private fun runBodyResolveOnAir(
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* 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.customAnnotations
import org.jetbrains.kotlin.fir.types.forEachType import org.jetbrains.kotlin.fir.types.forEachType
@@ -57,6 +56,18 @@ internal object FirLazyBodiesCalculator {
FirLazyAnnotationTransformerData(firElement.moduleData.session, FirLazyAnnotationTransformerScope.COMPILER_ONLY) 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<FirValueParameter>, newValueParameters: List<FirValueParameter>) { private fun replaceValueParameterDefaultValues(valueParameters: List<FirValueParameter>, newValueParameters: List<FirValueParameter>) {
@@ -68,15 +79,6 @@ private fun replaceValueParameterDefaultValues(valueParameters: List<FirValuePar
} }
} }
private fun calculateLazyArgumentsForAnnotation(annotationCall: FirAnnotationCall, session: FirSession) {
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)
annotationCall.replaceArgumentList(newAnnotationCall.argumentList)
}
private fun calculateLazyBodiesForFunction(designation: FirDesignation) { private fun calculateLazyBodiesForFunction(designation: FirDesignation) {
val simpleFunction = designation.target as FirSimpleFunction val simpleFunction = designation.target as FirSimpleFunction
require(needCalculatingLazyBodyForFunction(simpleFunction)) require(needCalculatingLazyBodyForFunction(simpleFunction))
@@ -214,9 +216,6 @@ private fun needCalculatingLazyBodyForProperty(firProperty: FirProperty): Boolea
|| (firProperty.delegate as? FirWrappedDelegateExpression)?.expression is FirLazyExpression || (firProperty.delegate as? FirWrappedDelegateExpression)?.expression is FirLazyExpression
|| firProperty.getExplicitBackingField()?.initializer is FirLazyExpression || firProperty.getExplicitBackingField()?.initializer is FirLazyExpression
private fun needCalculatingAnnotationCall(firAnnotationCall: FirAnnotationCall): Boolean =
firAnnotationCall.argumentList.arguments.any { it is FirLazyExpression }
private enum class FirLazyAnnotationTransformerScope { private enum class FirLazyAnnotationTransformerScope {
ALL_ANNOTATIONS, ALL_ANNOTATIONS,
COMPILER_ONLY; COMPILER_ONLY;
@@ -287,8 +286,9 @@ private abstract class FirLazyAnnotationTransformer : FirTransformer<FirLazyAnno
override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: FirLazyAnnotationTransformerData): FirStatement { override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: FirLazyAnnotationTransformerData): FirStatement {
val shouldCalculate = data.compilerAnnotationsOnly == FirLazyAnnotationTransformerScope.ALL_ANNOTATIONS || val shouldCalculate = data.compilerAnnotationsOnly == FirLazyAnnotationTransformerScope.ALL_ANNOTATIONS ||
canBeCompilerAnnotation(annotationCall, data.session) canBeCompilerAnnotation(annotationCall, data.session)
if (shouldCalculate && needCalculatingAnnotationCall(annotationCall)) { if (shouldCalculate && FirLazyBodiesCalculator.needCalculatingAnnotationCall(annotationCall)) {
calculateLazyArgumentsForAnnotation(annotationCall, data.session) val newArgumentList = FirLazyBodiesCalculator.calculateLazyArgumentsForAnnotation(annotationCall, data.session)
annotationCall.replaceArgumentList(newArgumentList)
} }
super.transformAnnotationCall(annotationCall, data) super.transformAnnotationCall(annotationCall, data)
@@ -13,17 +13,26 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockPro
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.LLFirPhaseUpdater import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.LLFirPhaseUpdater
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkDeprecationProviderIsResolved
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkPhase import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkPhase
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirElementWithResolveState import org.jetbrains.kotlin.fir.FirElementWithResolveState
import org.jetbrains.kotlin.fir.FirFileAnnotationsContainer
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCallCopy
import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.CompilerRequiredAnnotationsComputationSession import org.jetbrains.kotlin.fir.resolve.transformers.plugin.CompilerRequiredAnnotationsComputationSession
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirCompilerRequiredAnnotationsResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirCompilerRequiredAnnotationsResolveTransformer
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.fir.types.FirUserTypeRef
internal object LLFirCompilerAnnotationsLazyResolver : LLFirLazyResolver(FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS) { internal object LLFirCompilerAnnotationsLazyResolver : LLFirLazyResolver(FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS) {
override fun resolve( override fun resolve(
@@ -43,8 +52,12 @@ internal object LLFirCompilerAnnotationsLazyResolver : LLFirLazyResolver(FirReso
override fun checkIsResolved(target: FirElementWithResolveState) { override fun checkIsResolved(target: FirElementWithResolveState) {
target.checkPhase(resolverPhase) target.checkPhase(resolverPhase)
when (target) {
is FirClassLikeDeclaration -> checkDeprecationProviderIsResolved(target, target.deprecationsProvider)
is FirCallableDeclaration -> checkDeprecationProviderIsResolved(target, target.deprecationsProvider)
}
checkNestedDeclarationsAreResolved(target) checkNestedDeclarationsAreResolved(target)
// TODO add proper check that COMPILER_REQUIRED_ANNOTATIONS are resolved
} }
} }
@@ -54,7 +67,7 @@ private class LLFirCompilerRequiredAnnotationsTargetResolver(
session: FirSession, session: FirSession,
scopeSession: ScopeSession, scopeSession: ScopeSession,
computationSession: LLFirCompilerRequiredAnnotationsComputationSession? = null, 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() { inner class LLFirCompilerRequiredAnnotationsComputationSession : CompilerRequiredAnnotationsComputationSession() {
override fun resolveAnnotationSymbol(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) { override fun resolveAnnotationSymbol(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) {
val regularClass = symbol.fir val regularClass = symbol.fir
@@ -80,6 +93,10 @@ private class LLFirCompilerRequiredAnnotationsTargetResolver(
computationSession ?: LLFirCompilerRequiredAnnotationsComputationSession(), computationSession ?: LLFirCompilerRequiredAnnotationsComputationSession(),
) )
@OptIn(PrivateForInline::class)
private val llFirComputationSession: LLFirCompilerRequiredAnnotationsComputationSession
get() = transformer.annotationTransformer.computationSession as LLFirCompilerRequiredAnnotationsComputationSession
override fun withFile(firFile: FirFile, action: () -> Unit) { override fun withFile(firFile: FirFile, action: () -> Unit) {
transformer.annotationTransformer.withFileAndFileScopes(firFile) { transformer.annotationTransformer.withFileAndFileScopes(firFile) {
action() action()
@@ -93,25 +110,211 @@ private class LLFirCompilerRequiredAnnotationsTargetResolver(
} }
} }
override fun doLazyResolveUnderLock(target: FirElementWithResolveState) { override fun doResolveWithoutLock(target: FirElementWithResolveState): Boolean {
FirLazyBodiesCalculator.calculateCompilerAnnotations(target) if (target !is FirRegularClass && !target.isRegularDeclarationWithAnnotation) {
throwUnexpectedFirElementError(target)
}
when { requireIsInstance<FirAnnotationContainer>(target)
target is FirRegularClass -> { resolveTargetDeclaration(target)
transformer.annotationTransformer.resolveRegularClass(
target, return true
transformChildren = {}, }
afterChildrenTransform = {
transformer.annotationTransformer.calculateDeprecations(target) private fun <T> 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 -> { // 5. Transform annotations in the air
target.transformSingle(transformer.annotationTransformer, null) 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> T.createAnnotationTransformer(): AnnotationTransformer? where T : FirAnnotationContainer, T : FirElementWithResolveState {
if (!hasAnnotationsToResolve()) {
return (AnnotationTransformer(mutableMapOf()))
}
val map = hashMapOf<FirElementWithResolveState, List<FirAnnotation>>()
var isUnresolved = false
withReadLock(this) {
isUnresolved = true
annotationsForTransformationTo(map)
}
return map.takeIf { isUnresolved }?.let(::AnnotationTransformer)
}
private inner class AnnotationTransformer(
private val annotationMap: MutableMap<FirElementWithResolveState, List<FirAnnotation>>,
) {
private val deprecations: MutableMap<FirElementWithResolveState, DeprecationsProvider> = 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<FirAnnotationContainer>(declaration)
deprecations[declaration] = declaration.extractDeprecationInfoPerUseSite(
session = session,
customAnnotations = annotations,
).toDeprecationsProvider(cacheFactory)
}
}
fun <T> 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> T.annotationsForTransformationTo(
map: MutableMap<FirElementWithResolveState, List<FirAnnotation>>,
) 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<FirAnnotation>(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
}
} }
@@ -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) { internal fun checkReturnTypeRefIsResolved(declaration: FirCallableDeclaration, acceptImplicitTypeRef: Boolean = false) {
checkTypeRefIsResolved(declaration.returnTypeRef, typeRefName = "return type", declaration, acceptImplicitTypeRef) checkTypeRefIsResolved(declaration.returnTypeRef, typeRefName = "return type", declaration, acceptImplicitTypeRef)
} }
@@ -30,7 +30,7 @@ FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt
COMPILER_REQUIRED_ANNOTATIONS: COMPILER_REQUIRED_ANNOTATIONS:
FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt
@FILE:Deprecated[Unresolved](String()) @FILE:R|kotlin/Deprecated|[CompilerRequiredAnnotations](String())
@FILE:Anno[Unresolved](LAZY_EXPRESSION) @FILE:Anno[Unresolved](LAZY_EXPRESSION)
[ResolvedTo(COMPILER_REQUIRED_ANNOTATIONS)] annotations container [ResolvedTo(COMPILER_REQUIRED_ANNOTATIONS)] annotations container
@Target[Unresolved](LAZY_EXPRESSION) public? final? [ResolvedTo(RAW_FIR)] annotation class Anno : R|kotlin/Annotation| { @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: COMPANION_GENERATION:
FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt
@FILE:Deprecated[Unresolved](String()) @FILE:R|kotlin/Deprecated|[CompilerRequiredAnnotations](String())
@FILE:Anno[Unresolved](LAZY_EXPRESSION) @FILE:Anno[Unresolved](LAZY_EXPRESSION)
[ResolvedTo(COMPANION_GENERATION)] annotations container [ResolvedTo(COMPANION_GENERATION)] annotations container
@Target[Unresolved](LAZY_EXPRESSION) public? final? [ResolvedTo(RAW_FIR)] annotation class Anno : R|kotlin/Annotation| { @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: SUPER_TYPES:
FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt FILE: [ResolvedTo(IMPORTS)] fileAnnotations.kt
@FILE:Deprecated[Unresolved](String()) @FILE:R|kotlin/Deprecated|[CompilerRequiredAnnotations](String())
@FILE:Anno[Unresolved](LAZY_EXPRESSION) @FILE:Anno[Unresolved](LAZY_EXPRESSION)
[ResolvedTo(SUPER_TYPES)] annotations container [ResolvedTo(SUPER_TYPES)] annotations container
@Target[Unresolved](LAZY_EXPRESSION) public? final? [ResolvedTo(RAW_FIR)] annotation class Anno : R|kotlin/Annotation| { @Target[Unresolved](LAZY_EXPRESSION) public? final? [ResolvedTo(RAW_FIR)] annotation class Anno : R|kotlin/Annotation| {
@@ -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. * 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) return extractDeprecationInfoPerUseSite(session).toDeprecationsProvider(session.firCachesFactory)
} }
fun FirAnnotationContainer.extractDeprecationInfoPerUseSite(session: FirSession): DeprecationAnnotationInfoPerUseSiteStorage { fun FirAnnotationContainer.extractDeprecationInfoPerUseSite(
session: FirSession,
customAnnotations: List<FirAnnotation>? = annotations,
getterAnnotations: List<FirAnnotation>? = null,
setterAnnotations: List<FirAnnotation>? = null,
): DeprecationAnnotationInfoPerUseSiteStorage {
val fromJava = this is FirDeclaration && this.isJavaOrEnhancement val fromJava = this is FirDeclaration && this.isJavaOrEnhancement
return buildDeprecationAnnotationInfoPerUseSiteStorage { return buildDeprecationAnnotationInfoPerUseSiteStorage {
add(annotations.extractDeprecationAnnotationInfoPerUseSite(session, fromJava)) add((customAnnotations ?: annotations).extractDeprecationAnnotationInfoPerUseSite(session, fromJava))
if (this@extractDeprecationInfoPerUseSite is FirProperty) { 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, session: FirSession,
getter: FirFunction?, getter: FirFunction?,
setter: FirFunction? setter: FirFunction?
): DeprecationsProvider { ): DeprecationsProvider = getDeprecationsAnnotationInfoByUseSiteFromAccessors(
return getDeprecationsAnnotationInfoByUseSiteFromAccessors(session, getter, setter).toDeprecationsProvider(session.firCachesFactory) session = session,
} getter = getter,
setter = setter,
).toDeprecationsProvider(session.firCachesFactory)
fun getDeprecationsAnnotationInfoByUseSiteFromAccessors( fun getDeprecationsAnnotationInfoByUseSiteFromAccessors(
session: FirSession, session: FirSession,
getter: FirFunction?, getter: FirFunction?,
setter: FirFunction? getterAnnotations: List<FirAnnotation>? = getter?.annotations,
): DeprecationAnnotationInfoPerUseSiteStorage { setter: FirFunction?,
return buildDeprecationAnnotationInfoPerUseSiteStorage { setterAnnotations: List<FirAnnotation>? = setter?.annotations,
val setterDeprecations = setter?.extractDeprecationInfoPerUseSite(session) ): DeprecationAnnotationInfoPerUseSiteStorage = buildDeprecationAnnotationInfoPerUseSiteStorage {
setterDeprecations?.storage?.forEach { (useSite, infos) -> val setterDeprecations = setter?.extractDeprecationInfoPerUseSite(session, customAnnotations = setterAnnotations)
if (useSite == null) { setterDeprecations?.storage?.forEach { (useSite, infos) ->
add(AnnotationUseSiteTarget.PROPERTY_SETTER, infos) if (useSite == null) {
} else { add(AnnotationUseSiteTarget.PROPERTY_SETTER, infos)
add(useSite, infos) } else {
} add(useSite, infos)
} }
val getterDeprecations = getter?.extractDeprecationInfoPerUseSite(session) }
getterDeprecations?.storage?.forEach { (useSite, infos) ->
if (useSite == null) { val getterDeprecations = getter?.extractDeprecationInfoPerUseSite(session, customAnnotations = getterAnnotations)
add(AnnotationUseSiteTarget.PROPERTY_GETTER, infos) getterDeprecations?.storage?.forEach { (useSite, infos) ->
} else { if (useSite == null) {
add(useSite, infos) add(AnnotationUseSiteTarget.PROPERTY_GETTER, infos)
} } else {
add(useSite, infos)
} }
} }
} }
@@ -226,27 +226,28 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer(
override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: Nothing?): FirStatement { override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: Nothing?): FirStatement {
val annotationTypeRef = annotationCall.annotationTypeRef val annotationTypeRef = annotationCall.annotationTypeRef
if (annotationTypeRef !is FirUserTypeRef) return annotationCall if (annotationTypeRef !is FirUserTypeRef) return annotationCall
val name = annotationTypeRef.qualifier.last().name if (!shouldRunAnnotationResolve(annotationTypeRef)) return annotationCall
transformAnnotationCall(annotationCall, annotationTypeRef)
if (!shouldRunAnnotationResolve(name)) return annotationCall return annotationCall
}
fun transformAnnotationCall(annotationCall: FirAnnotationCall, typeRef: FirUserTypeRef) {
val transformedAnnotationType = typeResolverTransformer.transformUserTypeRef( val transformedAnnotationType = typeResolverTransformer.transformUserTypeRef(
annotationTypeRef.createDeepCopy(), userTypeRef = createDeepCopyOfTypeRef(typeRef),
ScopeClassDeclaration(scopes.asReversed(), classDeclarationsStack) data = ScopeClassDeclaration(scopes.asReversed(), classDeclarationsStack),
) as? FirResolvedTypeRef ?: return annotationCall ) as? FirResolvedTypeRef ?: return
resolveAnnotationsOnAnnotationIfNeeded(transformedAnnotationType) resolveAnnotationsOnAnnotationIfNeeded(transformedAnnotationType)
if (!transformedAnnotationType.requiredToSave()) return annotationCall if (!transformedAnnotationType.requiredToSave()) return
annotationCall.replaceAnnotationTypeRef(transformedAnnotationType) annotationCall.replaceAnnotationTypeRef(transformedAnnotationType)
annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.CompilerRequiredAnnotations) annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.CompilerRequiredAnnotations)
// TODO: what if we have type alias here? // TODO: what if we have type alias here?
if (transformedAnnotationType.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId in REQUIRED_ANNOTATIONS_WITH_ARGUMENTS) { if (transformedAnnotationType.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId in REQUIRED_ANNOTATIONS_WITH_ARGUMENTS) {
argumentsTransformer.transformAnnotation(annotationCall, ResolutionMode.ContextDependent) argumentsTransformer.transformAnnotation(annotationCall, ResolutionMode.ContextDependent)
} }
return annotationCall
} }
private fun resolveAnnotationsOnAnnotationIfNeeded(annotationTypeRef: FirResolvedTypeRef) { private fun resolveAnnotationsOnAnnotationIfNeeded(annotationTypeRef: FirResolvedTypeRef) {
@@ -258,7 +259,8 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer(
error("Should not be there") 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 if (metaAnnotationsFromPlugins.isNotEmpty()) return true
return name in REQUIRED_ANNOTATION_NAMES || annotationsFromPlugins.any { it.shortName() == name } return name in REQUIRED_ANNOTATION_NAMES || annotationsFromPlugins.any { it.shortName() == name }
} }
@@ -420,20 +422,31 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer(
} }
private fun FirProperty.moveJavaDeprecatedAnnotationToBackingField() { private fun FirProperty.moveJavaDeprecatedAnnotationToBackingField() {
val annotations = this.annotations val newPosition = extractBackingFieldAnnotationsFromProperty(this) ?: return
val backingField = this.backingField this.replaceAnnotations(newPosition.propertyAnnotations)
if (annotations.isNotEmpty() && backingField != null) { backingField?.replaceAnnotations(newPosition.backingFieldAnnotations)
val (backingFieldAnnotations, propertyAnnotations) = annotations.partition {
it.toAnnotationClassIdSafe(session) == Java.Deprecated
}
if (backingFieldAnnotations.isNotEmpty()) {
this.replaceAnnotations(propertyAnnotations)
backingField.replaceAnnotations(backingField.annotations + backingFieldAnnotations)
}
}
} }
fun extractBackingFieldAnnotationsFromProperty(
property: FirProperty,
propertyAnnotations: List<FirAnnotation> = property.annotations,
backingFieldAnnotations: List<FirAnnotation> = 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<FirAnnotation>, val propertyAnnotations: List<FirAnnotation>)
override fun transformSimpleFunction( override fun transformSimpleFunction(
simpleFunction: FirSimpleFunction, simpleFunction: FirSimpleFunction,
data: Nothing? data: Nothing?
@@ -525,20 +538,18 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer(
} }
} }
private fun FirUserTypeRef.createDeepCopy(): FirUserTypeRef { fun createDeepCopyOfTypeRef(original: FirUserTypeRef): FirUserTypeRef = buildUserTypeRef {
val original = this source = original.source
return buildUserTypeRef { isMarkedNullable = original.isMarkedNullable
source = original.source annotations.addAll(original.annotations)
isMarkedNullable = original.isMarkedNullable original.qualifier.mapTo(qualifier) { it.createDeepCopy() }
annotations.addAll(original.annotations)
original.qualifier.mapTo(qualifier) { it.createDeepCopy() }
}
} }
private fun FirQualifierPart.createDeepCopy(): FirQualifierPart { private fun FirQualifierPart.createDeepCopy(): FirQualifierPart {
val newArgumentList = FirTypeArgumentListImpl(typeArgumentList.source).apply { val newArgumentList = FirTypeArgumentListImpl(typeArgumentList.source).apply {
typeArgumentList.typeArguments.mapTo(typeArguments) { it.createDeepCopy() } typeArgumentList.typeArguments.mapTo(typeArguments) { it.createDeepCopy() }
} }
return FirQualifierPartImpl( return FirQualifierPartImpl(
source, source,
name, name,
@@ -551,7 +562,7 @@ abstract class AbstractFirSpecificAnnotationResolveTransformer(
is FirTypeProjectionWithVariance -> buildTypeProjectionWithVariance { is FirTypeProjectionWithVariance -> buildTypeProjectionWithVariance {
source = original.source source = original.source
typeRef = when (val originalTypeRef = original.typeRef) { typeRef = when (val originalTypeRef = original.typeRef) {
is FirUserTypeRef -> originalTypeRef.createDeepCopy() is FirUserTypeRef -> createDeepCopyOfTypeRef(originalTypeRef)
else -> originalTypeRef else -> originalTypeRef
} }
variance = original.variance variance = original.variance
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.transformers.plugin 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.FirElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.PrivateForInline
@@ -141,15 +142,18 @@ open class CompilerRequiredAnnotationsComputationSession {
} }
} }
private val declarationsWithResolvedAnnotations = mutableSetOf<FirDeclaration>() private val declarationsWithResolvedAnnotations = mutableSetOf<FirAnnotationContainer>()
fun annotationsAreResolved(declaration: FirDeclaration): Boolean { fun annotationsAreResolved(declaration: FirAnnotationContainer, treatNonSourceDeclarationsAsResolved: Boolean): Boolean {
if (declaration is FirFile) return false 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 return declaration in declarationsWithResolvedAnnotations
} }
fun recordThatAnnotationsAreResolved(declaration: FirDeclaration) { fun recordThatAnnotationsAreResolved(declaration: FirAnnotationContainer) {
if (!declarationsWithResolvedAnnotations.add(declaration)) { if (!declarationsWithResolvedAnnotations.add(declaration)) {
error("Annotations are resolved twice") error("Annotations are resolved twice")
} }
@@ -157,7 +161,7 @@ open class CompilerRequiredAnnotationsComputationSession {
fun resolveAnnotationsOnAnnotationIfNeeded(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) { fun resolveAnnotationsOnAnnotationIfNeeded(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) {
val regularClass = symbol.fir val regularClass = symbol.fir
if (annotationsAreResolved(regularClass)) return if (annotationsAreResolved(regularClass, treatNonSourceDeclarationsAsResolved = true)) return
resolveAnnotationSymbol(symbol, scopeSession) resolveAnnotationSymbol(symbol, scopeSession)
} }
@@ -199,7 +203,7 @@ class FirSpecificAnnotationResolveTransformer(
) : AbstractFirSpecificAnnotationResolveTransformer(session, scopeSession, computationSession) { ) : AbstractFirSpecificAnnotationResolveTransformer(session, scopeSession, computationSession) {
override fun shouldTransformDeclaration(declaration: FirDeclaration): Boolean { override fun shouldTransformDeclaration(declaration: FirDeclaration): Boolean {
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
return !computationSession.annotationsAreResolved(declaration) return !computationSession.annotationsAreResolved(declaration, treatNonSourceDeclarationsAsResolved = true)
} }
} }