[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
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.fir.plugin.fqn
import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
@@ -58,10 +57,8 @@ class AdditionalMembersGenerator(session: FirSession) : FirDeclarationGeneration
return listOf(buildMaterializeFunction(matchedClassSymbol, callableId, Key).symbol)
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
if (classId.shortClassName != NESTED_NAME) return null
val parentClassId = classId.parentClassId ?: return null
if (matchedClasses.none { it.classId == parentClassId }) return null
override fun generateNestedClassLikeDeclaration(owner: FirClassSymbol<*>, name: Name): FirClassLikeSymbol<*>? {
if (matchedClasses.none { it == owner }) return null
return buildRegularClass {
resolvePhase = FirResolvePhase.BODY_RESOLVE
moduleData = session.moduleData
@@ -69,16 +66,14 @@ class AdditionalMembersGenerator(session: FirSession) : FirDeclarationGeneration
classKind = ClassKind.CLASS
scopeProvider = session.kotlinScopeProvider
status = FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.FINAL, EffectiveVisibility.Public)
name = classId.shortClassName
symbol = FirRegularClassSymbol(classId)
this.name = name
symbol = FirRegularClassSymbol(owner.classId.createNestedClassId(name))
superTypeRefs += session.builtinTypes.anyType
}.symbol
}
override fun generateConstructors(context: MemberGenerationContext): List<FirConstructorSymbol> {
val ownerClassId = context.owner.classId
assert(ownerClassId in nestedClassIds)
return listOf(buildConstructor(ownerClassId, isInner = false, Key).symbol)
return listOf(buildConstructor(context.owner, isInner = false, Key).symbol)
}
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
@@ -17,11 +17,8 @@ import org.jetbrains.kotlin.fir.declarations.builder.buildRegularClass
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.origin
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
import org.jetbrains.kotlin.fir.extensions.*
import org.jetbrains.kotlin.fir.extensions.predicate.LookupPredicate
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.plugin.fqn
import org.jetbrains.kotlin.fir.resolve.defaultType
@@ -29,7 +26,6 @@ import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
@@ -42,15 +38,8 @@ class CompanionGenerator(session: FirSession) : FirDeclarationGenerationExtensio
private val FOO_NAME = Name.identifier("foo")
}
private val matchedClasses by lazy {
session.predicateBasedProvider.getSymbolsByPredicate(PREDICATE)
.filterIsInstance<FirRegularClassSymbol>()
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
if (classId.shortClassName != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) return null
val owner = matchedClasses.firstOrNull { it.classId == classId.outerClassId } ?: return null
if (owner.companionObjectSymbol != null) return null
override fun generateNestedClassLikeDeclaration(owner: FirClassSymbol<*>, name: Name): FirClassLikeSymbol<*>? {
if (name != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) return null
val regularClass = buildRegularClass {
resolvePhase = FirResolvePhase.BODY_RESOLVE
moduleData = session.moduleData
@@ -64,8 +53,8 @@ class CompanionGenerator(session: FirSession) : FirDeclarationGenerationExtensio
).apply {
isCompanion = true
}
name = SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
symbol = FirRegularClassSymbol(classId)
this.name = name
symbol = FirRegularClassSymbol(owner.classId.createNestedClassId(name))
superTypeRefs += session.builtinTypes.anyType
}
return regularClass.symbol
@@ -94,7 +83,7 @@ class CompanionGenerator(session: FirSession) : FirDeclarationGenerationExtensio
}
override fun generateConstructors(context: MemberGenerationContext): List<FirConstructorSymbol> {
val constructor = buildConstructor(context.owner.classId, isInner = false, Key)
val constructor = buildConstructor(context.owner, isInner = false, Key)
return listOf(constructor.symbol)
}
@@ -61,30 +61,26 @@ class ExternalClassGenerator(session: FirSession) : FirDeclarationGenerationExte
}
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
val owner = classId.outerClassId?.let { session.symbolProvider.getClassLikeSymbolByClassId(it) } as? FirClassSymbol<*>
return when {
owner != null -> when (val origin = owner.origin) {
is FirDeclarationOrigin.Plugin -> when (origin.key) {
Key -> generateNestedClass(classId, owner)
else -> null
}
else -> null
}
else -> generateAllOpenGeneratedClass(classId)
}
}
private fun generateAllOpenGeneratedClass(classId: ClassId): FirClassLikeSymbol<*>? {
override fun generateTopLevelClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
if (classId != GENERATED_CLASS_ID) return null
if (matchedClasses.isEmpty()) return null
return buildClass(classId).symbol
}
override fun generateNestedClassLikeDeclaration(owner: FirClassSymbol<*>, name: Name): FirClassLikeSymbol<*>? {
return when (val origin = owner.origin) {
is FirDeclarationOrigin.Plugin -> when (origin.key) {
Key -> generateNestedClass(owner.classId.createNestedClassId(name), owner)
else -> null
}
else -> null
}
}
override fun generateConstructors(context: MemberGenerationContext): List<FirConstructorSymbol> {
val classId = context.owner.classId
if (classId != GENERATED_CLASS_ID && classId !in classIdsForMatchedClasses) return emptyList()
return listOf(buildConstructor(classId, isInner = false, Key).symbol)
return listOf(buildConstructor(context.owner, isInner = false, Key).symbol)
}
private fun generateNestedClass(classId: ClassId, owner: FirClassSymbol<*>): FirClassLikeSymbol<*>? {
@@ -19,10 +19,7 @@ import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinTypeProjection
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
@@ -63,8 +60,9 @@ fun FirDeclarationGenerationExtension.buildMaterializeFunction(
// FIXME: this has to be shared
@OptIn(SymbolInternals::class)
fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner: Boolean, key: GeneratedDeclarationKey): FirConstructor {
val lookupTag = ConeClassLikeLookupTagImpl(classId)
fun FirDeclarationGenerationExtension.buildConstructor(owner: FirClassSymbol<*>, isInner: Boolean, key: GeneratedDeclarationKey): FirConstructor {
val classId = owner.classId
val lookupTag = owner.toLookupTag()
return buildPrimaryConstructor {
resolvePhase = FirResolvePhase.BODY_RESOLVE
moduleData = session.moduleData
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
abstract class AbstractTransformerForGenerator(protected val context: IrPluginContext) : IrElementVisitorVoid {
abstract class AbstractTransformerForGenerator(
protected val context: IrPluginContext,
private val visitBodies: Boolean
) : IrElementVisitorVoid {
protected val irFactory = context.irFactory
protected val irBuiltIns = context.irBuiltIns
@@ -29,24 +32,38 @@ abstract class AbstractTransformerForGenerator(protected val context: IrPluginCo
abstract fun generateBodyForConstructor(constructor: IrConstructor, key: GeneratedDeclarationKey): IrBody?
final override fun visitElement(element: IrElement) {
when (element) {
is IrDeclaration,
is IrFile,
is IrModuleFragment -> element.acceptChildrenVoid(this)
else -> {}
if (visitBodies) {
element.acceptChildrenVoid(this)
} else {
when (element) {
is IrDeclaration,
is IrFile,
is IrModuleFragment -> element.acceptChildrenVoid(this)
else -> {}
}
}
}
final override fun visitSimpleFunction(declaration: IrSimpleFunction) {
val origin = declaration.origin
if (origin !is GeneratedByPlugin || !interestedIn(origin.pluginKey)) return
if (origin !is GeneratedByPlugin || !interestedIn(origin.pluginKey)) {
if (visitBodies) {
visitElement(declaration)
}
return
}
require(declaration.body == null)
declaration.body = generateBodyForFunction(declaration, origin.pluginKey)
}
final override fun visitConstructor(declaration: IrConstructor) {
val origin = declaration.origin
if (origin !is GeneratedByPlugin || !interestedIn(origin.pluginKey)) return
if (origin !is GeneratedByPlugin || !interestedIn(origin.pluginKey)) {
if (visitBodies) {
visitElement(declaration)
}
return
}
require(declaration.body == null)
declaration.body = generateBodyForConstructor(declaration, origin.pluginKey)
}
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrBody
class TransformerForAdditionalMembersGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context) {
class TransformerForAdditionalMembersGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context, visitBodies = false) {
override fun interestedIn(key: GeneratedDeclarationKey): Boolean {
return key == AdditionalMembersGenerator.Key
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
class TransformerForCompanionGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context) {
class TransformerForCompanionGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context, visitBodies = true) {
override fun interestedIn(key: GeneratedDeclarationKey): Boolean {
return key == CompanionGenerator.Key
}
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrBody
class TransformerForExternalClassGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context) {
class TransformerForExternalClassGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context, visitBodies = false) {
override fun interestedIn(key: GeneratedDeclarationKey): Boolean {
return key == ExternalClassGenerator.Key
}
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.types.classFqName
class TransformerForTopLevelDeclarationsGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context) {
class TransformerForTopLevelDeclarationsGenerator(context: IrPluginContext) : AbstractTransformerForGenerator(context, visitBodies = false) {
override fun interestedIn(key: GeneratedDeclarationKey): Boolean {
return key == TopLevelDeclarationsGenerator.Key
}
@@ -0,0 +1,33 @@
FILE: localClassWithCompanionObject.kt
public final fun box(): R|kotlin/String| {
@R|org/jetbrains/kotlin/fir/plugin/CompanionWithFoo|() local final class SomeClass : R|kotlin/Any| {
public constructor(): R|SomeClass| {
super<R|kotlin/Any|>()
}
public final companion object Companion : R|kotlin/Any| {
public final fun foo(): R|kotlin/Int|
public constructor(): R|SomeClass.Companion|
}
}
local final fun takeInt(x: R|kotlin/Int|): R|kotlin/Unit| {
when () {
!=(R|<local>/x|, Int(10)) -> {
throw R|java/lang/IllegalArgumentException.IllegalArgumentException|()
}
}
}
local final fun test(): R|kotlin/Unit| {
R|<local>/takeInt|(Q|SomeClass|.R|/SomeClass.Companion.foo|())
R|<local>/takeInt|(Q|SomeClass.Companion|.R|/SomeClass.Companion.foo|())
}
R|<local>/test|()
^box String(OK)
}
@@ -0,0 +1,18 @@
import org.jetbrains.kotlin.fir.plugin.CompanionWithFoo
fun box(): String {
@CompanionWithFoo
class SomeClass
fun takeInt(x: Int) {
if (x != 10) throw IllegalArgumentException()
}
fun test() {
takeInt(SomeClass.foo())
takeInt(SomeClass.Companion.foo())
}
test()
return "OK"
}
@@ -0,0 +1,41 @@
FILE: localClassWithCompanionObject.kt
public final fun context(): R|kotlin/Unit| {
@R|org/jetbrains/kotlin/fir/plugin/CompanionWithFoo|() local final class SomeClass : R|kotlin/Any| {
public constructor(): R|SomeClass| {
super<R|kotlin/Any|>()
}
@R|org/jetbrains/kotlin/fir/plugin/CompanionWithFoo|() local final class Nested : R|kotlin/Any| {
public constructor(): R|SomeClass.Nested| {
super<R|kotlin/Any|>()
}
public final companion object Companion : R|kotlin/Any| {
public final fun foo(): R|kotlin/Int|
public constructor(): R|SomeClass.Nested.Companion|
}
}
public final companion object Companion : R|kotlin/Any| {
public final fun foo(): R|kotlin/Int|
public constructor(): R|SomeClass.Companion|
}
}
local final fun takeInt(x: R|kotlin/Int|): R|kotlin/Unit| {
}
local final fun test(): R|kotlin/Unit| {
R|<local>/takeInt|(Q|SomeClass|.R|/SomeClass.Companion.foo|())
R|<local>/takeInt|(Q|SomeClass.Companion|.R|/SomeClass.Companion.foo|())
R|<local>/takeInt|(Q|SomeClass.Nested|.R|/SomeClass.Nested.Companion.foo|())
R|<local>/takeInt|(Q|SomeClass.Nested.Companion|.R|/SomeClass.Nested.Companion.foo|())
}
}
@@ -0,0 +1,20 @@
import org.jetbrains.kotlin.fir.plugin.CompanionWithFoo
fun context() {
@CompanionWithFoo
class SomeClass {
@CompanionWithFoo
<!NESTED_CLASS_NOT_ALLOWED!>class Nested<!>
}
fun takeInt(x: Int) {}
fun test() {
takeInt(SomeClass.foo())
takeInt(SomeClass.Companion.foo())
takeInt(SomeClass.Nested.foo())
takeInt(SomeClass.Nested.Companion.foo())
}
}
@@ -43,6 +43,12 @@ public class FirPluginBlackBoxCodegenTestGenerated extends AbstractFirPluginBlac
runTest("plugins/fir-plugin-prototype/testData/box/generatedClassWithMembersAndNestedClasses.kt");
}
@Test
@TestMetadata("localClassWithCompanionObject.kt")
public void testLocalClassWithCompanionObject() throws Exception {
runTest("plugins/fir-plugin-prototype/testData/box/localClassWithCompanionObject.kt");
}
@Test
@TestMetadata("metaAnnotationFromLibrary.kt")
public void testMetaAnnotationFromLibrary() throws Exception {
@@ -73,6 +73,12 @@ public class FirPluginDiagnosticTestGenerated extends AbstractFirPluginDiagnosti
runTest("plugins/fir-plugin-prototype/testData/diagnostics/memberGen/generatedClassWithMembersAndNestedClasses.kt");
}
@Test
@TestMetadata("localClassWithCompanionObject.kt")
public void testLocalClassWithCompanionObject() throws Exception {
runTest("plugins/fir-plugin-prototype/testData/diagnostics/memberGen/localClassWithCompanionObject.kt");
}
@Test
@TestMetadata("topLevelCallables.kt")
public void testTopLevelCallables() throws Exception {
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.types.Variance
@OptIn(SymbolInternals::class)
fun FirDeclarationGenerationExtension.buildPrimaryConstructor(owner: FirClassSymbol<*>, isInner: Boolean, key: GeneratedDeclarationKey, status: FirDeclarationStatus): FirConstructor {
val classId = owner.classId
val lookupTag = ConeClassLikeLookupTagImpl(classId)
val lookupTag = owner.toLookupTag()
return buildPrimaryConstructor {
moduleData = session.moduleData
origin = key.origin
@@ -11,16 +11,16 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
import org.jetbrains.kotlin.fir.containingClassForLocalAttr
import org.jetbrains.kotlin.fir.copy
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.origin
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.extensions.*
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
@@ -68,19 +68,16 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
return result
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
return when (classId.shortClassName) {
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT -> {
generateCompanionDeclaration(classId)
}
SerialEntityNames.SERIALIZER_CLASS_NAME -> {
addSerializerImplClass(classId)
}
else -> error("Can't generate class ${classId.asSingleFqName()}")
override fun generateNestedClassLikeDeclaration(owner: FirClassSymbol<*>, name: Name): FirClassLikeSymbol<*>? {
if (owner !is FirRegularClassSymbol) return null
if (!session.predicateBasedProvider.matches(FirSerializationPredicates.annotatedWithSerializableOrMeta, owner)) return null
return when (name) {
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT -> generateCompanionDeclaration(owner)
SerialEntityNames.SERIALIZER_CLASS_NAME -> generateSerializerImplClass(owner)
else -> error("Can't generate class ${owner.classId.createNestedClassId(name).asSingleFqName()}")
}
}
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
val classId = classSymbol.classId
val result = mutableSetOf<Name>()
@@ -285,15 +282,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
return listOf(defaultObjectConstructor.symbol)
}
private fun getClassWithAnnotatedWithSerializableOrMeta(classId: ClassId?): FirRegularClassSymbol? {
if (classId == null) return null
return (session.symbolProvider.getClassLikeSymbolByClassId(classId) as? FirRegularClassSymbol)?.takeIf {
session.predicateBasedProvider.matches(FirSerializationPredicates.annotatedWithSerializableOrMeta, it)
}
}
fun addSerializerImplClass(classId: ClassId): FirClassLikeSymbol<*>? {
val owner = getClassWithAnnotatedWithSerializableOrMeta(classId.outerClassId) ?: return null
private fun generateSerializerImplClass(owner: FirRegularClassSymbol): FirClassLikeSymbol<*> {
val hasTypeParams = owner.typeParameterSymbols.isNotEmpty()
val serializerKind = if (hasTypeParams) ClassKind.CLASS else ClassKind.OBJECT
val serializerFirClass = buildRegularClass {
@@ -302,7 +291,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
classKind = serializerKind
scopeProvider = session.kotlinScopeProvider
name = SerialEntityNames.SERIALIZER_CLASS_NAME
symbol = FirRegularClassSymbol(classId)
symbol = FirRegularClassSymbol(owner.classId.createNestedClassId(name))
status = FirResolvedDeclarationStatusImpl(
Visibilities.Public,
Modality.FINAL,
@@ -325,13 +314,11 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
)
), isNullable = false
).toFirResolvedTypeRef()
}
}.also { it.initLocalAttributeIfNeeded(owner) }
return serializerFirClass.symbol
}
fun generateCompanionDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
if (classId.shortClassName != SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) return null
val owner = getClassWithAnnotatedWithSerializableOrMeta(classId.outerClassId) ?: return null
private fun generateCompanionDeclaration(owner: FirRegularClassSymbol): FirRegularClassSymbol? {
if (owner.companionObjectSymbol != null) return null
val regularClass = buildRegularClass {
moduleData = session.moduleData
@@ -346,16 +333,22 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
isCompanion = true
}
name = SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
symbol = FirRegularClassSymbol(classId)
symbol = FirRegularClassSymbol(owner.classId.createNestedClassId(name))
superTypeRefs += session.builtinTypes.anyType
if (with(session) { owner.companionNeedsSerializerFactory }) {
val serializerFactoryClassId = ClassId(SerializationPackages.internalPackageFqName, SERIALIZER_FACTORY_INTERFACE_NAME)
superTypeRefs += serializerFactoryClassId.constructClassLikeType(emptyArray(), false).toFirResolvedTypeRef()
}
}
}.also { it.initLocalAttributeIfNeeded(owner) }
return regularClass.symbol
}
private fun FirRegularClass.initLocalAttributeIfNeeded(owner: FirRegularClassSymbol) {
if (owner.isLocal) {
this.containingClassForLocalAttr = owner.toLookupTag()
}
}
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirSerializationPredicates.annotatedWithSerializableOrMeta, FirSerializationPredicates.hasMetaAnnotation)
}
@@ -0,0 +1,50 @@
FILE: serializerInLocalClass.kt
public final fun local(): R|kotlin/String| {
@R|kotlinx/serialization/Serializable|() local final data class Carrier : R|kotlin/Any| {
public constructor(i: R|kotlin/Int|): R|Carrier| {
super<R|kotlin/Any|>()
}
public final val i: R|kotlin/Int| = R|<local>/i|
public get(): R|kotlin/Int|
public final operator fun component1(): R|kotlin/Int|
public final fun copy(i: R|kotlin/Int| = this@R|/Carrier|.R|<local>/i|): R|Carrier|
public final companion object Companion : R|kotlin/Any| {
public final fun serializer(): R|kotlinx/serialization/KSerializer<Carrier>|
private constructor(): R|Carrier.Companion|
}
public final object $serializer : R|kotlinx/serialization/internal/GeneratedSerializer<Carrier>| {
public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|Carrier|): R|kotlin/Unit|
public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|Carrier|
public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|
public get(): R|kotlinx/serialization/descriptors/SerialDescriptor|
public final fun childSerializers(): R|kotlin/Array<kotlinx/serialization/KSerializer<*>>|
private constructor(): R|Carrier.$serializer|
}
}
^local Q|Carrier|.R|/Carrier.Companion.serializer|().R|SubstitutionOverride<kotlinx/serialization/KSerializer.descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor|>|.R|kotlin/Any.toString|()
}
public final fun box(): R|kotlin/String| {
lval expected: R|kotlin/String| = String(local.Carrier(i: kotlin.Int))
lval actual: R|kotlin/String| = R|/local|()
when () {
!=(R|<local>/expected|, R|<local>/actual|) -> {
^box <strcat>(String(Fail: ), R|<local>/actual|)
}
}
^box String(OK)
}
@@ -0,0 +1,20 @@
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
fun local(): String {
@Serializable
data class Carrier(val i: Int)
return Carrier.serializer().descriptor.toString()
}
fun box(): String {
val expected = "local.Carrier(i: kotlin.Int)"
val actual = local()
if (expected != actual) {
return "Fail: $actual"
}
return "OK"
}
@@ -265,6 +265,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
runTest("plugins/kotlinx-serialization/testData/firMembers/serializableWithCompanion.kt");
}
@Test
@TestMetadata("serializerInLocalClass.kt")
public void testSerializerInLocalClass() throws Exception {
runTest("plugins/kotlinx-serialization/testData/firMembers/serializerInLocalClass.kt");
}
@Test
@TestMetadata("serializerViaCompanion.kt")
public void testSerializerViaCompanion() throws Exception {
@@ -240,6 +240,12 @@ public class SerializationFirDiagnosticTestGenerated extends AbstractSerializati
runTest("plugins/kotlinx-serialization/testData/firMembers/serializableWithCompanion.kt");
}
@Test
@TestMetadata("serializerInLocalClass.kt")
public void testSerializerInLocalClass() throws Exception {
runTest("plugins/kotlinx-serialization/testData/firMembers/serializerInLocalClass.kt");
}
@Test
@TestMetadata("serializerViaCompanion.kt")
public void testSerializerViaCompanion() throws Exception {
@@ -78,11 +78,9 @@ class BuilderGenerator(session: FirSession) : FirDeclarationGenerationExtension(
return functionsCache.getValue(classSymbol)?.get(callableId.callableName).orEmpty().map { it.symbol }
}
override fun generateClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*>? {
val outerClassId = classId.outerClassId ?: return null
val outerClassSymbol = session.symbolProvider.getClassLikeSymbolByClassId(outerClassId) as? FirClassSymbol<*> ?: return null
if (!outerClassSymbol.isSuitableJavaClass()) return null
return builderClassCache.getValue(outerClassSymbol)?.symbol
override fun generateNestedClassLikeDeclaration(owner: FirClassSymbol<*>, name: Name): FirClassLikeSymbol<*>? {
if (!owner.isSuitableJavaClass()) return null
return builderClassCache.getValue(owner)?.symbol
}
private fun createFunctions(classSymbol: FirClassSymbol<*>): Map<Name, List<FirJavaMethod>>? {