From 5a1c2c56bd342fa5a8afa54dedee61a66e5f5066 Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Thu, 13 Oct 2022 15:25:39 +0000 Subject: [PATCH] Added support of external serializers to the FIR Co-authored-by: Leonid Startsev Merge-request: KT-MR-7334 Merged-by: Sergey Shanshin --- .../kotlin/fir/backend/Fir2IrPluginContext.kt | 2 + .../common/extensions/IrPluginContext.kt | 5 + .../common/extensions/IrPluginContextImpl.kt | 2 + .../compiler/backend/ir/IrPreGenerator.kt | 14 +- .../compiler/backend/ir/IrPredicates.kt | 15 +- .../ir/SerializableCompanionIrGenerator.kt | 2 +- .../backend/ir/SerializableIrGenerator.kt | 4 +- .../backend/ir/SerializerIrGenerator.kt | 15 +- .../SerializationLoweringExtension.kt | 1 + .../compiler/resolve/NamingConventions.kt | 2 + .../fir/SerializationFirResolveExtension.kt | 76 +++-- .../SerializationFirSupertypesExtension.kt | 37 ++- .../compiler/fir/SerializationFirUtils.kt | 19 +- .../FirSerializationPluginClassChecker.kt | 6 +- .../diagnostics/ExternalSerializers.fir.fail | 3 - .../diagnostics/ExternalSerializers.fir.kt | 9 - .../diagnostics/ExternalSerializers.kt | 1 + .../diagnostics/LazyRecursionBug.fir.kt | 9 - .../testData/diagnostics/LazyRecursionBug.kt | 1 + ...SerializerTypeCompatibleForSpecials.fir.kt | 18 -- .../SerializerTypeCompatibleForSpecials.kt | 1 + .../SerializerTypeIncompatible.fir.kt | 6 +- .../firMembers/externalSerializers.fir.txt | 55 ++++ .../firMembers/externalSerializers.kt | 27 ++ .../firMembers/serializerViaCompanion.fir.txt | 265 ++++++++++++++++++ .../firMembers/serializerViaCompanion.kt | 111 ++++++++ ...SerializationFirBlackBoxTestGenerated.java | 12 + ...rializationFirDiagnosticTestGenerated.java | 12 + 28 files changed, 629 insertions(+), 101 deletions(-) delete mode 100644 plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.fail delete mode 100644 plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.kt delete mode 100644 plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.fir.kt delete mode 100644 plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.fir.kt create mode 100644 plugins/kotlinx-serialization/testData/firMembers/externalSerializers.fir.txt create mode 100644 plugins/kotlinx-serialization/testData/firMembers/externalSerializers.kt create mode 100644 plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.fir.txt create mode 100644 plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt index b3a73d9c5f2..d004cb13828 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrPluginContext.kt @@ -52,6 +52,8 @@ class Fir2IrPluginContext( override val typeTranslator: TypeTranslator get() = error(ERROR_MESSAGE) + override val afterK2: Boolean = true + override val languageVersionSettings: LanguageVersionSettings get() = components.session.languageVersionSettings diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt index e9024921642..a38cdde8068 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContext.kt @@ -34,6 +34,11 @@ annotation class FirIncompatiblePluginAPI(val hint: String = "") interface IrPluginContext : IrGeneratorContext { val languageVersionSettings: LanguageVersionSettings + /** + * Indicates that the plugin works after FIR. Effectively it means that all descriptor-based API may contain incorrect and/or incomplete information, and declarations marked with `@FirIncompatibleApi` will throw runtime exceptions. + */ + val afterK2: Boolean + @ObsoleteDescriptorBasedAPI val moduleDescriptor: ModuleDescriptor diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt index 401a8ae14ab..86be0783fa8 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/extensions/IrPluginContextImpl.kt @@ -42,6 +42,8 @@ open class IrPluginContextImpl constructor( override val symbols: BuiltinSymbolsBase = BuiltinSymbolsBase(irBuiltIns, st) ) : IrPluginContext { + override val afterK2: Boolean = false + override val platform: TargetPlatform? = module.platform @OptIn(ObsoleteDescriptorBasedAPI::class) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPreGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPreGenerator.kt index 00227edf557..0e7a72e0c20 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPreGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPreGenerator.kt @@ -15,12 +15,9 @@ import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.companionObject import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.isInlineClass -import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.bitMaskSlotCount -import org.jetbrains.kotlinx.serialization.compiler.resolve.hasCompanionObjectAsSerializer /** * Generates only specific declarations, but NOT their bodies. @@ -43,7 +40,12 @@ class IrPreGenerator( private fun preGenerateWriteSelfMethodIfNeeded() { if (!irClass.isInternalSerializable) return val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return - if (!irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SerialEntityNames.SAVE) }) return + if (!irClass.shouldHaveSpecificSyntheticMethods { + serializerDescriptor.findPluginGeneratedMethod( + SerialEntityNames.SAVE, + compilerContext.afterK2 + ) + }) return if (irClass.findWriteSelfMethod() != null) return val method = irClass.addFunction { name = SerialEntityNames.WRITE_SELF_NAME @@ -95,7 +97,7 @@ class IrPreGenerator( if (!irClass.isInternalSerializable) return // do not add synthetic deserialization constructor if .deserialize method is customized if (irClass.hasCompanionObjectAsSerializer && irClass.companionObject() - ?.findPluginGeneratedMethod(SerialEntityNames.LOAD) == null + ?.findPluginGeneratedMethod(SerialEntityNames.LOAD, compilerContext.afterK2) == null ) return if (irClass.isValue) return if (irClass.findSerializableSyntheticConstructor() != null) return @@ -133,4 +135,4 @@ class IrPreGenerator( } } -} \ No newline at end of file +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt index 7958337bd88..f70b9637bb8 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt @@ -57,9 +57,9 @@ internal val IrClass.isInternallySerializableObject: Boolean get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs() -internal fun IrClass.findPluginGeneratedMethod(name: String): IrSimpleFunction? { +internal fun IrClass.findPluginGeneratedMethod(name: String, afterK2: Boolean): IrSimpleFunction? { return this.functions.find { - it.name.asString() == name && it.isFromPlugin() + it.name.asString() == name && it.isFromPlugin(afterK2) } } @@ -147,8 +147,13 @@ fun IrClass.getSuperClassNotAny(): IrClass? { } @OptIn(ObsoleteDescriptorBasedAPI::class) -internal fun IrDeclaration.isFromPlugin(): Boolean = - this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) || (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED // old FE doesn't specify origin +internal fun IrDeclaration.isFromPlugin(afterK2: Boolean): Boolean = + if (afterK2) { + this.origin == IrDeclarationOrigin.GeneratedByPlugin(SerializationPluginKey) + } else { + // old FE doesn't specify custom origin + (this.descriptor as? CallableMemberDescriptor)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED + } internal fun IrConstructor.isSerializationCtor(): Boolean { /*kind == CallableMemberDescriptor.Kind.SYNTHESIZED does not work because DeserializedClassConstructorDescriptor loses its kind*/ @@ -248,4 +253,4 @@ fun IrSimpleType.argumentTypesOrUpperBounds(): List { } internal inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) = - !isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null) \ No newline at end of file + !isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index 763dcc6dc28..c360b15528a 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -157,7 +157,7 @@ class SerializableCompanionIrGenerator( it.valueParameters.size == 1 && it.valueParameters.first().isVararg && it.returnType.isKSerializer() - && it.isFromPlugin() + && it.isFromPlugin(compilerContext.afterK2) }?.let { return it } val kSerializerStarType = compilerContext.getClassFromRuntime(SerialEntityNames.KSERIALIZER_CLASS).starProjectedType val f = irClass.addFunction( diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index 7db4fd082b4..74402ec6aaa 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -359,7 +359,7 @@ class SerializableIrGenerator( private fun generateSyntheticInternalConstructor() { val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return - if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(LOAD) }) { + if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(LOAD, compilerContext.afterK2) }) { val constrDesc = irClass.constructors.find(IrConstructor::isSerializationCtor) ?: return generateInternalConstructor(constrDesc) } @@ -367,7 +367,7 @@ class SerializableIrGenerator( private fun generateSyntheticMethods() { val serializerDescriptor = irClass.classSerializer(compilerContext)?.owner ?: return - if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SAVE) }) { + if (irClass.shouldHaveSpecificSyntheticMethods { serializerDescriptor.findPluginGeneratedMethod(SAVE, compilerContext.afterK2) }) { val func = irClass.findWriteSelfMethod() ?: return func.origin = SERIALIZATION_PLUGIN_ORIGIN generateWriteSelfMethod(func) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 618e4b42225..86a0a7fe820 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin @@ -63,7 +62,7 @@ open class SerializerIrGenerator( protected val generatedSerialDescPropertyDescriptor = getProperty( SerialEntityNames.SERIAL_DESC_FIELD, { true } - )?.takeIf { it.isFromPlugin() } + )?.takeIf { it.isFromPlugin(compilerContext.afterK2) } protected val anySerialDescProperty = getProperty( SerialEntityNames.SERIAL_DESC_FIELD, @@ -592,15 +591,17 @@ open class SerializerIrGenerator( val prop = generatedSerialDescPropertyDescriptor?.let { generateSerializableClassProperty(it); true } ?: false if (prop) generateSerialDesc() - val save = irClass.findPluginGeneratedMethod(SAVE)?.let { generateSave(it); true } ?: false - val load = irClass.findPluginGeneratedMethod(LOAD)?.let { generateLoad(it); true } ?: false - irClass.findPluginGeneratedMethod(SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier)?.let { generateChildSerializersGetter(it) } - irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier) + val withFir = compilerContext.afterK2 + val save = irClass.findPluginGeneratedMethod(SAVE, withFir)?.let { generateSave(it); true } ?: false + val load = irClass.findPluginGeneratedMethod(LOAD, withFir)?.let { generateLoad(it); true } ?: false + irClass.findPluginGeneratedMethod(SerialEntityNames.CHILD_SERIALIZERS_GETTER.identifier, withFir) + ?.let { generateChildSerializersGetter(it) } + irClass.findPluginGeneratedMethod(SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER.identifier, withFir) ?.let { generateTypeParamsSerializersGetter(it) } if (!prop && (save || load)) generateSerialDesc() if (serializableIrClass.typeParameters.isNotEmpty()) { - findSerializerConstructorForTypeArgumentsSerializers(irClass)?.takeIf { it.owner.isFromPlugin() }?.let { + findSerializerConstructorForTypeArgumentsSerializers(irClass)?.takeIf { it.owner.isFromPlugin(withFir) }?.let { generateGenericFieldsAndConstructor(it.owner) } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt index e41e5b0e165..6872e3ba861 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationLoweringExtension.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.CompilationException import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrIntrinsicExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.fileParent diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index bf47e60096a..45e5a29f646 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -198,12 +198,14 @@ object SerializationDependencies { } object SerializersClassIds { + val kSerializerId = ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME) val enumSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.enumSerializer)) val polymorphicSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.polymorphicSerializer)) val referenceArraySerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.referenceArraySerializer)) val objectSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.objectSerializer)) val sealedSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.sealedSerializer)) val contextSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.contextSerializer)) + val generatedSerializerId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS) } fun findStandardKotlinTypeSerializerName(typeName: String?): String? { diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index 150f0e5d7cd..51eb3323dd7 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -39,9 +39,8 @@ import org.jetbrains.kotlin.platform.konan.isNative import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_FACTORY_INTERFACE_NAME import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages - -val generatedSerializerClassId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS) -val kSerializerClassId = ClassId(SerializationPackages.packageFqName, SerialEntityNames.KSERIALIZER_NAME) +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.generatedSerializerId +import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.kSerializerId class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGenerationExtension(session) { @@ -62,7 +61,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene with(session) { if (classSymbol.shouldHaveGeneratedMethodsInCompanion && !classSymbol.isSerializableObject) result += SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT - if (classSymbol.shouldHaveGeneratedSerializer(!runtimeHasEnumSerializerFactory) /* TODO && !classSymbol.hasCompanionObjectAsSerializer*/) + if (classSymbol.shouldHaveGeneratedSerializer && !classSymbol.hasCompanionObjectAsSerializer) result += SerialEntityNames.SERIALIZER_CLASS_NAME } @@ -85,31 +84,24 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set { val classId = classSymbol.classId val result = mutableSetOf() - when (classId.shortClassName) { - SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT -> { - val origin = classSymbol.origin as? FirDeclarationOrigin.Plugin - if (origin?.key == SerializationPluginKey) { - result += SpecialNames.INIT - result += SerialEntityNames.SERIALIZER_PROVIDER_NAME - } else { - // TODO: handle user-written companions & named companions - } + + val origin = classSymbol.origin as? FirDeclarationOrigin.Plugin + + when { + classSymbol.isCompanion && origin?.key == SerializationPluginKey -> { + result += SpecialNames.INIT + result += SerialEntityNames.SERIALIZER_PROVIDER_NAME } - SerialEntityNames.SERIALIZER_CLASS_NAME -> { + + classId.shortClassName == SerialEntityNames.SERIALIZER_CLASS_NAME -> { // TODO: check classSymbol for already added functions - // TODO: support user-defined serializers? result += setOf( SpecialNames.INIT, SerialEntityNames.SAVE_NAME, SerialEntityNames.LOAD_NAME, SerialEntityNames.SERIAL_DESC_FIELD_NAME ) - if (classSymbol.resolvedSuperTypes.any { - it.classId == ClassId( - SerializationPackages.internalPackageFqName, - SerialEntityNames.GENERATED_SERIALIZER_CLASS - ) - }) { + if (classSymbol.resolvedSuperTypes.any { it.classId == generatedSerializerId }) { result += SerialEntityNames.CHILD_SERIALIZERS_GETTER if (classSymbol.typeParameterSymbols.isNotEmpty()) { @@ -118,7 +110,28 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene } } - else -> if (with(session) { classSymbol.isSerializableObject }) result += SerialEntityNames.SERIALIZER_PROVIDER_NAME + classSymbol.isExternalSerializer -> { + if (classSymbol.declarationSymbols.filterIsInstance() + .none { it.name == SerialEntityNames.SERIAL_DESC_FIELD_NAME } + ) { + result += SerialEntityNames.SERIAL_DESC_FIELD_NAME + } + + if (classSymbol.isCompanion) result += SerialEntityNames.SERIALIZER_PROVIDER_NAME + + if (classSymbol.declarationSymbols.filterIsInstance() + .none { it.name == SerialEntityNames.SAVE_NAME } + ) { + result += SerialEntityNames.SAVE_NAME // TODO how check parameters?! + } + if (classSymbol.declarationSymbols.filterIsInstance() + .none { it.name == SerialEntityNames.LOAD_NAME } + ) { + result += SerialEntityNames.LOAD_NAME // TODO how check parameters?! + } + } + + with(session) { classSymbol.isSerializableObject } -> result += SerialEntityNames.SERIALIZER_PROVIDER_NAME } return result } @@ -135,7 +148,6 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene return targets.singleOrNull() ?: error("Multiple overrides found for ${callableId.callableName}") } - // TODO: support @Serializer(for) @OptIn(SymbolInternals::class) override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List { val owner = context?.owner ?: return emptyList() @@ -150,7 +162,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene serializableClass ?: return emptyList() return listOf(generateSerializerGetterInCompanion(owner, serializableClass, callableId)) } - if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList() + if (!owner.isSerializer) return emptyList() if (callableId.callableName !in setOf( SpecialNames.INIT, SerialEntityNames.SAVE_NAME, @@ -193,14 +205,14 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene valueParameters.addAll(List(serializableClassSymbol.typeParameterSymbols.size) { i -> newSimpleValueParameter( session, - kSerializerClassId.constructClassLikeType(arrayOf(parametersAsArguments[i]), false).toFirResolvedTypeRef(), + kSerializerId.constructClassLikeType(arrayOf(parametersAsArguments[i]), false).toFirResolvedTypeRef(), Name.identifier("${SerialEntityNames.typeArgPrefix}$i") ) }) returnTypeRef = buildResolvedTypeRef { - type = kSerializerClassId.constructClassLikeType( + type = kSerializerId.constructClassLikeType( arrayOf(serializableClassSymbol.constructType(parametersAsArguments, false)), isNullable = false ) @@ -212,7 +224,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene @OptIn(SymbolInternals::class) override fun generateProperties(callableId: CallableId, context: MemberGenerationContext?): List { val owner = context?.owner ?: return emptyList() - if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList() + if (!owner.isSerializer) return emptyList() if (callableId.callableName != SerialEntityNames.SERIAL_DESC_FIELD_NAME) return emptyList() val target = getFromSupertype(callableId, owner) { it.getProperties(callableId.callableName).filterIsInstance() } @@ -265,7 +277,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene valueParameters.addAll(owner.typeParameterSymbols.mapIndexed { i, typeParam -> newSimpleValueParameter( session, - kSerializerClassId.constructClassLikeType(arrayOf(typeParam.toConeType()), false).toFirResolvedTypeRef(), + kSerializerId.constructClassLikeType(arrayOf(typeParam.toConeType()), false).toFirResolvedTypeRef(), Name.identifier("${SerialEntityNames.typeArgPrefix}$i") ) }) @@ -302,7 +314,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene }) val parametersAsArguments = typeParameters.map { it.toConeType() }.toTypedArray() - superTypeRefs += generatedSerializerClassId.constructClassLikeType( + superTypeRefs += generatedSerializerId.constructClassLikeType( arrayOf( owner.constructType( parametersAsArguments, @@ -345,6 +357,12 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene register(FirSerializationPredicates.annotatedWithSerializableOrMeta, FirSerializationPredicates.hasMetaAnnotation) } + private val FirClassSymbol<*>.isSerializer: Boolean + get() = name == SerialEntityNames.SERIALIZER_CLASS_NAME || isExternalSerializer + + private val FirClassSymbol<*>.isExternalSerializer: Boolean + get() = session.predicateBasedProvider.matches(FirSerializationPredicates.serializerFor, this) + context(FirSession) @Suppress("IncorrectFormatting") // KTIJ-22227 private val FirClassSymbol<*>.companionNeedsSerializerFactory: Boolean diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt index 66223cab318..324432c2025 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirSupertypesExtension.kt @@ -7,12 +7,21 @@ package org.jetbrains.kotlinx.serialization.compiler.fir import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirGetClassCall +import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension +import org.jetbrains.kotlin.fir.extensions.buildUserTypeFromQualifierParts import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider +import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference +import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.constructClassLikeType import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlinx.serialization.compiler.fir.FirSerializationPredicates.serializerFor import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages @@ -36,9 +45,33 @@ class SerializationFirSupertypesExtension(session: FirSession) : FirSupertypeGen val generatedSerializerClassId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS) if (resolvedSupertypes.any { it.type.classId == kSerializerClassId || it.type.classId == generatedSerializerClassId }) return emptyList() - return if (session.predicateBasedProvider.matches(serializerFor, classLikeDeclaration)) { - TODO("Support @Serializer(for=...) supertype generation") + val getClassArgument = classLikeDeclaration.serializerFor ?: return emptyList() + val serializerConeType = resolveConeTypeFromArgument(getClassArgument) + + listOf( + buildResolvedTypeRef { + type = kSerializerClassId.constructClassLikeType(arrayOf(serializerConeType), isNullable = false) + } + ) } else emptyList() } + + // Function helps to resolve class call from annotation argument to `ConeKotlinType` + context(TypeResolveServiceContainer) + @Suppress("IncorrectFormatting") // KTIJ-22227 + private fun resolveConeTypeFromArgument(getClassCall: FirGetClassCall): ConeKotlinType { + val typeToResolve = buildUserTypeFromQualifierParts(isMarkedNullable = false) { + fun visitQualifiers(expression: FirExpression) { + if (expression !is FirPropertyAccessExpression) return + expression.explicitReceiver?.let { visitQualifiers(it) } + expression.qualifierName?.let { part(it) } + } + visitQualifiers(getClassCall.argument) + } + return typeResolver.resolveUserType(typeToResolve).type + } + + private val FirPropertyAccessExpression.qualifierName: Name? + get() = (calleeReference as? FirSimpleNamedReference)?.name } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt index 3da9b49f880..0e43c0bfe31 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.arguments @@ -85,6 +86,10 @@ internal val FirBasedSymbol<*>.serializableWith: ConeKotlinType? internal val List.serializableWith: ConeKotlinType? get() = serializableAnnotation()?.getKClassArgument(AnnotationParameterNames.WITH) +fun FirAnnotation.getGetKClassArgument(name: Name): FirGetClassCall? { + return findArgumentByName(name) as? FirGetClassCall +} + internal val FirClassSymbol<*>.serializerAnnotation: FirAnnotation? get() = getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId) @@ -94,6 +99,15 @@ internal val FirClassSymbol<*>.serializerForClass: ConeKotlinType? .getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId) ?.getKClassArgument(AnnotationParameterNames.FOR_CLASS) +internal val FirClassSymbol<*>.serializerFor: FirGetClassCall? + get() = resolvedAnnotationsWithArguments + .getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId) + ?.getGetKClassArgument(AnnotationParameterNames.FOR_CLASS) + +internal val FirClassLikeDeclaration.serializerFor: FirGetClassCall? + get() = getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId) + ?.getGetKClassArgument(AnnotationParameterNames.FOR_CLASS) + context(FirSession) @Suppress("IncorrectFormatting") // KTIJ-22227 internal val FirClassSymbol<*>.isInternallySerializableObject: Boolean @@ -172,7 +186,8 @@ val FirClassSymbol<*>.hasCompanionObjectAsSerializer: Boolean context(FirSession) @Suppress("IncorrectFormatting") // KTIJ-22227 val FirClassSymbol<*>.hasCustomSerializerOnCompanion: Boolean - get() = (this as? FirRegularClassSymbol)?.companionObjectSymbol?.serializerForClass != null +// TODO It was accepted that the annotation on the companion can take only the current class as an argument. Before the release of FIR, we need to add a new annotation and check gere for its presence + get() = (this as? FirRegularClassSymbol)?.companionObjectSymbol?.serializerFor != null context(FirSession) val FirClassSymbol<*>.isEnumWithLegacyGeneratedSerializer: Boolean @@ -180,7 +195,7 @@ val FirClassSymbol<*>.isEnumWithLegacyGeneratedSerializer: Boolean context(FirSession) val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean - get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isEnumWithLegacyGeneratedSerializer + get() = (isInternalSerializable && isFinalOrOpen()) || isEnumWithLegacyGeneratedSerializer context(FirSession) @Suppress("IncorrectFormatting") // KTIJ-22227 diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt index d82294e1bf1..d128efe5771 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt @@ -7,9 +7,7 @@ package org.jetbrains.kotlinx.serialization.compiler.fir.checkers import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.descriptors.isEnumClass import org.jetbrains.kotlin.diagnostics.* -import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass @@ -20,7 +18,6 @@ import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.* @@ -33,7 +30,6 @@ import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySeria import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSerializerOrContextUnchecked import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider import org.jetbrains.kotlinx.serialization.compiler.fir.services.versionReader -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations object FirSerializationPluginClassChecker : FirClassChecker() { private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable")) @@ -64,7 +60,7 @@ object FirSerializationPluginClassChecker : FirClassChecker() { private fun checkExternalSerializer(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) { val serializableKType = classSymbol.serializerForClass ?: return val serializableClassSymbol = serializableKType.toRegularClassSymbol(session) ?: return - val properties = session.serializablePropertiesProvider.getSerializablePropertiesForClass(classSymbol) + val properties = session.serializablePropertiesProvider.getSerializablePropertiesForClass(serializableClassSymbol) if (!properties.isExternallySerializable) { val source = classSymbol.serializerAnnotation?.source ?: classSymbol.source val error = if (serializableClassSymbol.moduleData == session.moduleData) { diff --git a/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.fail b/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.fail deleted file mode 100644 index 88f7edcbc38..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.fail +++ /dev/null @@ -1,3 +0,0 @@ -An operation is not implemented: Support @Serializer(for=...) supertype generation -kotlin.NotImplementedError: An operation is not implemented: Support @Serializer(for=...) supertype generation - at org.jetbrains.kotlinx.serialization.compiler.fir.SerializationFirSupertypesExtension.computeAdditionalSupertypes(SerializationFirSupertypesExtension.kt:39) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.kt deleted file mode 100644 index 97831db6625..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -// SKIP_TXT - -// FILE: test.kt -import kotlinx.serialization.* - -class Foo(i: Int, val j: Int) - -@Serializer(forClass = Foo::class) -object ExternalSerializer diff --git a/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.kt b/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.kt index 81702f971a8..d7d9aa291a1 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/ExternalSerializers.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT // FILE: test.kt diff --git a/plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.fir.kt deleted file mode 100644 index afce707a688..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -// This test enshures that analysis ends up without compiler exceptions - -import kotlinx.serialization.* - -@Serializable -class Digest() { - @Serializer(forClass = Digest::class) - companion object : KSerializer {} -} diff --git a/plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.kt b/plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.kt index d2455d7f342..54f03ae0437 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/LazyRecursionBug.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // This test enshures that analysis ends up without compiler exceptions import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.fir.kt deleted file mode 100644 index f88873b8dba..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -// WITH_STDLIB -// SKIP_TXT - -import kotlinx.serialization.* - -class Bar -@Serializer(forClass = Bar::class) -object BarSerializer: KSerializer - -class Baz -@Serializer(forClass = Baz::class) -object BazSerializer: KSerializer - -@Serializable -class Foo1(@Polymorphic val i: Baz) - -@Serializable -class Foo2(val li: MutableList<@Serializable(with = BazSerializer::class) Baz>) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.kt index 037cdaa70f0..bedfcbe0549 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeCompatibleForSpecials.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // SKIP_TXT diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt index 0f49c9e3195..2f35ebe2574 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt @@ -5,13 +5,13 @@ import kotlinx.serialization.* class Bar @Serializer(forClass = Bar::class) -object BarSerializer: KSerializer +object BarSerializer: KSerializer class Baz @Serializer(forClass = Baz::class) -object BazSerializer: KSerializer +object BazSerializer: KSerializer @Serializer(forClass = Baz::class) -object NullableBazSerializer: KSerializer +object NullableBazSerializer: KSerializer @Serializable(with = BazSerializer::class) class Biz(val i: Int) diff --git a/plugins/kotlinx-serialization/testData/firMembers/externalSerializers.fir.txt b/plugins/kotlinx-serialization/testData/firMembers/externalSerializers.fir.txt new file mode 100644 index 00000000000..a2eca7b50cc --- /dev/null +++ b/plugins/kotlinx-serialization/testData/firMembers/externalSerializers.fir.txt @@ -0,0 +1,55 @@ +FILE: externalSerializers.kt + package com.example + + public final class Data : R|kotlin/Any| { + public constructor(j: R|kotlin/Int|): R|com/example/Data| { + super() + } + + public final val j: R|kotlin/Int| = R|/j| + public get(): R|kotlin/Int| + + } + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/Data|)) public final object ObjectSerializer : R|kotlin/Any|, R|kotlinx/serialization/KSerializer| { + private constructor(): R|com/example/ObjectSerializer| { + super() + } + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/Data|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/Data| + + } + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/Data|)) public final class ClassSerializer : R|kotlin/Any|, R|kotlinx/serialization/KSerializer| { + public constructor(): R|com/example/ClassSerializer| { + super() + } + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/Data|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/Data| + + } + public final fun box(): R|kotlin/String| { + lval encodedForClass: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(R|com/example/ClassSerializer.ClassSerializer|(), R|com/example/Data.Data|(Int(1))) + when () { + !=(R|/encodedForClass|, String({"j":1})) -> { + ^box R|/encodedForClass| + } + } + + lval encodedForObject: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(Q|com/example/ObjectSerializer|, R|com/example/Data.Data|(Int(2))) + when () { + !=(R|/encodedForObject|, String({"j":2})) -> { + ^box R|/encodedForObject| + } + } + + ^box String(OK) + } diff --git a/plugins/kotlinx-serialization/testData/firMembers/externalSerializers.kt b/plugins/kotlinx-serialization/testData/firMembers/externalSerializers.kt new file mode 100644 index 00000000000..ea980821ab6 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/firMembers/externalSerializers.kt @@ -0,0 +1,27 @@ +// WITH_STDLIB + +package com.example + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.descriptors.* + + +class Data(val j: Int) + +@Serializer(forClass = Data::class) +object ObjectSerializer + +@Serializer(forClass = Data::class) +class ClassSerializer + +fun box(): String { + val encodedForClass = Json.encodeToString(ClassSerializer(), Data(1)) + if (encodedForClass != """{"j":1}""") return encodedForClass + + val encodedForObject = Json.encodeToString(ObjectSerializer, Data(2)) + if (encodedForObject != """{"j":2}""") return encodedForObject + + return "OK" +} diff --git a/plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.fir.txt b/plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.fir.txt new file mode 100644 index 00000000000..e404a45158c --- /dev/null +++ b/plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.fir.txt @@ -0,0 +1,265 @@ +FILE: serializerViaCompanion.kt + package com.example + + @R|kotlinx/serialization/Serializable|() public final data class WithCompanion : R|kotlin/Any| { + public constructor(i: R|kotlin/Int|): R|com/example/WithCompanion| { + super() + } + + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/WithCompanion|)) public final companion object Companion : R|kotlin/Any|, R|kotlinx/serialization/KSerializer| { + private constructor(): R|com/example/WithCompanion.Companion| { + super() + } + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/WithCompanion|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/WithCompanion| + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + + public final operator fun component1(): R|kotlin/Int| + + public final fun copy(i: R|kotlin/Int| = this@R|com/example/WithCompanion|.R|com/example/WithCompanion.i|): R|com/example/WithCompanion| + + } + @R|kotlinx/serialization/Serializable|() public final data class WithNamedCompanion : R|kotlin/Any| { + public constructor(i: R|kotlin/Int|): R|com/example/WithNamedCompanion| { + super() + } + + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/WithNamedCompanion|)) public final companion object Named : R|kotlin/Any|, R|kotlinx/serialization/KSerializer| { + private constructor(): R|com/example/WithNamedCompanion.Named| { + super() + } + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/WithNamedCompanion|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/WithNamedCompanion| + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + + public final operator fun component1(): R|kotlin/Int| + + public final fun copy(i: R|kotlin/Int| = this@R|com/example/WithNamedCompanion|.R|com/example/WithNamedCompanion.i|): R|com/example/WithNamedCompanion| + + } + @R|kotlinx/serialization/Serializable|() public final data class WithExplicitType : R|kotlin/Any| { + public constructor(i: R|kotlin/Int|): R|com/example/WithExplicitType| { + super() + } + + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/WithExplicitType|)) public final companion object Companion : R|kotlinx/serialization/KSerializer| { + private constructor(): R|com/example/WithExplicitType.Companion| { + super() + } + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/WithExplicitType|): R|kotlin/Unit| + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/WithExplicitType| + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + + public final operator fun component1(): R|kotlin/Int| + + public final fun copy(i: R|kotlin/Int| = this@R|com/example/WithExplicitType|.R|com/example/WithExplicitType.i|): R|com/example/WithExplicitType| + + } + @R|kotlinx/serialization/Serializable|() public final data class PartiallyOverridden : R|kotlin/Any| { + public constructor(i: R|kotlin/Int|): R|com/example/PartiallyOverridden| { + super() + } + + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/PartiallyOverridden|)) public final companion object Companion : R|kotlinx/serialization/KSerializer| { + private constructor(): R|com/example/PartiallyOverridden.Companion| { + super() + } + + public final override val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| = R|kotlinx/serialization/descriptors/buildClassSerialDescriptor|(String(Partially-Overridden), = buildClassSerialDescriptor@fun R|kotlinx/serialization/descriptors/ClassSerialDescriptorBuilder|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|kotlinx/serialization/descriptors/ClassSerialDescriptorBuilder.element|(String(i), R|kotlinx/serialization/descriptors/PrimitiveSerialDescriptor|(String(i), Q|kotlinx/serialization/descriptors/PrimitiveKind.INT|)) + } + ) + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final override fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/PartiallyOverridden|): R|kotlin/Unit| { + lval compositeOutput: R|kotlinx/serialization/encoding/CompositeEncoder| = R|/encoder|.R|kotlinx/serialization/encoding/Encoder.beginStructure|(Q|com/example/PartiallyOverridden|.R|com/example/PartiallyOverridden.Companion.descriptor|) + R|/compositeOutput|.R|kotlinx/serialization/encoding/CompositeEncoder.encodeIntElement|(Q|com/example/PartiallyOverridden|.R|com/example/PartiallyOverridden.Companion.descriptor|, Int(0), R|/value|.R|com/example/PartiallyOverridden.i|.R|kotlin/Int.plus|(Int(10))) + R|/compositeOutput|.R|kotlinx/serialization/encoding/CompositeEncoder.endStructure|(Q|com/example/PartiallyOverridden|.R|com/example/PartiallyOverridden.Companion.descriptor|) + } + + public final fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/PartiallyOverridden| + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + + public final operator fun component1(): R|kotlin/Int| + + public final fun copy(i: R|kotlin/Int| = this@R|com/example/PartiallyOverridden|.R|com/example/PartiallyOverridden.i|): R|com/example/PartiallyOverridden| + + } + @R|kotlinx/serialization/Serializable|() public final data class PartiallyWithoutType : R|kotlin/Any| { + public constructor(i: R|kotlin/Int|): R|com/example/PartiallyWithoutType| { + super() + } + + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/PartiallyWithoutType|)) public final companion object Companion : R|kotlin/Any|, R|kotlinx/serialization/KSerializer| { + private constructor(): R|com/example/PartiallyWithoutType.Companion| { + super() + } + + public final override fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/PartiallyWithoutType| { + lval dec: R|kotlinx/serialization/encoding/CompositeDecoder| = R|/decoder|.R|kotlinx/serialization/encoding/Decoder.beginStructure|(this@R|com/example/PartiallyWithoutType.Companion|.R|com/example/PartiallyWithoutType.Companion.descriptor|) + lvar iv: R|kotlin/Int?| = Null(null) + loop@while(Boolean(true)) { + when (lval i: R|kotlin/Int| = R|/dec|.R|kotlinx/serialization/encoding/CompositeDecoder.decodeElementIndex|(this@R|com/example/PartiallyWithoutType.Companion|.R|com/example/PartiallyWithoutType.Companion.descriptor|)) { + ==($subj$, Q|kotlinx/serialization/encoding/CompositeDecoder|.R|kotlinx/serialization/encoding/CompositeDecoder.Companion.DECODE_DONE|) -> { + break@@@[Boolean(true)] + } + ==($subj$, Int(0)) -> { + R|/iv| = R|/dec|.R|kotlinx/serialization/encoding/CompositeDecoder.decodeIntElement|(this@R|com/example/PartiallyWithoutType.Companion|.R|com/example/PartiallyWithoutType.Companion.descriptor|, R|/i|) + } + else -> { + throw R|kotlinx/serialization/SerializationException.SerializationException|((String(Unknown index ), R|/i|)) + } + } + + } + + R|/dec|.R|kotlinx/serialization/encoding/CompositeDecoder.endStructure|(this@R|com/example/PartiallyWithoutType.Companion|.R|com/example/PartiallyWithoutType.Companion.descriptor|) + ^deserialize R|com/example/PartiallyWithoutType.PartiallyWithoutType|(R|/iv|!!.R|kotlin/Int.plus|(Int(10))) + } + + public final val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/PartiallyWithoutType|): R|kotlin/Unit| + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + + public final operator fun component1(): R|kotlin/Int| + + public final fun copy(i: R|kotlin/Int| = this@R|com/example/PartiallyWithoutType|.R|com/example/PartiallyWithoutType.i|): R|com/example/PartiallyWithoutType| + + } + @R|kotlinx/serialization/Serializable|() public final data class FullyOverridden : R|kotlin/Any| { + public constructor(i: R|kotlin/Int|): R|com/example/FullyOverridden| { + super() + } + + public final val i: R|kotlin/Int| = R|/i| + public get(): R|kotlin/Int| + + @R|kotlinx/serialization/Serializer|(forClass = (Q|com/example/FullyOverridden|)) public final companion object Companion : R|kotlinx/serialization/KSerializer| { + private constructor(): R|com/example/FullyOverridden.Companion| { + super() + } + + public final override val descriptor: R|kotlinx/serialization/descriptors/SerialDescriptor| = R|kotlinx/serialization/descriptors/PrimitiveSerialDescriptor|(String(FullyOverridden), Q|kotlinx/serialization/descriptors/PrimitiveKind.STRING|) + public get(): R|kotlinx/serialization/descriptors/SerialDescriptor| + + public final override fun serialize(encoder: R|kotlinx/serialization/encoding/Encoder|, value: R|com/example/FullyOverridden|): R|kotlin/Unit| { + R|/encoder|.R|kotlinx/serialization/encoding/Encoder.encodeString|((String(i=), R|/value|.R|com/example/FullyOverridden.i|)) + } + + public final override fun deserialize(decoder: R|kotlinx/serialization/encoding/Decoder|): R|com/example/FullyOverridden| { + lval i: R|kotlin/Int| = R|/decoder|.R|kotlinx/serialization/encoding/Decoder.decodeString|().R|kotlin/text/substringAfter|(Char(=)).R|kotlin/text/toInt|() + ^deserialize R|com/example/FullyOverridden.FullyOverridden|(R|/i|) + } + + public final fun serializer(): R|kotlinx/serialization/KSerializer| + + } + + public final operator fun component1(): R|kotlin/Int| + + public final fun copy(i: R|kotlin/Int| = this@R|com/example/FullyOverridden|.R|com/example/FullyOverridden.i|): R|com/example/FullyOverridden| + + } + public final fun box(): R|kotlin/String| { + R|com/example/encodeAndDecode|(Q|com/example/WithCompanion|.R|com/example/WithCompanion.Companion.serializer|(), R|com/example/WithCompanion.WithCompanion|(Int(1)), String({"i":1}))?.{ $subj$.R|kotlin/let|( = let@fun (it: R|kotlin/String|): R|kotlin/Nothing| { + ^box R|/it| + } + ) } + R|com/example/encodeAndDecode|(Q|com/example/WithNamedCompanion|.R|com/example/WithNamedCompanion.Named.serializer|(), R|com/example/WithNamedCompanion.WithNamedCompanion|(Int(2)), String({"i":2}))?.{ $subj$.R|kotlin/let|( = let@fun (it: R|kotlin/String|): R|kotlin/Nothing| { + ^box R|/it| + } + ) } + R|com/example/encodeAndDecode|(Q|com/example/WithExplicitType|.R|com/example/WithExplicitType.Companion.serializer|(), R|com/example/WithExplicitType.WithExplicitType|(Int(3)), String({"i":3}))?.{ $subj$.R|kotlin/let|( = let@fun (it: R|kotlin/String|): R|kotlin/Nothing| { + ^box R|/it| + } + ) } + R|com/example/encodeAndDecode|(Q|com/example/FullyOverridden|.R|com/example/FullyOverridden.Companion.serializer|(), R|com/example/FullyOverridden.FullyOverridden|(Int(4)), String("i=4"))?.{ $subj$.R|kotlin/let|( = let@fun (it: R|kotlin/String|): R|kotlin/Nothing| { + ^box R|/it| + } + ) } + R|com/example/encodeAndDecode|(Q|com/example/PartiallyOverridden|.R|com/example/PartiallyOverridden.Companion.serializer|(), R|com/example/PartiallyOverridden.PartiallyOverridden|(Int(5)), String({"i":15}), R|com/example/PartiallyOverridden.PartiallyOverridden|(Int(15)))?.{ $subj$.R|kotlin/let|( = let@fun (it: R|kotlin/String|): R|kotlin/Nothing| { + ^box R|/it| + } + ) } + when () { + !=(Q|com/example/PartiallyOverridden|.R|com/example/PartiallyOverridden.Companion.serializer|().R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.serialName|, String(Partially-Overridden)) -> { + ^box Q|com/example/PartiallyOverridden|.R|com/example/PartiallyOverridden.Companion.serializer|().R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.serialName| + } + } + + R|com/example/encodeAndDecode|(Q|com/example/PartiallyWithoutType|.R|com/example/PartiallyWithoutType.Companion.serializer|(), R|com/example/PartiallyWithoutType.PartiallyWithoutType|(Int(6)), String({"i":6}), R|com/example/PartiallyWithoutType.PartiallyWithoutType|(Int(16)))?.{ $subj$.R|kotlin/let|( = let@fun (it: R|kotlin/String|): R|kotlin/Nothing| { + ^box R|/it| + } + ) } + when () { + !=(Q|com/example/PartiallyWithoutType|.R|com/example/PartiallyWithoutType.Companion.serializer|().R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.serialName|, (Q|com/example/PartiallyWithoutType|).R|SubstitutionOverride|) -> { + ^box Q|com/example/PartiallyWithoutType|.R|com/example/PartiallyWithoutType.Companion.serializer|().R|SubstitutionOverride|.R|kotlinx/serialization/descriptors/SerialDescriptor.serialName| + } + } + + ^box String(OK) + } + private final fun encodeAndDecode(serializer: R|kotlinx/serialization/KSerializer|, value: R|T|, expectedEncoded: R|kotlin/String|, expectedDecoded: R|T?| = Null(null)): R|kotlin/String?| { + lval encoded: R|kotlin/String| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.encodeToString|(R|/serializer|, R|/value|) + when () { + !=(R|/encoded|, R|/expectedEncoded|) -> { + ^encodeAndDecode R|/encoded| + } + } + + lval decoded: R|T| = Q|kotlinx/serialization/json/Json|.R|kotlinx/serialization/json/Json.decodeFromString|(R|/serializer|, R|/encoded|) + when () { + !=(R|/decoded|, R|/expectedDecoded| ?: R|/value|) -> { + ^encodeAndDecode (String(DECODED=), R|/decoded|.R|kotlin/toString|()) + } + } + + ^encodeAndDecode Null(null) + } diff --git a/plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.kt b/plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.kt new file mode 100644 index 00000000000..6611d796aeb --- /dev/null +++ b/plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.kt @@ -0,0 +1,111 @@ +// WITH_STDLIB + +package com.example + +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.descriptors.* + + +@Serializable +data class WithCompanion(val i: Int) { + @Serializer(forClass = WithCompanion::class) + companion object +} + +@Serializable +data class WithNamedCompanion(val i: Int) { + @Serializer(forClass = WithNamedCompanion::class) + companion object Named +} + +@Serializable +data class WithExplicitType(val i: Int) { + @Serializer(forClass = WithExplicitType::class) + companion object : KSerializer +} + +@Serializable +data class PartiallyOverridden(val i: Int) { + @Serializer(forClass = PartiallyOverridden::class) + companion object : KSerializer { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Partially-Overridden") { + element("i", PrimitiveSerialDescriptor("i", PrimitiveKind.INT)) + } + + override fun serialize(encoder: Encoder, value: PartiallyOverridden) { + val compositeOutput = encoder.beginStructure(PartiallyOverridden.descriptor) + compositeOutput.encodeIntElement(PartiallyOverridden.descriptor, 0, value.i + 10) + compositeOutput.endStructure(PartiallyOverridden.descriptor) + } + } +} + +@Serializable +data class PartiallyWithoutType(val i: Int) { + @Serializer(forClass = PartiallyWithoutType::class) + companion object { + + override fun deserialize(decoder: Decoder): PartiallyWithoutType { + val dec: CompositeDecoder = decoder.beginStructure(descriptor) + var iv: Int? = null + loop@ while (true) { + when (val i = dec.decodeElementIndex(descriptor)) { + CompositeDecoder.DECODE_DONE -> break@loop + 0 -> iv = dec.decodeIntElement(descriptor, i) + else -> throw SerializationException("Unknown index $i") + } + } + dec.endStructure(descriptor) + return PartiallyWithoutType(iv!! + 10) + } + } +} + + +@Serializable +data class FullyOverridden(val i: Int) { + @Serializer(forClass = FullyOverridden::class) + companion object : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("FullyOverridden", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: FullyOverridden) { + encoder.encodeString("i=${value.i}") + } + + override fun deserialize(decoder: Decoder): FullyOverridden { + val i = decoder.decodeString().substringAfter('=').toInt() + return FullyOverridden(i) + } + } +} + + +fun box(): String { + encodeAndDecode(WithCompanion.serializer(), WithCompanion(1), """{"i":1}""")?.let { return it } + + encodeAndDecode(WithNamedCompanion.serializer(), WithNamedCompanion(2), """{"i":2}""")?.let { return it } + + encodeAndDecode(WithExplicitType.serializer(), WithExplicitType(3), """{"i":3}""")?.let { return it } + + encodeAndDecode(FullyOverridden.serializer(), FullyOverridden(4), "\"i=4\"")?.let { return it } + + encodeAndDecode(PartiallyOverridden.serializer(), PartiallyOverridden(5), """{"i":15}""", PartiallyOverridden(15))?.let { return it } + if (PartiallyOverridden.serializer().descriptor.serialName != "Partially-Overridden") return PartiallyOverridden.serializer().descriptor.serialName + + encodeAndDecode(PartiallyWithoutType.serializer(), PartiallyWithoutType(6), """{"i":6}""", PartiallyWithoutType(16))?.let { return it } + if (PartiallyWithoutType.serializer().descriptor.serialName != PartiallyWithoutType::class.qualifiedName) return PartiallyWithoutType.serializer().descriptor.serialName + + return "OK" +} + + +private fun encodeAndDecode(serializer: KSerializer, value: T, expectedEncoded: String, expectedDecoded: T? = null): String? { + val encoded = Json.encodeToString(serializer, value) + if (encoded != expectedEncoded) return encoded + + val decoded = Json.decodeFromString(serializer, encoded) + if (decoded != (expectedDecoded ?: value)) return "DECODED=${decoded.toString()}" + return null +} diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java index 49d763afa61..ddf73f2adb0 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java @@ -151,6 +151,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization runTest("plugins/kotlinx-serialization/testData/firMembers/enums.kt"); } + @Test + @TestMetadata("externalSerializers.kt") + public void testExternalSerializers() throws Exception { + runTest("plugins/kotlinx-serialization/testData/firMembers/externalSerializers.kt"); + } + @Test @TestMetadata("inlineClasses.kt") public void testInlineClasses() throws Exception { @@ -186,5 +192,11 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization public void testSerializableWith() throws Exception { runTest("plugins/kotlinx-serialization/testData/firMembers/serializableWith.kt"); } + + @Test + @TestMetadata("serializerViaCompanion.kt") + public void testSerializerViaCompanion() throws Exception { + runTest("plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.kt"); + } } } diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java index c93bf227f5e..c67d0459432 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java @@ -174,6 +174,12 @@ public class SerializationFirDiagnosticTestGenerated extends AbstractSerializati runTest("plugins/kotlinx-serialization/testData/firMembers/enums.kt"); } + @Test + @TestMetadata("externalSerializers.kt") + public void testExternalSerializers() throws Exception { + runTest("plugins/kotlinx-serialization/testData/firMembers/externalSerializers.kt"); + } + @Test @TestMetadata("inlineClasses.kt") public void testInlineClasses() throws Exception { @@ -209,5 +215,11 @@ public class SerializationFirDiagnosticTestGenerated extends AbstractSerializati public void testSerializableWith() throws Exception { runTest("plugins/kotlinx-serialization/testData/firMembers/serializableWith.kt"); } + + @Test + @TestMetadata("serializerViaCompanion.kt") + public void testSerializerViaCompanion() throws Exception { + runTest("plugins/kotlinx-serialization/testData/firMembers/serializerViaCompanion.kt"); + } } }