Handle situation where KSerializer is absent from immediate supertypes
of the class used in @UseSerializers: Use methods to receive full list of supertypes. K1 supertypes() call returned all supertypes, while IrClass.supertypes and FirClassSymbol.resolvedSuperTypes return only immediate ones. This lead to a difference in behavior between K1 and K2, and regression after plugin backend was rewritten from descriptors to IR. #KT-55340 Fixed
This commit is contained in:
committed by
Space Team
parent
c011f0c374
commit
1c4614e93b
+2
-2
@@ -82,10 +82,10 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
}
|
}
|
||||||
|
|
||||||
val additionalSerializersInScopeOfCurrentFile: Map<Pair<IrClassSymbol, Boolean>, IrClassSymbol> by lazy {
|
val additionalSerializersInScopeOfCurrentFile: Map<Pair<IrClassSymbol, Boolean>, IrClassSymbol> by lazy {
|
||||||
getClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName,)
|
getClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName)
|
||||||
.associateBy(
|
.associateBy(
|
||||||
{ serializerSymbol ->
|
{ serializerSymbol ->
|
||||||
val kotlinType = (serializerSymbol.owner.superTypes.find(IrType::isKSerializer) as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull
|
val kotlinType = getAllSubstitutedSupertypes(serializerSymbol.owner).find(IrType::isKSerializer)?.arguments?.firstOrNull()?.typeOrNull
|
||||||
val classSymbol = kotlinType?.classOrNull
|
val classSymbol = kotlinType?.classOrNull
|
||||||
?: error("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type")
|
?: error("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type")
|
||||||
classSymbol to kotlinType.isNullable()
|
classSymbol to kotlinType.isNullable()
|
||||||
|
|||||||
+2
@@ -208,6 +208,8 @@ object SerializersClassIds {
|
|||||||
val sealedSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.sealedSerializer))
|
val sealedSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.sealedSerializer))
|
||||||
val contextSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.contextSerializer))
|
val contextSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.contextSerializer))
|
||||||
val generatedSerializerId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS)
|
val generatedSerializerId = ClassId(SerializationPackages.internalPackageFqName, SerialEntityNames.GENERATED_SERIALIZER_CLASS)
|
||||||
|
|
||||||
|
val setOfSpecialSerializers = setOf(contextSerializerId, polymorphicSerializerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findStandardKotlinTypeSerializerName(typeName: String?): String? {
|
fun findStandardKotlinTypeSerializerName(typeName: String?): String? {
|
||||||
|
|||||||
+23
-1
@@ -13,10 +13,14 @@ import org.jetbrains.kotlin.fir.expressions.*
|
|||||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||||
import org.jetbrains.kotlin.fir.expressions.arguments
|
import org.jetbrains.kotlin.fir.expressions.arguments
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.createSubstitutionForSupertype
|
||||||
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
|
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
|
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
|
||||||
@@ -180,13 +184,31 @@ val ConeKotlinType.isKSerializer: Boolean
|
|||||||
fun ConeKotlinType.serializerForType(session: FirSession): ConeKotlinType? {
|
fun ConeKotlinType.serializerForType(session: FirSession): ConeKotlinType? {
|
||||||
return this.fullyExpandedType(session)
|
return this.fullyExpandedType(session)
|
||||||
.toRegularClassSymbol(session)
|
.toRegularClassSymbol(session)
|
||||||
?.resolvedSuperTypes
|
?.getAllSubstitutedSupertypes(session)
|
||||||
?.find { it.isKSerializer }
|
?.find { it.isKSerializer }
|
||||||
?.typeArguments
|
?.typeArguments
|
||||||
?.firstOrNull()
|
?.firstOrNull()
|
||||||
?.type
|
?.type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun FirRegularClassSymbol.getAllSubstitutedSupertypes(session: FirSession): Set<ConeKotlinType> {
|
||||||
|
val result = mutableSetOf<ConeKotlinType>()
|
||||||
|
|
||||||
|
fun process(symbol: FirRegularClassSymbol, substitutor: ConeSubstitutor) {
|
||||||
|
for (superType in symbol.resolvedSuperTypes) {
|
||||||
|
if (result.add(substitutor.substituteOrSelf(superType))) {
|
||||||
|
val superClassSymbol = superType.fullyExpandedType(session).toRegularClassSymbol(session) ?: continue
|
||||||
|
val superSubstitutor =
|
||||||
|
(superType as? ConeLookupTagBasedType)?.let { createSubstitutionForSupertype(it, session) } ?: ConeSubstitutor.Empty
|
||||||
|
process(superClassSymbol, ChainedSubstitutor(superSubstitutor, substitutor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process(this, ConeSubstitutor.Empty)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
val ConeKotlinType.isTypeParameter: Boolean
|
val ConeKotlinType.isTypeParameter: Boolean
|
||||||
get() = this is ConeTypeParameterType
|
get() = this is ConeTypeParameterType
|
||||||
|
|
||||||
|
|||||||
+11
-6
@@ -34,6 +34,7 @@ import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSeriali
|
|||||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider
|
import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.versionReader
|
import org.jetbrains.kotlinx.serialization.compiler.fir.services.versionReader
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds
|
||||||
|
|
||||||
object FirSerializationPluginClassChecker : FirClassChecker() {
|
object FirSerializationPluginClassChecker : FirClassChecker() {
|
||||||
private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable"))
|
private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable"))
|
||||||
@@ -421,27 +422,31 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
|
|||||||
) {
|
) {
|
||||||
for (property in properties) {
|
for (property in properties) {
|
||||||
// TODO: reporting diagnostics on properties from superclasses looks a bad idea
|
// TODO: reporting diagnostics on properties from superclasses looks a bad idea
|
||||||
val serializerType = property.serializableWith
|
val customSerializerType = property.serializableWith
|
||||||
val serializerSymbol = serializerType?.toRegularClassSymbol(session)
|
val serializerSymbol = customSerializerType?.toRegularClassSymbol(session)
|
||||||
val propertySymbol = property.propertySymbol
|
val propertySymbol = property.propertySymbol
|
||||||
val typeRef = propertySymbol.resolvedReturnTypeRef
|
val typeRef = propertySymbol.resolvedReturnTypeRef
|
||||||
val propertyType = typeRef.coneType
|
val propertyType = typeRef.coneType
|
||||||
val source = typeRef.source ?: propertySymbol.source
|
val source = typeRef.source ?: propertySymbol.source
|
||||||
if (serializerType != null && serializerSymbol != null) {
|
if (customSerializerType != null && serializerSymbol != null) {
|
||||||
|
// Do not account for @Polymorphic and @Contextual, as they are serializers for T: Any
|
||||||
|
// and would not be compatible on direct comparison
|
||||||
|
if (customSerializerType.classId in SerializersClassIds.setOfSpecialSerializers) return
|
||||||
|
|
||||||
checkCustomSerializerMatch(
|
checkCustomSerializerMatch(
|
||||||
classSymbol,
|
classSymbol,
|
||||||
source = typeRef.source ?: propertySymbol.source,
|
source = typeRef.source ?: propertySymbol.source,
|
||||||
propertyType,
|
propertyType,
|
||||||
serializerType,
|
customSerializerType,
|
||||||
reporter
|
reporter
|
||||||
)
|
)
|
||||||
checkCustomSerializerIsNotLocal(
|
checkCustomSerializerIsNotLocal(
|
||||||
source = propertySymbol.serializableAnnotation(needArguments = false)?.source,
|
source = propertySymbol.serializableAnnotation(needArguments = false)?.source,
|
||||||
classSymbol,
|
classSymbol,
|
||||||
serializerType,
|
customSerializerType,
|
||||||
reporter
|
reporter
|
||||||
)
|
)
|
||||||
checkSerializerNullability(propertyType, serializerType, source, reporter)
|
checkSerializerNullability(propertyType, customSerializerType, source, reporter)
|
||||||
} else {
|
} else {
|
||||||
checkType(propertyType, source, reporter)
|
checkType(propertyType, source, reporter)
|
||||||
checkGenericArrayType(propertyType, source, reporter)
|
checkGenericArrayType(propertyType, source, reporter)
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
|
||||||
|
// WITH_STDLIB
|
||||||
|
|
||||||
|
// FILE: a.kt
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.descriptors.*
|
||||||
|
import kotlinx.serialization.encoding.*
|
||||||
|
|
||||||
|
class IList<T>
|
||||||
|
|
||||||
|
abstract class DataSerializer<T, K>: KSerializer<T> {
|
||||||
|
abstract fun getK(): K
|
||||||
|
}
|
||||||
|
|
||||||
|
class MySerializer<T>(val elementSer: KSerializer<T>): DataSerializer<IList<T>, Int>() {
|
||||||
|
|
||||||
|
override fun getK(): Int = 42
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = PrimitiveSerialDescriptor("MySer<${elementSer.descriptor.serialName}>", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: IList<T>) = TODO("serialize")
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): IList<T> = TODO("deserialize")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
@file:UseSerializers(MySerializer::class)
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Holder(
|
||||||
|
val i: Int,
|
||||||
|
val c: IList<Int>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val d = Holder.serializer().descriptor.toString()
|
||||||
|
return if (d == "a.Holder(i: kotlin.Int, c: MySer<kotlin.Int>)") "OK" else d
|
||||||
|
}
|
||||||
+6
@@ -134,6 +134,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
|
|||||||
public void testTypealiasesTest() throws Exception {
|
public void testTypealiasesTest() throws Exception {
|
||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/typealiasesTest.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/typealiasesTest.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("useSerializersChain.kt")
|
||||||
|
public void testUseSerializersChain() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/useSerializersChain.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|||||||
+6
@@ -132,4 +132,10 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
|
|||||||
public void testTypealiasesTest() throws Exception {
|
public void testTypealiasesTest() throws Exception {
|
||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/typealiasesTest.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/typealiasesTest.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("useSerializersChain.kt")
|
||||||
|
public void testUseSerializersChain() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/useSerializersChain.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user