Added support of MetaSerializable to the FIR

This commit is contained in:
Sergey.Shanshin
2022-10-12 20:10:58 +00:00
committed by Space Team
parent 28340459b0
commit 503f4d924d
13 changed files with 980 additions and 233 deletions
@@ -5,9 +5,7 @@
package org.jetbrains.kotlinx.serialization.compiler.fir
import org.jetbrains.kotlin.fir.extensions.predicate.AnnotatedWith
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
import org.jetbrains.kotlin.fir.extensions.predicate.ancestorAnnotated
import org.jetbrains.kotlin.fir.extensions.predicate.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
object FirSerializationPredicates {
@@ -15,5 +13,7 @@ object FirSerializationPredicates {
AnnotatedWith(setOf(SerializationAnnotations.serializerAnnotationFqName)) // @Serializer(for=...)
internal val generatedSerializer: DeclarationPredicate =
ancestorAnnotated(SerializationAnnotations.serializableAnnotationFqName) // @Serializable X.$serializer
internal val annotatedWithSerializable = AnnotatedWith(setOf(SerializationAnnotations.serializableAnnotationFqName))
}
internal val hasMetaAnnotation = metaAnnotated(SerializationAnnotations.metaSerializableAnnotationFqName)
internal val annotatedWithSerializableOrMeta =
AnnotatedWith(setOf(SerializationAnnotations.serializableAnnotationFqName)) or metaAnnotated(SerializationAnnotations.metaSerializableAnnotationFqName)
}
@@ -10,11 +10,13 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
import org.jetbrains.kotlin.fir.copy
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.origin
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
@@ -55,15 +57,15 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
hasFactory && hasMarkedFactory
}
internal val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && isFinalOrOpen()) || (classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotationWithoutArgs && !runtimeHasEnumSerializerFactory)
override fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>): Set<Name> {
val result = mutableSetOf<Name>()
if (classSymbol.shouldHaveGeneratedMethodsInCompanion && !classSymbol.isSerializableObject)
result += SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
if (classSymbol.shouldHaveGeneratedSerializer /* TODO && !classSymbol.hasCompanionObjectAsSerializer*/)
result += SerialEntityNames.SERIALIZER_CLASS_NAME
with(session) {
if (classSymbol.shouldHaveGeneratedMethodsInCompanion && !classSymbol.isSerializableObject)
result += SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
if (classSymbol.shouldHaveGeneratedSerializer(!runtimeHasEnumSerializerFactory) /* TODO && !classSymbol.hasCompanionObjectAsSerializer*/)
result += SerialEntityNames.SERIALIZER_CLASS_NAME
}
return result
}
@@ -115,7 +117,8 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
}
}
}
else -> if (classSymbol.isSerializableObject) result += SerialEntityNames.SERIALIZER_PROVIDER_NAME
else -> if (with(session) { classSymbol.isSerializableObject }) result += SerialEntityNames.SERIALIZER_PROVIDER_NAME
}
return result
}
@@ -129,8 +132,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
useSiteSuperType.scopeForSupertype(session, scopeSession, owner.fir)
}
val targets = scopes.flatMap { extractor(it) }
val target = targets.singleOrNull() ?: error("Multiple overrides found for ${callableId.callableName}")
return target
return targets.singleOrNull() ?: error("Multiple overrides found for ${callableId.callableName}")
}
// TODO: support @Serializer(for)
@@ -138,7 +140,14 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
val owner = context?.owner ?: return emptyList()
if (callableId.callableName == SerialEntityNames.SERIALIZER_PROVIDER_NAME) {
val serializableClass = owner.getSerializableClassSymbolIfCompanion(session) ?: return emptyList()
val serializableClass = if (owner.isCompanion) {
val containingSymbol = owner.getContainingDeclarationSymbol(session) as? FirClassSymbol<*> ?: return emptyList()
if (with(session) { containingSymbol.shouldHaveGeneratedMethodsInCompanion }) containingSymbol else null
} else {
if (with(session) { owner.isSerializableObject }) owner else null
}
serializableClass ?: return emptyList()
return listOf(generateSerializerGetterInCompanion(owner, serializableClass, callableId))
}
if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList()
@@ -228,7 +237,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
// FIXME: it seems that this list will always be used, why not provide it automatically?
private val matchedClasses by lazy {
session.predicateBasedProvider.getSymbolsByPredicate(FirSerializationPredicates.annotatedWithSerializable)
session.predicateBasedProvider.getSymbolsByPredicate(FirSerializationPredicates.annotatedWithSerializableOrMeta)
.filterIsInstance<FirRegularClassSymbol>()
}
@@ -324,7 +333,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
name = SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
symbol = FirRegularClassSymbol(classId)
superTypeRefs += session.builtinTypes.anyType
if (owner.companionNeedsSerializerFactory()) {
if (with(session) { owner.companionNeedsSerializerFactory }) {
val serializerFactoryClassId = ClassId(SerializationPackages.internalPackageFqName, SERIALIZER_FACTORY_INTERFACE_NAME)
superTypeRefs += serializerFactoryClassId.constructClassLikeType(emptyArray(), false).toFirResolvedTypeRef()
}
@@ -333,16 +342,20 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
}
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirSerializationPredicates.annotatedWithSerializable)
register(FirSerializationPredicates.annotatedWithSerializableOrMeta, FirSerializationPredicates.hasMetaAnnotation)
}
private fun FirClassSymbol<*>.companionNeedsSerializerFactory(): Boolean {
if (!(moduleData.platform.isNative() || moduleData.platform.isJs())) return false
if (isSerializableObject) return true
if (isSerializableEnum) return true
if (isAbstractOrSealedSerializableClass) return true
if (isSealedSerializableInterface) return true
if (typeParameterSymbols.isEmpty()) return false
return true
}
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
private val FirClassSymbol<*>.companionNeedsSerializerFactory: Boolean
get() {
if (!(moduleData.platform.isNative() || moduleData.platform.isJs())) return false
if (isSerializableObject) return true
if (isSerializableEnum) return true
if (isAbstractOrSealedSerializableClass) return true
if (isSealedSerializableInterface) return true
if (typeParameterSymbols.isEmpty()) return false
return true
}
}
@@ -7,24 +7,18 @@ package org.jetbrains.kotlinx.serialization.compiler.fir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
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.defaultType
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.scopes.getSingleClassifier
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.kotlin.name.StandardClassIds
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
@@ -32,7 +26,6 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotat
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationClassId
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoClassId
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialNameAnnotationClassId
import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins
object AnnotationParameterNames {
val VALUE = Name.identifier("value")
@@ -65,10 +58,6 @@ val FirBasedSymbol<*>.hasSerialTransient: Boolean
val FirBasedSymbol<*>.serialTransientAnnotation: FirAnnotation?
get() = getAnnotationByClassId(SerializationAnnotations.serialTransientClassId)
context(CheckerContext)
val FirBasedSymbol<*>.hasAnySerialAnnotation: Boolean
get() = serialNameValue != null || resolvedAnnotationsWithClassIds.any { it.annotationClassSymbol?.isSerialInfoAnnotation == true }
val FirClassSymbol<*>.hasSerializableAnnotation: Boolean
get() = serializableAnnotation(needArguments = false) != null
@@ -90,81 +79,84 @@ val FirClassSymbol<*>.hasSerializableAnnotationWithoutArgs: Boolean
}
} ?: false
context(CheckerContext)
val FirClassSymbol<*>.hasSerializableOrMetaAnnotationWithoutArgs: Boolean
get() = hasSerializableAnnotationWithoutArgs || (!hasSerializableAnnotation && hasMetaSerializableAnnotation)
context(CheckerContext)
val FirClassSymbol<*>.hasSerializableOrMetaAnnotation
get() = hasSerializableAnnotation || hasMetaSerializableAnnotation
context(CheckerContext)
val FirClassSymbol<*>.hasMetaSerializableAnnotation: Boolean
get() = metaSerializableAnnotation(needArguments = false) != null
context(CheckerContext)
fun FirClassSymbol<*>.metaSerializableAnnotation(needArguments: Boolean): FirAnnotation? {
val annotations = if (needArguments) resolvedAnnotationsWithClassIds else resolvedAnnotationsWithArguments
return annotations.firstOrNull { it.isMetaSerializableAnnotation }
}
context(CheckerContext)
val FirAnnotation.isMetaSerializableAnnotation: Boolean
get() = annotationClassSymbol?.hasAnnotation(metaSerializableAnnotationClassId) ?: false
context(CheckerContext)
val ConeKotlinType.serializableWith: ConeKotlinType?
get() = customAnnotations.serializableWith ?: toRegularClassSymbol(session)?.serializableWith
internal val FirBasedSymbol<*>.serializableWith: ConeKotlinType?
get() = serializableAnnotation(needArguments = true)?.getKClassArgument(AnnotationParameterNames.WITH)
internal val List<FirAnnotation>.serializableWith: ConeKotlinType?
get() = serializableAnnotation()?.getKClassArgument(AnnotationParameterNames.WITH)
internal val FirClassSymbol<*>.serializerAnnotation: FirAnnotation?
get() = getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId)
// ---------------------- class utils ----------------------
internal val FirClassSymbol<*>.serializerForClass: ConeKotlinType?
get() = resolvedAnnotationsWithArguments
.getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId)
?.getKClassArgument(AnnotationParameterNames.FOR_CLASS)
internal val FirClassSymbol<*>.serializerAnnotation: FirAnnotation?
get() = getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId)
context(CheckerContext)
private val FirAnnotation.annotationClassSymbol: FirRegularClassSymbol?
get() = annotationTypeRef.coneType
.fullyExpandedType(session)
.toRegularClassSymbol(session)
// ---------------------- class utils ----------------------
internal val FirClassSymbol<*>.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = this.isSerializableObject || this.isSerializableEnum || this.classKind == ClassKind.CLASS && hasSerializableAnnotation || this.isSealedSerializableInterface
internal val FirClassSymbol<*>.isSerializableObject: Boolean
get() = classKind.isObject && hasSerializableAnnotation
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isInternallySerializableObject: Boolean
get() = classKind.isObject && hasSerializableAnnotationWithoutArgs
get() = classKind.isObject && hasSerializableOrMetaAnnotationWithoutArgs
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isSerializableObject: Boolean
get() = classKind.isObject && hasSerializableOrMetaAnnotation
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isSealedSerializableInterface: Boolean
get() = classKind.isInterface && rawStatus.modality == Modality.SEALED && hasSerializableAnnotation
get() = classKind.isInterface && rawStatus.modality == Modality.SEALED && hasSerializableOrMetaAnnotation
context(FirSession)
val FirClassSymbol<*>.hasSerializableOrMetaAnnotation: Boolean
get() = hasSerializableAnnotation || hasMetaSerializableAnnotation
context(FirSession)
val FirClassSymbol<*>.hasMetaSerializableAnnotation: Boolean
get() = predicateBasedProvider.matches(FirSerializationPredicates.hasMetaAnnotation, this)
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.shouldHaveGeneratedMethodsInCompanion: Boolean
get() = isSerializableObject
|| isSerializableEnum
|| (classKind == ClassKind.CLASS && hasSerializableOrMetaAnnotation)
|| isSealedSerializableInterface
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isInternalSerializable: Boolean
get() {
if (!classKind.isClass) return false
return hasSerializableAnnotationWithoutArgs
return hasSerializableOrMetaAnnotationWithoutArgs
}
context(FirSession)
val FirClassSymbol<*>.hasSerializableOrMetaAnnotationWithoutArgs: Boolean
get() = hasSerializableAnnotationWithoutArgs || (!hasSerializableAnnotation && hasMetaSerializableAnnotation)
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isAbstractOrSealedSerializableClass: Boolean
get() = isInternalSerializable && (rawStatus.modality == Modality.ABSTRACT || rawStatus.modality == Modality.SEALED)
internal val FirClassSymbol<*>.isInternallySerializableEnum: Boolean
get() = classKind.isEnumClass && hasSerializableAnnotationWithoutArgs
/**
* Check that class is enum and marked by `Serializable` or meta-serializable annotation.
*/
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isSerializableEnum: Boolean
get() = classKind.isEnumClass && hasSerializableAnnotation
get() = classKind.isEnumClass && hasSerializableOrMetaAnnotation
/**
* Check that class is enum and marked by `Serializable` annotation without serializer argument or meta-serializable annotation.
*/
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isSerializableEnumWithoutArgs: Boolean
get() = classKind.isEnumClass && hasSerializableOrMetaAnnotationWithoutArgs
internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean {
val modality = rawStatus.modality
@@ -172,75 +164,30 @@ internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean {
return (modality == null || modality == Modality.FINAL || modality == Modality.OPEN)
}
internal fun FirClassSymbol<*>.getSerializableClassSymbolIfCompanion(session: FirSession): FirClassSymbol<*>? {
if (isSerializableObject) return this
if (!isCompanion) return null
val classDescriptor = (getContainingDeclarationSymbol(session) as? FirClassSymbol<*>) ?: return null
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
return classDescriptor
}
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
val FirClassSymbol<*>.hasCompanionObjectAsSerializer: Boolean
get() = isInternallySerializableObject ||
(this as? FirRegularClassSymbol)?.companionObjectSymbol?.serializerForClass == this.defaultType()
get() = isInternallySerializableObject || hasCustomSerializerOnCompanion
fun FirClassSymbol<*>.getSuperClassNotAny(session: FirSession): FirRegularClassSymbol? {
return getSuperClassOrAny(session).takeUnless { it.classId == StandardClassIds.Any }
}
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
val FirClassSymbol<*>.hasCustomSerializerOnCompanion: Boolean
get() = (this as? FirRegularClassSymbol)?.companionObjectSymbol?.serializerForClass != null
fun FirClassSymbol<*>.getSuperClassOrAny(session: FirSession): FirRegularClassSymbol {
return resolvedSuperTypes.firstNotNullOfOrNull { superType ->
superType.fullyExpandedType(session)
.toRegularClassSymbol(session)
?.takeIf { it.classKind == ClassKind.CLASS }
} ?: session.builtinTypes.anyType.toRegularClassSymbol(session) ?: error("Symbol for kotlin/Any not found")
}
context(CheckerContext)
val FirClassSymbol<*>?.classSerializer: FirClassSymbol<*>?
get() {
if (this == null) return null
// serializer annotation on class?
serializableWith?.let { return it.toRegularClassSymbol(session) }
// companion object serializer?
if (this is FirRegularClassSymbol && hasCompanionObjectAsSerializer) return companionObjectSymbol
// can infer @Poly?
polymorphicSerializerIfApplicableAutomatically?.let { return it }
// default serializable?
if (shouldHaveGeneratedSerializer) {
// $serializer nested class
return unsubstitutedScope(this@CheckerContext)
.getSingleClassifier(SerialEntityNames.SERIALIZER_CLASS_NAME) as? FirClassSymbol<*>
}
return null
}
context(CheckerContext)
val FirClassSymbol<*>.polymorphicSerializerIfApplicableAutomatically: FirClassSymbol<*>?
get() {
val serializerName = when {
isInterface -> when (modality) {
Modality.SEALED -> SpecialBuiltins.sealedSerializer
else -> SpecialBuiltins.polymorphicSerializer
}
isInternalSerializable -> when (modality) {
Modality.SEALED -> SpecialBuiltins.sealedSerializer
Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer
else -> null
}
else -> null
}
return serializerName?.let { session.dependencySerializationInfoProvider.getClassFromSerializationPackage(Name.identifier(it)) }
}
context(CheckerContext)
context(FirSession)
val FirClassSymbol<*>.isEnumWithLegacyGeneratedSerializer: Boolean
get() = isInternallySerializableEnum && session.dependencySerializationInfoProvider.useGeneratedEnumSerializer
get() = classKind.isEnumClass && dependencySerializationInfoProvider.useGeneratedEnumSerializer && hasSerializableOrMetaAnnotationWithoutArgs
context(CheckerContext)
context(FirSession)
val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isEnumWithLegacyGeneratedSerializer
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal fun FirClassSymbol<*>.shouldHaveGeneratedSerializer(useGeneratedEnumSerializer: Boolean): Boolean {
return (isInternalSerializable && isFinalOrOpen()) || (useGeneratedEnumSerializer && isSerializableEnumWithoutArgs)
}
// ---------------------- type utils ----------------------
val ConeKotlinType.isKSerializer: Boolean
@@ -259,15 +206,6 @@ fun ConeKotlinType.serializerForType(session: FirSession): ConeKotlinType? {
val ConeKotlinType.isTypeParameter: Boolean
get() = this is ConeTypeParameterType
context(CheckerContext)
val ConeKotlinType.overriddenSerializer: ConeKotlinType?
get() = toRegularClassSymbol(session)?.serializableWith
context(CheckerContext)
context(FirSession)
val ConeKotlinType.isGeneratedSerializableObject: Boolean
get() = toRegularClassSymbol(session)?.let { it.classKind.isObject && it.hasSerializableOrMetaAnnotationWithoutArgs } ?: false
// ---------------------- other ----------------------
val CheckerContext.currentFile: FirFile
get() = containingDeclarations.first() as FirFile
get() = toRegularClassSymbol(this@FirSession)?.let { it.classKind.isObject && it.hasSerializableOrMetaAnnotationWithoutArgs } ?: false
@@ -9,6 +9,7 @@ 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
@@ -19,6 +20,7 @@ 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.*
@@ -31,6 +33,7 @@ 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"))
@@ -204,9 +207,9 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
)
return false
}
if (!classSymbol.hasSerializableOrMetaAnnotation) return false
if (!with(session) { classSymbol.hasSerializableOrMetaAnnotation }) return false
if (classSymbol.isAnonymousObjectOrInsideIt) {
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED)
return false
@@ -227,13 +230,13 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
return false
}
if (!classSymbol.hasSerializableOrMetaAnnotationWithoutArgs) {
if (!with(session) { classSymbol.hasSerializableOrMetaAnnotationWithoutArgs }) {
// defined custom serializer
checkClassWithCustomSerializer(classSymbol, reporter)
return false
}
if (classSymbol.serializableAnnotationIsUseless) {
if (with(session) { classSymbol.serializableAnnotationIsUseless }) {
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
return false
}
@@ -241,7 +244,7 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
// check that we can instantiate supertype
if (!classSymbol.isEnumClass) { // enums are inherited from java.lang.Enum and can't be inherited from other classes
val superClassSymbol = classSymbol.getSuperClassOrAny(session)
if (!superClassSymbol.isInternalSerializable) {
if (with(session) { !superClassSymbol.isInternalSerializable }) {
val noArgConstructorSymbol = superClassSymbol.declarationSymbols.firstOrNull { it is FirConstructorSymbol && it.valueParameterSymbols.isEmpty() }
if (noArgConstructorSymbol == null) {
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
@@ -296,9 +299,11 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
private fun buildSerializableProperties(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter): FirSerializableProperties? {
if (!classSymbol.hasSerializableOrMetaAnnotation) return null
if (!classSymbol.isInternalSerializable) return null
if (classSymbol.hasCompanionObjectAsSerializer) return null
with(session) {
if (!classSymbol.hasSerializableOrMetaAnnotation) return null
if (!classSymbol.isInternalSerializable) return null
if (classSymbol.hasCompanionObjectAsSerializer) return null
}
val properties = session.serializablePropertiesProvider.getSerializablePropertiesForClass(classSymbol)
if (!properties.isExternallySerializable) {
@@ -487,34 +492,4 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
reporter.reportOn(source, FirSerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE, serializerType, classType)
}
}
// --------------------------------------------------------------------------------------
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
private val FirClassSymbol<*>.isSerializableEnumWithMissingSerializer: Boolean
get() {
if (!isEnumClass) return false
if (hasSerializableOrMetaAnnotation) return false
if (hasAnySerialAnnotation) return true
return collectEnumEntries().any { it.hasAnySerialAnnotation }
}
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
private val FirClassSymbol<*>.serializableOrMetaAnnotationSource: KtSourceElement?
get() {
serializableAnnotation(needArguments = false)?.source?.let { return it }
metaSerializableAnnotation(needArguments = false)?.source?.let { return it }
return null
}
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
private val FirClassSymbol<*>.serializableAnnotationIsUseless: Boolean
get() = !classKind.isEnumClass &&
hasSerializableOrMetaAnnotationWithoutArgs &&
!isInternalSerializable &&
!hasCompanionObjectAsSerializer &&
!isSealedSerializableInterface
}
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization.compiler.fir.checkers
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.isEnumClass
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.collectEnumEntries
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.scopes.getSingleClassifier
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.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.customAnnotations
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlinx.serialization.compiler.fir.*
import org.jetbrains.kotlinx.serialization.compiler.fir.serializableWith
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins
// ---------------------- search utils ----------------------
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>?.classSerializer: FirClassSymbol<*>?
get() {
if (this == null) return null
// serializer annotation on class?
serializableWith?.let { return it.toRegularClassSymbol(session) }
// companion object serializer?
if (this is FirRegularClassSymbol && with(session) { hasCompanionObjectAsSerializer }) return companionObjectSymbol
// can infer @Poly?
polymorphicSerializerIfApplicableAutomatically?.let { return it }
// default serializable?
if (with(session) { shouldHaveGeneratedSerializer }) {
// $serializer nested class
return unsubstitutedScope(this@CheckerContext)
.getSingleClassifier(SerialEntityNames.SERIALIZER_CLASS_NAME) as? FirClassSymbol<*>
}
return null
}
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
val FirClassSymbol<*>.polymorphicSerializerIfApplicableAutomatically: FirClassSymbol<*>?
get() {
val serializerName = when {
isInterface -> when (modality) {
Modality.SEALED -> SpecialBuiltins.sealedSerializer
else -> SpecialBuiltins.polymorphicSerializer
}
with(session) { isInternalSerializable } -> when (modality) {
Modality.SEALED -> SpecialBuiltins.sealedSerializer
Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer
else -> null
}
else -> null
}
return serializerName?.let { session.dependencySerializationInfoProvider.getClassFromSerializationPackage(Name.identifier(it)) }
}
// ---------------------- annotation utils ----------------------
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirAnnotation.annotationClassSymbol: FirRegularClassSymbol?
get() = annotationTypeRef.coneType
.fullyExpandedType(session)
.toRegularClassSymbol(session)
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirAnnotation.isMetaSerializableAnnotation: Boolean
get() = annotationClassSymbol?.hasAnnotation(SerializationAnnotations.metaSerializableAnnotationClassId) ?: false
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal fun FirClassSymbol<*>.metaSerializableAnnotation(needArguments: Boolean): FirAnnotation? {
val annotations = if (needArguments) resolvedAnnotationsWithClassIds else resolvedAnnotationsWithArguments
return annotations.firstOrNull { it.isMetaSerializableAnnotation }
}
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.serializableOrMetaAnnotationSource: KtSourceElement?
get() {
serializableAnnotation(needArguments = false)?.source?.let { return it }
metaSerializableAnnotation(needArguments = false)?.source?.let { return it }
return null
}
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirBasedSymbol<*>.hasAnySerialAnnotation: Boolean
get() = serialNameValue != null || resolvedAnnotationsWithClassIds.any { it.annotationClassSymbol?.isSerialInfoAnnotation == true }
// ---------------------- class utils ----------------------
fun FirClassSymbol<*>.getSuperClassNotAny(session: FirSession): FirRegularClassSymbol? {
return getSuperClassOrAny(session).takeUnless { it.classId == StandardClassIds.Any }
}
fun FirClassSymbol<*>.getSuperClassOrAny(session: FirSession): FirRegularClassSymbol {
return resolvedSuperTypes.firstNotNullOfOrNull { superType ->
superType.fullyExpandedType(session)
.toRegularClassSymbol(session)
?.takeIf { it.classKind == ClassKind.CLASS }
} ?: session.builtinTypes.anyType.toRegularClassSymbol(session) ?: error("Symbol for kotlin/Any not found")
}
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.isSerializableEnumWithMissingSerializer: Boolean
get() {
if (!isEnumClass) return false
if (with(session) { hasSerializableOrMetaAnnotation }) return false
if (hasAnySerialAnnotation) return true
return collectEnumEntries().any { it.hasAnySerialAnnotation }
}
context(FirSession)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val FirClassSymbol<*>.serializableAnnotationIsUseless: Boolean
get() = !classKind.isEnumClass &&
hasSerializableOrMetaAnnotationWithoutArgs &&
!isInternalSerializable &&
!hasCompanionObjectAsSerializer &&
!isSealedSerializableInterface
// ---------------------- type utils ----------------------
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val ConeKotlinType.serializableWith: ConeKotlinType?
get() = customAnnotations.serializableWith ?: toRegularClassSymbol(session)?.serializableWith
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
internal val ConeKotlinType.overriddenSerializer: ConeKotlinType?
get() = toRegularClassSymbol(session)?.serializableWith
// ---------------------- others ----------------------
internal val CheckerContext.currentFile: FirFile
get() = containingDeclarations.first() as FirFile
@@ -25,7 +25,9 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlinx.serialization.compiler.fir.*
import org.jetbrains.kotlinx.serialization.compiler.fir.serializableWith
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.classSerializer
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.currentFile
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.overriddenSerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
@@ -116,20 +118,20 @@ fun findTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? {
if (type.isArrayType) {
return serializationProvider.getClassFromInternalSerializationPackage(SpecialBuiltins.Names.referenceArraySerializer)
}
if (type.isGeneratedSerializableObject) {
if (with(session) { type.isGeneratedSerializableObject }) {
return serializationProvider.getClassFromInternalSerializationPackage(SpecialBuiltins.Names.objectSerializer)
}
// see if there is a standard serializer
val standardSerializer = findStandardKotlinTypeSerializer(type) ?: findEnumTypeSerializer(type)
val standardSerializer = with(session) { findStandardKotlinTypeSerializer(type) ?: findEnumTypeSerializer(type) }
if (standardSerializer != null) return standardSerializer
val symbol = type.toRegularClassSymbol(session) ?: return null
if (symbol.isSealedSerializableInterface) {
if (with(session) { symbol.isSealedSerializableInterface }) {
return serializationProvider.getClassFromSerializationPackage(SpecialBuiltins.Names.polymorphicSerializer)
}
return symbol.classSerializer // check for serializer defined on the type
}
context(CheckerContext)
context(FirSession)
fun findStandardKotlinTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? {
val name = when {
type.isBoolean -> PrimitiveBuiltins.booleanSerializer
@@ -142,15 +144,15 @@ fun findStandardKotlinTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? {
type.isChar -> PrimitiveBuiltins.charSerializer
else -> findStandardKotlinTypeSerializerName(type.classId?.asFqNameString())
}?.let(Name::identifier) ?: return null
val symbolProvider = session.symbolProvider
val symbolProvider = symbolProvider
return symbolProvider.getClassLikeSymbolByClassId(ClassId(SerializationPackages.internalPackageFqName, name)) as? FirClassSymbol<*>
?: symbolProvider.getClassLikeSymbolByClassId(ClassId(SerializationPackages.packageFqName, name)) as? FirClassSymbol<*>
}
context(CheckerContext)
context(FirSession)
fun findEnumTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? {
val symbol = type.toRegularClassSymbol(session) ?: return null
val symbol = type.toRegularClassSymbol(this@FirSession) ?: return null
return runIf(symbol.isEnumClass && !symbol.isEnumWithLegacyGeneratedSerializer) {
session.symbolProvider.getClassLikeSymbolByClassId(SerializersClassIds.enumSerializerId) as? FirClassSymbol<*>
symbolProvider.getClassLikeSymbolByClassId(SerializersClassIds.enumSerializerId) as? FirClassSymbol<*>
}
}
@@ -13,17 +13,17 @@ import org.jetbrains.kotlin.fir.caches.createCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.declarations.utils.correspondingValueParameterFromPrimaryConstructor
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableReference
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
import org.jetbrains.kotlin.fir.references.impl.FirPropertyFromParameterResolvedNamedReference
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.scopes.processAllProperties
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlinx.serialization.compiler.fir.*
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.getSuperClassNotAny
import org.jetbrains.kotlinx.serialization.compiler.fir.isInternalSerializable
import org.jetbrains.kotlinx.serialization.compiler.resolve.ISerializableProperty
@@ -35,6 +35,10 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi
return cache.getValue(classSymbol)
}
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirSerializationPredicates.hasMetaAnnotation)
}
private fun createSerializableProperties(classSymbol: FirClassSymbol<*>): FirSerializableProperties {
val allPropertySymbols = buildList {
classSymbol
@@ -49,7 +53,7 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi
it to parameterSymbol.hasDefaultValue
}.toMap().withDefault { false }
val isInternalSerializable = classSymbol.isInternalSerializable
val isInternalSerializable = with(session) { classSymbol.isInternalSerializable }
fun isPropertySerializable(propertySymbol: FirPropertySymbol): Boolean {
return when {
@@ -75,7 +79,7 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi
.let { (fromConstructor, standalone) ->
val superClassSymbol = classSymbol.getSuperClassNotAny(session)
buildList {
if (superClassSymbol != null && superClassSymbol.isInternalSerializable) {
if (superClassSymbol != null && with(session) { superClassSymbol.isInternalSerializable }) {
addAll(getSerializablePropertiesForClass(superClassSymbol).serializableProperties)
}
addAll(fromConstructor)
@@ -84,7 +88,7 @@ class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessi
}
.let { restoreCorrectOrderFromClassProtoExtension(classSymbol, it) }
val isExternallySerializable = classSymbol.isInternallySerializableEnum ||
val isExternallySerializable = classSymbol.isEnumClass ||
primaryConstructorProperties.size == classSymbol.primaryConstructorSymbol()?.valueParameterSymbols?.size
val (serializableConstructorProperties, serializableStandaloneProperties) = serializableProperties.partition { it.propertySymbol in primaryConstructorProperties }