[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:
Dmitriy Novozhilov
2022-12-21 10:29:54 +02:00
committed by Space Team
parent faa96ec7c0
commit 6783621eb0
31 changed files with 458 additions and 162 deletions
@@ -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 }
}
@@ -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,
@@ -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)
}
}
}
@@ -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)
}
@@ -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
}