Added support serialization of local classes in IR backend
Resolves Kotlin/kotlinx.serialization#1427
This commit is contained in:
+12
@@ -717,6 +717,18 @@ interface IrBuilderExtension {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createClassReference(irClass: IrClass, startOffset: Int, endOffset: Int): IrClassReference {
|
||||||
|
val classType = irClass.descriptor.toSimpleType(false)
|
||||||
|
val classSymbol = irClass.symbol
|
||||||
|
return IrClassReferenceImpl(
|
||||||
|
startOffset,
|
||||||
|
endOffset,
|
||||||
|
compilerContext.irBuiltIns.kClassClass.starProjectedType,
|
||||||
|
classSymbol,
|
||||||
|
classType.approximateJvmErasure.toIrType()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// It is impossible to use star projections right away, because Array<Int>::class differ from Array<String>::class :
|
// It is impossible to use star projections right away, because Array<Int>::class differ from Array<String>::class :
|
||||||
// detailed information about type arguments required for class references on jvm
|
// detailed information about type arguments required for class references on jvm
|
||||||
private val KotlinType.approximateJvmErasure: KotlinType
|
private val KotlinType.approximateJvmErasure: KotlinType
|
||||||
|
|||||||
+15
-3
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
|||||||
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializer
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||||
@@ -58,7 +59,7 @@ class SerializableCompanionIrGenerator(
|
|||||||
)
|
)
|
||||||
) ?: return
|
) ?: return
|
||||||
|
|
||||||
val irSerializableClass = compilerContext.referenceClass(serializableDescriptor.fqNameSafe)?.owner ?: return
|
val irSerializableClass = if (irClass.isCompanion) irClass.parentAsClass else irClass
|
||||||
val serializableWithAlreadyPresent = irSerializableClass.annotations.any {
|
val serializableWithAlreadyPresent = irSerializableClass.annotations.any {
|
||||||
it.symbol.descriptor.constructedClass.fqNameSafe == annotationMarkerClass.fqNameSafe
|
it.symbol.descriptor.constructedClass.fqNameSafe == annotationMarkerClass.fqNameSafe
|
||||||
}
|
}
|
||||||
@@ -66,12 +67,23 @@ class SerializableCompanionIrGenerator(
|
|||||||
|
|
||||||
val annotationCtor = compilerContext.referenceConstructors(annotationMarkerClass.fqNameSafe).single { it.owner.isPrimary }
|
val annotationCtor = compilerContext.referenceConstructors(annotationMarkerClass.fqNameSafe).single { it.owner.isPrimary }
|
||||||
val annotationType = annotationMarkerClass.defaultType.toIrType()
|
val annotationType = annotationMarkerClass.defaultType.toIrType()
|
||||||
|
|
||||||
|
val serializerIrClass = if (serializableDescriptor.isInternalSerializable) {
|
||||||
|
// internally generated serializer always declared inside serializable class
|
||||||
|
irSerializableClass.declarations
|
||||||
|
.filterIsInstanceAnd<IrClass> { it.name == serializer.name }
|
||||||
|
.singleOrNull() ?: throw Exception("No class with name ${serializer.fqNameSafe}")
|
||||||
|
} else {
|
||||||
|
// FIXME referenceClass not supports local classes so it should be replaced in future
|
||||||
|
compilerContext.referenceClass(serializer.fqNameSafe)!!.owner
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val annotationCtorCall = IrConstructorCallImpl.fromSymbolDescriptor(startOffset, endOffset, annotationType, annotationCtor).apply {
|
val annotationCtorCall = IrConstructorCallImpl.fromSymbolDescriptor(startOffset, endOffset, annotationType, annotationCtor).apply {
|
||||||
val serializerType = serializer.toSimpleType(false)
|
|
||||||
putValueArgument(
|
putValueArgument(
|
||||||
0,
|
0,
|
||||||
createClassReference(
|
createClassReference(
|
||||||
serializerType,
|
serializerIrClass,
|
||||||
startOffset,
|
startOffset,
|
||||||
endOffset
|
endOffset
|
||||||
)
|
)
|
||||||
|
|||||||
+9
-3
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
|||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
|
||||||
import org.jetbrains.kotlin.utils.getOrPutNullable
|
import org.jetbrains.kotlin.utils.getOrPutNullable
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
|
||||||
@@ -172,10 +173,15 @@ class SerializableIrGenerator(
|
|||||||
|
|
||||||
private fun IrBlockBodyBuilder.getStaticSerialDescriptorExpr(): IrExpression {
|
private fun IrBlockBodyBuilder.getStaticSerialDescriptorExpr(): IrExpression {
|
||||||
val serializer = serializableDescriptor.classSerializer!!
|
val serializer = serializableDescriptor.classSerializer!!
|
||||||
val serialDescriptorGetter = compilerContext.referenceClass(serializer.fqNameSafe)?.getPropertyGetter(SERIAL_DESC_FIELD)
|
// internally generated serializer always declared inside serializable class
|
||||||
?: throw Exception("No class with name ${serializer.fqNameSafe}")
|
val serializerIrClass = irClass.declarations
|
||||||
|
.filterIsInstanceAnd<IrClass> { it.name == serializer.name }
|
||||||
|
.singleOrNull() ?: throw Exception("No class with name ${serializer.fqNameSafe}")
|
||||||
|
|
||||||
|
val serialDescriptorGetter =
|
||||||
|
serializerIrClass.getPropertyGetter(SERIAL_DESC_FIELD)!!
|
||||||
return irGet(
|
return irGet(
|
||||||
serialDescriptorGetter.owner.returnType,
|
serializerIrClass.defaultType,
|
||||||
irGetObject(serializer),
|
irGetObject(serializer),
|
||||||
serialDescriptorGetter.owner.symbol
|
serialDescriptorGetter.owner.symbol
|
||||||
)
|
)
|
||||||
|
|||||||
+11
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
import org.jetbrains.kotlin.backend.common.lower.irThrow
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
|
import org.jetbrains.kotlin.codegen.CompilationException
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.ir.types.*
|
|||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
|
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||||
@@ -458,6 +460,15 @@ open class SerializerIrGenerator(
|
|||||||
val ctor: IrConstructorSymbol = serializableSyntheticConstructor(serializableIrClass)
|
val ctor: IrConstructorSymbol = serializableSyntheticConstructor(serializableIrClass)
|
||||||
+irReturn(irInvoke(null, ctor, typeArgs, args))
|
+irReturn(irInvoke(null, ctor, typeArgs, args))
|
||||||
} else {
|
} else {
|
||||||
|
if (DescriptorUtils.isLocal(serializerDescriptor)) {
|
||||||
|
// if the serializer is local, then the serializable class too, since they must be in the same scope
|
||||||
|
throw CompilationException(
|
||||||
|
"External serializer class `${serializerDescriptor.fqNameSafe}` is local. Local external serializers are not supported yet.",
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
generateGoldenMaskCheck(bitMasks, properties, localSerialDesc.get())
|
generateGoldenMaskCheck(bitMasks, properties, localSerialDesc.get())
|
||||||
|
|
||||||
val ctor: IrConstructorSymbol =
|
val ctor: IrConstructorSymbol =
|
||||||
|
|||||||
+2
-1
@@ -16,7 +16,7 @@ import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
|
|||||||
public interface SerializationErrors {
|
public interface SerializationErrors {
|
||||||
DiagnosticFactory2<PsiElement, String, String> INLINE_CLASSES_NOT_SUPPORTED = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory2<PsiElement, String, String> INLINE_CLASSES_NOT_SUPPORTED = DiagnosticFactory2.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> PLUGIN_IS_NOT_ENABLED = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<PsiElement> PLUGIN_IS_NOT_ENABLED = DiagnosticFactory0.create(WARNING);
|
||||||
DiagnosticFactory0<PsiElement> LOCAL_CLASSES_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> ANONYMOUS_OBJECTS_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> INNER_CLASSES_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> INNER_CLASSES_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<PsiElement> EXPLICIT_SERIALIZABLE_IS_REQUIRED = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<PsiElement> EXPLICIT_SERIALIZABLE_IS_REQUIRED = DiagnosticFactory0.create(WARNING);
|
||||||
@@ -28,6 +28,7 @@ public interface SerializationErrors {
|
|||||||
DiagnosticFactory1<PsiElement, KotlinType> SERIALIZER_NOT_FOUND = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<PsiElement, KotlinType> SERIALIZER_NOT_FOUND = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> SERIALIZER_NULLABILITY_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> SERIALIZER_NULLABILITY_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
|
||||||
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, KotlinType> SERIALIZER_TYPE_INCOMPATIBLE = DiagnosticFactory3.create(WARNING);
|
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, KotlinType> SERIALIZER_TYPE_INCOMPATIBLE = DiagnosticFactory3.create(WARNING);
|
||||||
|
DiagnosticFactory1<PsiElement, KotlinType> LOCAL_SERIALIZER_USAGE = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> TRANSIENT_MISSING_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> TRANSIENT_MISSING_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
||||||
|
|||||||
+53
-7
@@ -29,6 +29,7 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertie
|
|||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContextUnchecked
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContextUnchecked
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serializableAnnotationFqName
|
||||||
|
|
||||||
internal val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice()
|
internal val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice()
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!descriptor.annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false
|
if (!descriptor.annotations.hasAnnotation(serializableAnnotationFqName)) return false
|
||||||
|
|
||||||
if (!serializationPluginEnabledOn(descriptor)) {
|
if (!serializationPluginEnabledOn(descriptor)) {
|
||||||
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED)
|
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED)
|
||||||
@@ -118,8 +119,8 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (DescriptorUtils.isLocal(descriptor)) {
|
if (descriptor.isAnonymousObjectOrContained) {
|
||||||
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.LOCAL_CLASSES_NOT_SUPPORTED)
|
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +141,11 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!descriptor.hasSerializableAnnotationWithoutArgs) return false
|
if (!descriptor.hasSerializableAnnotationWithoutArgs) {
|
||||||
|
// defined custom serializer
|
||||||
|
checkClassWithCustomSerializer(descriptor, declaration, trace)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (descriptor.serializableAnnotationIsUseless) {
|
if (descriptor.serializableAnnotationIsUseless) {
|
||||||
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
|
trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
|
||||||
@@ -158,9 +163,27 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkClassWithCustomSerializer(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
|
||||||
|
val annotationPsi = descriptor.findSerializableAnnotationDeclaration()
|
||||||
|
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
|
||||||
|
checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ClassDescriptor.isAnonymousObjectOrContained: Boolean
|
||||||
|
get() {
|
||||||
|
var current: DeclarationDescriptor? = this
|
||||||
|
while (current != null) {
|
||||||
|
if (DescriptorUtils.isAnonymousObject(current)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
current = current.containingDeclaration
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean {
|
private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean {
|
||||||
if (kind != ClassKind.ENUM_CLASS) return false
|
if (kind != ClassKind.ENUM_CLASS) return false
|
||||||
if (annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false
|
if (annotations.hasAnnotation(serializableAnnotationFqName)) return false
|
||||||
if (annotations.hasAnySerialAnnotation) return true
|
if (annotations.hasAnySerialAnnotation) return true
|
||||||
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
|
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
|
||||||
}
|
}
|
||||||
@@ -173,7 +196,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? {
|
private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? {
|
||||||
if (!descriptor.annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return null
|
if (!descriptor.annotations.hasAnnotation(serializableAnnotationFqName)) return null
|
||||||
if (!descriptor.isInternalSerializable) return null
|
if (!descriptor.isInternalSerializable) return null
|
||||||
if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user
|
if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user
|
||||||
|
|
||||||
@@ -234,6 +257,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
if (serializer != null) {
|
if (serializer != null) {
|
||||||
val element = ktType?.typeElement
|
val element = ktType?.typeElement
|
||||||
checkCustomSerializerMatch(it.module, it.type, it.descriptor, element, trace, propertyPsi)
|
checkCustomSerializerMatch(it.module, it.type, it.descriptor, element, trace, propertyPsi)
|
||||||
|
checkCustomSerializerIsNotLocal(it.module, it.descriptor, trace, propertyPsi)
|
||||||
checkSerializerNullability(it.type, serializer.defaultType, element, trace, propertyPsi)
|
checkSerializerNullability(it.type, serializer.defaultType, element, trace, propertyPsi)
|
||||||
generatorContextForAnalysis.checkTypeArguments(it.module, it.type, element, trace, propertyPsi)
|
generatorContextForAnalysis.checkTypeArguments(it.module, it.type, element, trace, propertyPsi)
|
||||||
} else {
|
} else {
|
||||||
@@ -286,6 +310,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
val serializer = findTypeSerializerOrContextUnchecked(module, type)
|
val serializer = findTypeSerializerOrContextUnchecked(module, type)
|
||||||
if (serializer != null) {
|
if (serializer != null) {
|
||||||
checkCustomSerializerMatch(module, type, type, element, trace, fallbackElement)
|
checkCustomSerializerMatch(module, type, type, element, trace, fallbackElement)
|
||||||
|
checkCustomSerializerIsNotLocal(module, type, trace, fallbackElement)
|
||||||
checkSerializerNullability(type, serializer.defaultType, element, trace, fallbackElement)
|
checkSerializerNullability(type, serializer.defaultType, element, trace, fallbackElement)
|
||||||
checkTypeArguments(module, type, element, trace, fallbackElement)
|
checkTypeArguments(module, type, element, trace, fallbackElement)
|
||||||
} else {
|
} else {
|
||||||
@@ -315,6 +340,27 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkCustomSerializerIsNotLocal(
|
||||||
|
module: ModuleDescriptor,
|
||||||
|
declaration: Annotated,
|
||||||
|
trace: BindingTrace,
|
||||||
|
declarationElement: PsiElement
|
||||||
|
) {
|
||||||
|
val serializerType = declaration.annotations.serializableWith(module) ?: return
|
||||||
|
val serializerDescriptor = serializerType.toClassDescriptor ?: return
|
||||||
|
|
||||||
|
if (DescriptorUtils.isLocal(serializerDescriptor)) {
|
||||||
|
val element = declaration.findSerializableAnnotationDeclaration() ?: declarationElement
|
||||||
|
|
||||||
|
trace.report(
|
||||||
|
SerializationErrors.LOCAL_SERIALIZER_USAGE.on(
|
||||||
|
element,
|
||||||
|
serializerType
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkSerializerNullability(
|
private fun checkSerializerNullability(
|
||||||
classType: KotlinType,
|
classType: KotlinType,
|
||||||
serializerType: KotlinType,
|
serializerType: KotlinType,
|
||||||
@@ -344,4 +390,4 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean
|
internal val ClassDescriptor.serializableAnnotationIsUseless: Boolean
|
||||||
get() = hasSerializableAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum()
|
get() = hasSerializableAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && !isSerializableEnum()
|
||||||
|
|||||||
+7
-2
@@ -26,8 +26,8 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
|||||||
"Make sure that you've setup your buildscript correctly and re-import project."
|
"Make sure that you've setup your buildscript correctly and re-import project."
|
||||||
)
|
)
|
||||||
MAP.put(
|
MAP.put(
|
||||||
SerializationErrors.LOCAL_CLASSES_NOT_SUPPORTED,
|
SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED,
|
||||||
"Local classes and anonymous objects can not be serializable."
|
"Anonymous objects or contained in it classes can not be serializable."
|
||||||
)
|
)
|
||||||
MAP.put(
|
MAP.put(
|
||||||
SerializationErrors.INNER_CLASSES_NOT_SUPPORTED,
|
SerializationErrors.INNER_CLASSES_NOT_SUPPORTED,
|
||||||
@@ -74,6 +74,11 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
|||||||
Renderers.RENDER_TYPE,
|
Renderers.RENDER_TYPE,
|
||||||
Renderers.RENDER_TYPE
|
Renderers.RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
MAP.put(
|
||||||
|
SerializationErrors.LOCAL_SERIALIZER_USAGE,
|
||||||
|
"Class ''{0}'' can't be used as a serializer since it is local",
|
||||||
|
Renderers.RENDER_TYPE
|
||||||
|
)
|
||||||
MAP.put(
|
MAP.put(
|
||||||
SerializationErrors.TRANSIENT_MISSING_INITIALIZER,
|
SerializationErrors.TRANSIENT_MISSING_INITIALIZER,
|
||||||
"This property is marked as @Transient and therefore must have an initializing expression"
|
"This property is marked as @Transient and therefore must have an initializing expression"
|
||||||
|
|||||||
plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/LocalAndAnonymous.kt
Vendored
+31
-6
@@ -1,17 +1,42 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
|
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE,-EXPERIMENTAL_API_USAGE_ERROR,-EXPERIMENTAL_API_USAGE
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// SKIP_TXT
|
// SKIP_TXT
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.descriptors.*
|
||||||
|
import kotlinx.serialization.encoding.*
|
||||||
|
|
||||||
fun container() {
|
fun container() {
|
||||||
<!LOCAL_CLASSES_NOT_SUPPORTED!>@Serializable<!>
|
@Serializable
|
||||||
class X
|
class X // local classes are allowed
|
||||||
|
|
||||||
val y = <!LOCAL_CLASSES_NOT_SUPPORTED!>@Serializable<!> object {}
|
val y = <!ANONYMOUS_OBJECTS_NOT_SUPPORTED!>@Serializable<!> object {
|
||||||
|
fun inObjectFun() {
|
||||||
|
<!ANONYMOUS_OBJECTS_NOT_SUPPORTED!>@Serializable<!>
|
||||||
|
class X // local classes in anonymous object functions are not allowed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LocalSerializer : KSerializer<Any?> {
|
||||||
|
override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", PrimitiveKind.INT)
|
||||||
|
override fun serialize(encoder: Encoder, value: Any?) {
|
||||||
|
encoder.encodeNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): Any? {
|
||||||
|
return decoder.decodeNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class WithLocalSerializerInProperty(<!LOCAL_SERIALIZER_USAGE!>@Serializable(with = LocalSerializer::class)<!> val x: Any?)
|
||||||
|
|
||||||
|
<!LOCAL_SERIALIZER_USAGE, SERIALIZER_TYPE_INCOMPATIBLE!>@Serializable(with = LocalSerializer::class)<!>
|
||||||
|
data class WithLocalSerializer(val i: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
val topLevelAnon = <!LOCAL_CLASSES_NOT_SUPPORTED!>@Serializable<!> object {}
|
val topLevelAnon = <!ANONYMOUS_OBJECTS_NOT_SUPPORTED!>@Serializable<!> object {}
|
||||||
|
|
||||||
@Serializable class A {
|
@Serializable class A {
|
||||||
@Serializable class B // nested classes are allowed
|
@Serializable class B // nested classes are allowed
|
||||||
@@ -19,4 +44,4 @@ val topLevelAnon = <!LOCAL_CLASSES_NOT_SUPPORTED!>@Serializable<!> object {}
|
|||||||
<!INNER_CLASSES_NOT_SUPPORTED!>@Serializable<!> inner class C // inner classes are not
|
<!INNER_CLASSES_NOT_SUPPORTED!>@Serializable<!> inner class C // inner classes are not
|
||||||
|
|
||||||
@Serializable object F {} // regular named object, OK
|
@Serializable object F {} // regular named object, OK
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -14,6 +14,9 @@ object BazSerializer: KSerializer<Baz>
|
|||||||
@Serializer(forClass = Baz::class)
|
@Serializer(forClass = Baz::class)
|
||||||
object NullableBazSerializer: KSerializer<Baz?>
|
object NullableBazSerializer: KSerializer<Baz?>
|
||||||
|
|
||||||
|
<!SERIALIZER_TYPE_INCOMPATIBLE!>@Serializable(with = BazSerializer::class)<!>
|
||||||
|
class Biz(val i: Int)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Foo(@Serializable(with = BazSerializer::class) val i: <!SERIALIZER_TYPE_INCOMPATIBLE!>Bar<!>)
|
class Foo(@Serializable(with = BazSerializer::class) val i: <!SERIALIZER_TYPE_INCOMPATIBLE!>Bar<!>)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user