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:
Leonid Startsev
2022-12-07 13:57:19 +01:00
committed by Space Team
parent c011f0c374
commit 1c4614e93b
7 changed files with 98 additions and 9 deletions
@@ -13,10 +13,14 @@ 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
import org.jetbrains.kotlin.fir.resolve.createSubstitutionForSupertype
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
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.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
@@ -180,13 +184,31 @@ val ConeKotlinType.isKSerializer: Boolean
fun ConeKotlinType.serializerForType(session: FirSession): ConeKotlinType? {
return this.fullyExpandedType(session)
.toRegularClassSymbol(session)
?.resolvedSuperTypes
?.getAllSubstitutedSupertypes(session)
?.find { it.isKSerializer }
?.typeArguments
?.firstOrNull()
?.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
get() = this is ConeTypeParameterType
@@ -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.versionReader
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds
object FirSerializationPluginClassChecker : FirClassChecker() {
private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable"))
@@ -421,27 +422,31 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
) {
for (property in properties) {
// TODO: reporting diagnostics on properties from superclasses looks a bad idea
val serializerType = property.serializableWith
val serializerSymbol = serializerType?.toRegularClassSymbol(session)
val customSerializerType = property.serializableWith
val serializerSymbol = customSerializerType?.toRegularClassSymbol(session)
val propertySymbol = property.propertySymbol
val typeRef = propertySymbol.resolvedReturnTypeRef
val propertyType = typeRef.coneType
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(
classSymbol,
source = typeRef.source ?: propertySymbol.source,
propertyType,
serializerType,
customSerializerType,
reporter
)
checkCustomSerializerIsNotLocal(
source = propertySymbol.serializableAnnotation(needArguments = false)?.source,
classSymbol,
serializerType,
customSerializerType,
reporter
)
checkSerializerNullability(propertyType, serializerType, source, reporter)
checkSerializerNullability(propertyType, customSerializerType, source, reporter)
} else {
checkType(propertyType, source, reporter)
checkGenericArrayType(propertyType, source, reporter)