Prioritize @Serializable annotation on type usage over class usage

In `val p: @Serializable(X) Foo` and `@Serializable(Y) class Foo` situation,
now X would be chosen as serializer.

Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1895
This commit is contained in:
Leonid Startsev
2022-09-27 19:46:08 +02:00
committed by Space Team
parent d2d13c8765
commit 242b83edbd
7 changed files with 82 additions and 13 deletions
@@ -46,7 +46,7 @@ fun BaseIrGenerator.getIrSerialTypeInfo(property: IrSerializableProperty, ctx: S
val T = property.type
property.serializableWith(ctx)?.let { return SerializableInfo(it) }
findAddOnSerializer(T, ctx)?.let { return SerializableInfo(it) }
T.overridenSerializer?.let { return SerializableInfo(it) }
T.overriddenSerializer?.let { return SerializableInfo(it) }
return when {
T.isTypeParameter() -> IrSerialTypeInfo(property, if (property.type.isMarkedNullable()) "Nullable" else "", null)
T.isPrimitiveType() -> IrSerialTypeInfo(
@@ -112,7 +112,7 @@ fun analyzeSpecialSerializers(
fun findTypeSerializer(context: SerializationBaseContext, type: IrType): IrClassSymbol? {
type.overridenSerializer?.let { return it }
type.overriddenSerializer?.let { return it }
if (type.isTypeParameter()) return null
if (type.isArray()) return context.referenceClassId(referenceArraySerializerId)
if (type.isGeneratedSerializableObject()) return context.referenceClassId(objectSerializerId)
@@ -166,8 +166,9 @@ internal fun IrClass.polymorphicSerializerIfApplicableAutomatically(context: Ser
}
}
internal val IrType.overridenSerializer: IrClassSymbol?
internal val IrType.overriddenSerializer: IrClassSymbol?
get() {
annotations.serializableWith()?.let { return it }
val desc = this.classOrNull ?: return null
desc.owner.serializableWith?.let { return it }
return null
@@ -538,7 +538,7 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty, ty
property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) }
findAddOnSerializer(property.type, property.module)?.let { return SerializableInfo(it) }
property.type.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) }
property.type.overriddenSerializer(property.module)?.toClassDescriptor?.let { return SerializableInfo(it) }
if (property.type.isTypeParameter()) return JVMSerialTypeInfo(
property,
@@ -54,7 +54,7 @@ fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): S
val T = property.type
property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) }
findAddOnSerializer(T, property.module)?.let { return SerializableInfo(it) }
T.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) }
T.overriddenSerializer(property.module)?.toClassDescriptor?.let { return SerializableInfo(it) }
return when {
T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null)
T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo(
@@ -147,7 +147,7 @@ fun AbstractSerialGenerator?.findTypeSerializerOrContext(
}
fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
val userOverride = kType.overridenSerializer
val userOverride = kType.overriddenSerializer(module)
if (userOverride != null) return userOverride.toClassDescriptor
if (kType.isTypeParameter()) return null
if (KotlinBuiltIns.isArray(kType)) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer)
@@ -245,13 +245,13 @@ val ClassDescriptor?.classSerializer: ClassDescriptor?
val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
get() = isInternallySerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType
// returns only user-overriden Serializer
val KotlinType.overridenSerializer: KotlinType?
get() {
val desc = this.toClassDescriptor ?: return null
desc.serializableWith?.let { return it }
return null
}
// returns only user-overridden Serializer
fun KotlinType.overriddenSerializer(module: ModuleDescriptor): KotlinType? {
annotations.serializableWith(module)?.let { return it }
val desc = this.toClassDescriptor ?: return null
desc.serializableWith?.let { return it }
return null
}
val KotlinType.genericIndex: Int?
get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index
@@ -0,0 +1,56 @@
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.internal.*
@Serializable(BruhSerializerA::class)
class Bruh(val s: String)
object BruhSerializerA : KSerializer<Bruh> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("Bruh", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Bruh) {
encoder.encodeString(value.s)
}
override fun deserialize(decoder: Decoder): Bruh {
return Bruh(decoder.decodeString())
}
}
object BruhSerializerB : KSerializer<Bruh> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("Bruh", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Bruh) {
encoder.encodeString(value.s + "#")
}
override fun deserialize(decoder: Decoder): Bruh {
return Bruh(decoder.decodeString().removeSuffix("#"))
}
}
typealias BruhAlias = @Serializable(BruhSerializerB::class) Bruh
@Serializable
class Tester(
val b1: Bruh,
@Serializable(BruhSerializerB::class) val b2: Bruh,
val b3: @Serializable(BruhSerializerB::class) Bruh,
val b4: BruhAlias
)
fun box(): String {
val t = Tester(Bruh("a"), Bruh("b"), Bruh("c"), Bruh("d"))
val s = Json.encodeToString(t)
if (s != """{"b1":"a","b2":"b#","b3":"c#","b4":"d#"}""") return s
return "OK"
}
@@ -105,6 +105,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
}
@Test
@TestMetadata("serializableOnPropertyType.kt")
public void testSerializableOnPropertyType() throws Exception {
runTest("plugins/kotlinx-serialization/testData/boxIr/serializableOnPropertyType.kt");
}
@Test
@TestMetadata("starProjections.kt")
public void testStarProjections() throws Exception {
@@ -103,6 +103,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
}
@Test
@TestMetadata("serializableOnPropertyType.kt")
public void testSerializableOnPropertyType() throws Exception {
runTest("plugins/kotlinx-serialization/testData/boxIr/serializableOnPropertyType.kt");
}
@Test
@TestMetadata("starProjections.kt")
public void testStarProjections() throws Exception {