[FIR] Change API for generating nested classes from plugins
`FirDeclarationGenerationExtension.generateClassLikeDeclaration` was split into two functions: one for generating top level classes, and one for nested classes. Such change reduces verbosity and error-proness of this extension and also allows to smoothly run plugins on local classes ^KT-55248 Fixed
This commit is contained in:
committed by
Space Team
parent
faa96ec7c0
commit
6783621eb0
+5
@@ -10,9 +10,11 @@ import com.google.common.collect.Multimap
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.NoMutableState
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.extensions.predicate.AbstractPredicate
|
||||
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
|
||||
@@ -145,6 +147,9 @@ class FirPredicateBasedProviderImpl(private val session: FirSession) : FirPredic
|
||||
// ------------------------------------ Utilities ------------------------------------
|
||||
|
||||
private fun matchWith(declaration: FirDeclaration, annotations: Set<AnnotationFqn>): Boolean {
|
||||
if (declaration is FirClass && declaration.isLocal) {
|
||||
return declaration.annotations.any { it.fqName(session) in annotations }
|
||||
}
|
||||
return cache.annotationsOfDeclaration[declaration].any { it in annotations }
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.resolve.createCurrentScopeList
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirProviderInterceptor
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.runCompilerRequiredAnnotationsResolvePhaseForLocalClass
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.runCompanionGenerationPhaseForLocalClass
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.runStatusResolveForLocalClass
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.runSupertypeResolvePhaseForLocalClass
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.runTypeResolvePhaseForLocalClass
|
||||
@@ -32,6 +34,14 @@ fun <F : FirClassLikeDeclaration> F.runAllPhasesForLocalClass(
|
||||
components.context.outerLocalClassForNested[nested.symbol] = outer.symbol
|
||||
}
|
||||
|
||||
runCompilerRequiredAnnotationsResolvePhaseForLocalClass(
|
||||
components.session,
|
||||
components.scopeSession,
|
||||
localClassesNavigationInfo,
|
||||
components.file,
|
||||
components.containingDeclarations,
|
||||
)
|
||||
runCompanionGenerationPhaseForLocalClass(components.session)
|
||||
runSupertypeResolvePhaseForLocalClass(
|
||||
components.session,
|
||||
components.scopeSession,
|
||||
|
||||
+15
-3
@@ -45,7 +45,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
|
||||
internal abstract class AbstractFirSpecificAnnotationResolveTransformer(
|
||||
protected val session: FirSession,
|
||||
protected val scopeSession: ScopeSession,
|
||||
protected val computationSession: CompilerRequiredAnnotationsComputationSession
|
||||
protected val computationSession: CompilerRequiredAnnotationsComputationSession,
|
||||
containingDeclarations: List<FirDeclaration> = emptyList()
|
||||
) : FirDefaultTransformer<Nothing?>() {
|
||||
companion object {
|
||||
private val REQUIRED_ANNOTATIONS: Set<ClassId> = setOf(Deprecated, DeprecatedSinceKotlin, WasExperimental, JvmRecord)
|
||||
@@ -58,6 +59,9 @@ internal abstract class AbstractFirSpecificAnnotationResolveTransformer(
|
||||
private val annotationsFromPlugins: Set<AnnotationFqn> = session.registeredPluginAnnotations.annotations
|
||||
private val metaAnnotationsFromPlugins: Set<AnnotationFqn> = session.registeredPluginAnnotations.metaAnnotations
|
||||
|
||||
protected open val shouldRecordIntoPredicateBasedProvider: Boolean
|
||||
get() = true
|
||||
|
||||
@PrivateForInline
|
||||
val typeResolverTransformer: FirSpecificTypeResolverTransformer = FirSpecificTypeResolverTransformer(
|
||||
session,
|
||||
@@ -72,7 +76,13 @@ internal abstract class AbstractFirSpecificAnnotationResolveTransformer(
|
||||
)
|
||||
|
||||
private var owners: PersistentList<FirDeclaration> = persistentListOf()
|
||||
private val classDeclarationsStack = ArrayDeque<FirClass>()
|
||||
private val classDeclarationsStack = ArrayDeque<FirClass>().apply {
|
||||
for (declaration in containingDeclarations) {
|
||||
if (declaration is FirClass) {
|
||||
add(declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(PrivateForInline::class)
|
||||
override fun transformAnnotationCall(annotationCall: FirAnnotationCall, data: Nothing?): FirStatement {
|
||||
@@ -163,7 +173,9 @@ internal abstract class AbstractFirSpecificAnnotationResolveTransformer(
|
||||
@OptIn(FirExtensionApiInternals::class)
|
||||
override fun transformDeclaration(declaration: FirDeclaration, data: Nothing?): FirDeclaration {
|
||||
return (transformAnnotationContainer(declaration, data) as FirDeclaration).also {
|
||||
predicateBasedProvider.registerAnnotatedDeclaration(declaration, owners)
|
||||
if (shouldRecordIntoPredicateBasedProvider) {
|
||||
predicateBasedProvider.registerAnnotatedDeclaration(declaration, owners)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-11
@@ -9,14 +9,20 @@ import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.classId
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
import org.jetbrains.kotlin.fir.extensions.FirSwitchableExtensionDeclarationsSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.extensions.generatedDeclarationsSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirTransformerBasedResolveProcessor
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.nestedClassifierScope
|
||||
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||
|
||||
class FirCompanionGenerationProcessor(
|
||||
session: FirSession,
|
||||
@@ -26,8 +32,8 @@ class FirCompanionGenerationProcessor(
|
||||
}
|
||||
|
||||
class FirCompanionGenerationTransformer(val session: FirSession) : FirTransformer<Nothing?>() {
|
||||
|
||||
lateinit var generatedDeclarationProvider: FirSwitchableExtensionDeclarationsSymbolProvider
|
||||
private val generatedDeclarationProvider: FirSwitchableExtensionDeclarationsSymbolProvider? =
|
||||
session.generatedDeclarationsSymbolProvider
|
||||
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E {
|
||||
return element
|
||||
@@ -35,26 +41,45 @@ class FirCompanionGenerationTransformer(val session: FirSession) : FirTransforme
|
||||
|
||||
override fun transformFile(file: FirFile, data: Nothing?): FirFile {
|
||||
// I don't want to use laziness here to prevent possible multi-threading problems
|
||||
generatedDeclarationProvider = session.generatedDeclarationsSymbolProvider ?: return file
|
||||
if (generatedDeclarationProvider == null) return file
|
||||
return file.transformDeclarations(this, data)
|
||||
}
|
||||
|
||||
override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): FirStatement {
|
||||
generateCompanion(regularClass)
|
||||
val companionSymbol = generateCompanion(regularClass)
|
||||
if (companionSymbol != null) {
|
||||
regularClass.replaceCompanionObjectSymbol(companionSymbol)
|
||||
}
|
||||
return regularClass.transformDeclarations(this, data)
|
||||
}
|
||||
|
||||
private fun generateCompanion(regularClass: FirRegularClass) {
|
||||
// TODO: add proper error reporting
|
||||
generatedDeclarationProvider = session.generatedDeclarationsSymbolProvider ?: return
|
||||
val companionClassId = regularClass.classId.createNestedClassId(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT)
|
||||
when (val generatedCompanion = generatedDeclarationProvider.getClassLikeSymbolByClassId(companionClassId)) {
|
||||
null -> {}
|
||||
private fun generateCompanion(regularClass: FirRegularClass): FirRegularClassSymbol? {
|
||||
if (generatedDeclarationProvider == null) return null
|
||||
val generatedCompanion = if (regularClass.isLocal) {
|
||||
var result: FirClassLikeSymbol<*>? = null
|
||||
session.nestedClassifierScope(regularClass)?.processClassifiersByName(DEFAULT_NAME_FOR_COMPANION_OBJECT) {
|
||||
if (it is FirClassLikeSymbol<*> && it.origin.generated) {
|
||||
result = it
|
||||
}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
val companionClassId = regularClass.classId.createNestedClassId(DEFAULT_NAME_FOR_COMPANION_OBJECT)
|
||||
generatedDeclarationProvider.getClassLikeSymbolByClassId(companionClassId)?.takeIf { it.origin.generated }
|
||||
}
|
||||
|
||||
return when (generatedCompanion) {
|
||||
null -> null
|
||||
is FirRegularClassSymbol -> when {
|
||||
regularClass.companionObjectSymbol != null -> error("Plugin generated companion object for class $regularClass, but it is already present in class")
|
||||
else -> regularClass.replaceCompanionObjectSymbol(generatedCompanion)
|
||||
else -> generatedCompanion
|
||||
}
|
||||
else -> error("Plugin generated non regular class as companion object")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <F : FirClassLikeDeclaration> F.runCompanionGenerationPhaseForLocalClass(session: FirSession): F {
|
||||
val transformer = FirCompanionGenerationTransformer(session)
|
||||
return this.transformSingle(transformer, null)
|
||||
}
|
||||
|
||||
+40
-5
@@ -14,15 +14,11 @@ import org.jetbrains.kotlin.fir.extensions.*
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.*
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.LocalClassesNavigationInfo
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.fir.withFileAnalysisExceptionWrapping
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.Deprecated
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.DeprecatedSinceKotlin
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.JvmRecord
|
||||
import org.jetbrains.kotlin.name.StandardClassIds.Annotations.WasExperimental
|
||||
|
||||
class FirCompilerRequiredAnnotationsResolveProcessor(
|
||||
session: FirSession,
|
||||
@@ -174,3 +170,42 @@ private class FirDesignatedSpecificAnnotationResolveTransformer(
|
||||
return !designation.shouldSkipClass(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
fun <F : FirClassLikeDeclaration> F.runCompilerRequiredAnnotationsResolvePhaseForLocalClass(
|
||||
session: FirSession,
|
||||
scopeSession: ScopeSession,
|
||||
localClassesNavigationInfo: LocalClassesNavigationInfo,
|
||||
useSiteFile: FirFile,
|
||||
containingDeclarations: List<FirDeclaration>,
|
||||
): F {
|
||||
val computationSession = CompilerRequiredAnnotationsComputationSession()
|
||||
val annotationsResolveTransformer = FirSpecificAnnotationForLocalClassesResolveTransformer(
|
||||
session,
|
||||
scopeSession,
|
||||
computationSession,
|
||||
containingDeclarations,
|
||||
localClassesNavigationInfo
|
||||
)
|
||||
return annotationsResolveTransformer.withFileScopes(useSiteFile) {
|
||||
this.transformSingle(annotationsResolveTransformer, null)
|
||||
}
|
||||
}
|
||||
|
||||
private class FirSpecificAnnotationForLocalClassesResolveTransformer(
|
||||
session: FirSession,
|
||||
scopeSession: ScopeSession,
|
||||
computationSession: CompilerRequiredAnnotationsComputationSession,
|
||||
containingDeclarations: List<FirDeclaration>,
|
||||
private val localClassesNavigationInfo: LocalClassesNavigationInfo
|
||||
) : AbstractFirSpecificAnnotationResolveTransformer(session, scopeSession, computationSession, containingDeclarations) {
|
||||
override fun shouldTransformDeclaration(declaration: FirDeclaration): Boolean {
|
||||
localClassesNavigationInfo.allMembers
|
||||
return when (declaration) {
|
||||
is FirClassLikeDeclaration -> declaration in localClassesNavigationInfo.parentForClass
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
override val shouldRecordIntoPredicateBasedProvider: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user