diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt index 89f378ca0e6..e5f3914c231 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/AbstractSerialGenerator.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations @@ -24,7 +25,7 @@ abstract class AbstractSerialGenerator(val bindingContext: BindingContext, val c ?: return emptyList() @Suppress("UNCHECKED_CAST") val typeList: List = annotation.firstArgument()?.value as? List ?: return emptyList() - return typeList.map { it.value } + return typeList.map { it.getArgumentType(declarationInFile.module) } } val contextualKClassListInCurrentFile: Set by lazy { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index 5eb07d2792d..0812742a4d2 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -45,7 +45,7 @@ abstract class SerializerCodegen( // if (save || load || prop) // generateSerialDesc() if (serializableDescriptor.declaredTypeParameters.isNotEmpty()) { - findSerializerConstructorForTypeArgumentsSerializers(serializerDescriptor)?.let { + findSerializerConstructorForTypeArgumentsSerializers(serializerDescriptor, onlyIfSynthetic = true)?.let { generateGenericFieldsAndConstructor(it) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index 77322348301..7456b9f1075 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered @@ -58,15 +59,15 @@ internal fun extractKSerializerArgumentFromImplementation(implementationClass: C return kSerializerSupertype.arguments.first().type } -internal val Annotations.serializableWith: KotlinType? - get() = findAnnotationValue(SerializationAnnotations.serializableAnnotationFqName, "with") +internal val DeclarationDescriptor.serializableWith: KotlinType? + get() = annotations.findAnnotationKotlinTypeValue(SerializationAnnotations.serializableAnnotationFqName, module,"with") -internal val Annotations.serializerForClass: KotlinType? - get() = findAnnotationValue(SerializationAnnotations.serializerAnnotationFqName, "forClass") +internal val DeclarationDescriptor.serializerForClass: KotlinType? + get() = annotations.findAnnotationKotlinTypeValue(SerializationAnnotations.serializerAnnotationFqName, module, "forClass") val Annotations.serialNameValue: String? get() { - val value = findAnnotationValue(SerializationAnnotations.serialNameAnnotationFqName, "value") + val value = findAnnotationConstantValue(SerializationAnnotations.serialNameAnnotationFqName, "value") return value } @@ -89,7 +90,7 @@ val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking // If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive. // Otherwise, this descriptor is deserialized from another module and it is OK to check value right away. val lazyDesc = annotations.findAnnotation(SerializationAnnotations.serializableAnnotationFqName) - as? LazyAnnotationDescriptor ?: return (annotations.serializableWith == null) + as? LazyAnnotationDescriptor ?: return (serializableWith == null) val psi = lazyDesc.annotationEntry return psi.valueArguments.isEmpty() } @@ -98,7 +99,7 @@ val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking internal val ClassDescriptor?.classSerializer: KotlinType? get() = this?.let { // serializer annotation on class? - annotations.serializableWith?.let { return it } + serializableWith?.let { return it } // default serializable? if (isInternalSerializable) { // companion object serializer? @@ -112,7 +113,7 @@ internal val ClassDescriptor?.classSerializer: KotlinType? } internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean - get() = companionObjectDescriptor?.annotations?.serializerForClass == this.defaultType + get() = companionObjectDescriptor?.serializerForClass == this.defaultType internal fun checkSerializerNullability(classType: KotlinType, serializerType: KotlinType): KotlinType { val castedToKSerial = requireNotNull( @@ -126,7 +127,7 @@ internal fun checkSerializerNullability(classType: KotlinType, serializerType: K // returns only user-overriden Serializer val KotlinType.overridenSerializer: KotlinType? - get() = (this.toClassDescriptor?.annotations?.serializableWith)?.let { checkSerializerNullability(this, it) } + get() = (this.toClassDescriptor?.serializableWith)?.let { checkSerializerNullability(this, it) } // serializer that was declared for this specific type or annotation from a class declaration val KotlinType.typeSerializer: KotlinType? @@ -143,7 +144,7 @@ fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor): } fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? { - val serializerForClass = serializerDescriptor.annotations.serializerForClass + val serializerForClass = serializerDescriptor.serializerForClass if (serializerForClass != null) return serializerForClass.toClassDescriptor if (serializerDescriptor.name !in setOf( SerialEntityNames.SERIALIZER_CLASS_NAME, @@ -173,10 +174,20 @@ fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean = getSerial // ---------------- -inline fun Annotations.findAnnotationValue(annotationFqName: FqName, property: String): R? = - findAnnotation(annotationFqName)?.let { annotation -> - annotation.allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value?.value - } as? R +inline fun Annotations.findAnnotationConstantValue(annotationFqName: FqName, property: String): R? = + findAnnotation(annotationFqName)?.let { annotation -> + annotation.allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value?.value + } as? R + +fun Annotations.findAnnotationKotlinTypeValue( + annotationFqName: FqName, + moduleForResolve: ModuleDescriptor, + property: String +): KotlinType? = + findAnnotation(annotationFqName)?.let { annotation -> + val maybeKClass = annotation.allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value as? KClassValue + maybeKClass?.getArgumentType(moduleForResolve) + } // Search utils diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index 0638478be0d..8fbb2d93df4 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -137,7 +137,12 @@ object KSerializerDescriptorResolver { ) { result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor)) } - result.addAll(createLocalSerializersFieldsDescriptors(classDescriptor, thisDescriptor)) + // don't add local serializer fields if typed constructor is not synthetic + if (classDescriptor.declaredTypeParameters.isNotEmpty() && + findSerializerConstructorForTypeArgumentsSerializers(thisDescriptor, onlyIfSynthetic = true) != null + ) { + result.addAll(createLocalSerializersFieldsDescriptors(classDescriptor, thisDescriptor)) + } } fun generateCompanionObjectMethods( @@ -296,15 +301,19 @@ object KSerializerDescriptorResolver { } // finds constructor (KSerializer, KSerializer...) on a KSerializer> - fun findSerializerConstructorForTypeArgumentsSerializers(serializerDescriptor: ClassDescriptor): ClassConstructorDescriptor? { + fun findSerializerConstructorForTypeArgumentsSerializers( + serializerDescriptor: ClassDescriptor, + onlyIfSynthetic: Boolean = false + ): ClassConstructorDescriptor? { val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializerDescriptor)?.arguments ?: throw AssertionError("Serializer does not implement KSerializer??") val typeParamsCount = serializableImplementationTypeArguments.size if (typeParamsCount == 0) return null //don't need it - return serializerDescriptor.constructors.find { ctor -> + val ctor = serializerDescriptor.constructors.find { ctor -> ctor.valueParameters.size == typeParamsCount && ctor.valueParameters.all { isKSerializer(it.type) } } + return if (!onlyIfSynthetic) ctor else ctor?.takeIf { it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED } } private fun createTypedSerializerConstructorDescriptor( diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt index 7ca96e6f58d..656815fc087 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt @@ -36,9 +36,9 @@ class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructor descriptor.annotationsWithArguments() private fun extractSerializableWith(annotations: Annotations): KotlinType? { - annotations.serializableWith?.let { return it } - if (annotations.hasAnnotation(SerializationAnnotations.contextualFqName)) - return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer).defaultType - return null + descriptor.serializableWith?.let { return it } + return if (annotations.hasAnnotation(SerializationAnnotations.contextualFqName)) + module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer).defaultType + else null } }