[KxSerialization] Added inspections on custom serializer parameters
Added inspections to check: - custom serializer on class has as many parameters in primary constructor as the serializable class of type arguments - all parameters in custom serializer has `KSerializer` type - property in serializable class not parametrized by type parameter - custom serializer on property of serializable class have no parameters in primary constructor
This commit is contained in:
committed by
Space Team
parent
9532172a22
commit
84ad12be57
+4
@@ -39,6 +39,8 @@ public interface SerializationErrors {
|
|||||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> ABSTRACT_SERIALIZER_TYPE = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> ABSTRACT_SERIALIZER_TYPE = 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);
|
DiagnosticFactory1<PsiElement, KotlinType> LOCAL_SERIALIZER_USAGE = DiagnosticFactory1.create(ERROR);
|
||||||
|
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, String> CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT = DiagnosticFactory3.create(ERROR);
|
||||||
|
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, String> CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE = DiagnosticFactory3.create(ERROR);
|
||||||
DiagnosticFactory0<PsiElement> TRANSIENT_MISSING_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> TRANSIENT_MISSING_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<PsiElement> GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<PsiElement> GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||||
@@ -61,6 +63,8 @@ public interface SerializationErrors {
|
|||||||
|
|
||||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_NOT_SERIALIZABLE = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_NOT_SERIALIZABLE = DiagnosticFactory2.create(ERROR);
|
||||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR);
|
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR);
|
||||||
|
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, String> EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR = DiagnosticFactory3.create(ERROR);
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
Object _initializer = new Object() {
|
Object _initializer = new Object() {
|
||||||
|
|||||||
+90
-10
@@ -27,12 +27,14 @@ import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
|||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isEnum
|
import org.jetbrains.kotlin.types.typeUtil.isEnum
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||||
import org.jetbrains.kotlin.util.slicedMap.Slices
|
import org.jetbrains.kotlin.util.slicedMap.Slices
|
||||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
|
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationErrors.EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationErrors.EXTERNAL_SERIALIZER_USELESS
|
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationErrors.EXTERNAL_SERIALIZER_USELESS
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD_NAME
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD_NAME
|
||||||
@@ -89,6 +91,25 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
val serializableDescriptor = serializableKType.toClassDescriptor ?: return
|
val serializableDescriptor = serializableKType.toClassDescriptor ?: return
|
||||||
val props = SerializableProperties(serializableDescriptor, trace.bindingContext)
|
val props = SerializableProperties(serializableDescriptor, trace.bindingContext)
|
||||||
|
|
||||||
|
val parametersCount = serializableKType.arguments.size
|
||||||
|
if (parametersCount > 0) {
|
||||||
|
val hasSuitableConstructor = classDescriptor.constructors.any { constructor ->
|
||||||
|
constructor.valueParameters.size == parametersCount
|
||||||
|
&& constructor.valueParameters.all { param -> isKSerializer(param.type) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSuitableConstructor) {
|
||||||
|
trace.report(
|
||||||
|
EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR.on(
|
||||||
|
declaration,
|
||||||
|
classDescriptor.defaultType,
|
||||||
|
serializableKType,
|
||||||
|
parametersCount.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val descriptorOverridden = classDescriptor.unsubstitutedMemberScope
|
val descriptorOverridden = classDescriptor.unsubstitutedMemberScope
|
||||||
.getContributedVariables(SERIAL_DESC_FIELD_NAME, NoLookupLocation.FROM_BACKEND).singleOrNull {
|
.getContributedVariables(SERIAL_DESC_FIELD_NAME, NoLookupLocation.FROM_BACKEND).singleOrNull {
|
||||||
it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED
|
it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||||
@@ -325,6 +346,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration()
|
val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration()
|
||||||
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
|
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
|
||||||
checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration)
|
checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration)
|
||||||
|
checkCustomSerializerParameters(descriptor.module, descriptor, descriptor.defaultType, annotationPsi, declaration, trace)
|
||||||
checkCustomSerializerNotAbstract(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
|
checkCustomSerializerNotAbstract(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,14 +463,9 @@ 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)
|
||||||
checkCustomSerializerNotAbstract(
|
val annotationPsi = it.descriptor.findSerializableOrMetaAnnotationDeclaration()
|
||||||
it.module,
|
checkCustomSerializerNotAbstract(it.module, it.type, it.descriptor, annotationPsi, trace, propertyPsi)
|
||||||
it.type,
|
checkCustomSerializerParameters(it.module, it.descriptor, it.type, annotationPsi, propertyPsi, trace)
|
||||||
it.descriptor,
|
|
||||||
it.descriptor.findSerializableOrMetaAnnotationDeclaration(),
|
|
||||||
trace,
|
|
||||||
propertyPsi
|
|
||||||
)
|
|
||||||
checkCustomSerializerIsNotLocal(it.module, it.descriptor, 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)
|
||||||
@@ -516,8 +533,14 @@ 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)
|
type.annotations.serializableWith(module)?.let {
|
||||||
checkCustomSerializerIsNotLocal(module, type, trace, fallbackElement)
|
checkCustomSerializerMatch(module, type, type, element, trace, fallbackElement)
|
||||||
|
checkCustomSerializerIsNotLocal(module, type, trace, fallbackElement)
|
||||||
|
|
||||||
|
val annotationElement = type.findSerializableAnnotationDeclaration()
|
||||||
|
checkCustomSerializerParameters(module, type, type, annotationElement, fallbackElement, trace)
|
||||||
|
checkCustomSerializerNotAbstract(module, type, type, annotationElement, 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 {
|
||||||
@@ -591,6 +614,63 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkCustomSerializerParameters(
|
||||||
|
module: ModuleDescriptor,
|
||||||
|
declaration: Annotated,
|
||||||
|
serializableType: KotlinType,
|
||||||
|
element: KtElement?,
|
||||||
|
fallbackElement: PsiElement,
|
||||||
|
trace: BindingTrace,
|
||||||
|
) {
|
||||||
|
val serializerType = declaration.annotations.serializableWith(module) ?: return
|
||||||
|
val serializerDescriptor = serializerType.toClassDescriptor ?: return
|
||||||
|
|
||||||
|
if (serializerDescriptor.classId in SerializersClassIds.setOfSpecialSerializers) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val primaryConstructor = serializerDescriptor.constructors.singleOrNull { constructor -> constructor.isPrimary } ?: return
|
||||||
|
|
||||||
|
val targetElement = element ?: fallbackElement
|
||||||
|
|
||||||
|
val isExternalSerializer = serializerDescriptor.serializerForClass != null
|
||||||
|
if ( // for external serializer, the verification will be carried out at the definition
|
||||||
|
!isExternalSerializer
|
||||||
|
// it is allowed that parameters are not passed to regular serializers at all
|
||||||
|
&& primaryConstructor.valueParameters.isNotEmpty()
|
||||||
|
// if the parameters are still specified, then their number must match in the serializable class and constructor
|
||||||
|
&& primaryConstructor.valueParameters.size != serializableType.arguments.size
|
||||||
|
) {
|
||||||
|
val message = if (serializableType.arguments.isNotEmpty()) {
|
||||||
|
"expected no parameters or ${serializableType.arguments.size}, but has ${primaryConstructor.valueParameters.size} parameters"
|
||||||
|
} else {
|
||||||
|
"expected no parameters but has ${primaryConstructor.valueParameters.size} parameters"
|
||||||
|
}
|
||||||
|
|
||||||
|
trace.report(
|
||||||
|
SerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT.on(
|
||||||
|
targetElement,
|
||||||
|
serializerType,
|
||||||
|
serializableType,
|
||||||
|
message
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryConstructor.valueParameters.forEach { param ->
|
||||||
|
if (!isKSerializer(param.type)) {
|
||||||
|
trace.report(
|
||||||
|
SerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE.on(
|
||||||
|
targetElement,
|
||||||
|
serializerType,
|
||||||
|
serializableType,
|
||||||
|
param.name.asString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkSerializerNullability(
|
private fun checkSerializerNullability(
|
||||||
classType: KotlinType,
|
classType: KotlinType,
|
||||||
serializerType: KotlinType,
|
serializerType: KotlinType,
|
||||||
|
|||||||
+22
@@ -118,6 +118,20 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
|||||||
"Class ''{0}'' can't be used as a serializer since it is local",
|
"Class ''{0}'' can't be used as a serializer since it is local",
|
||||||
Renderers.RENDER_TYPE
|
Renderers.RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
MAP.put(
|
||||||
|
SerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT,
|
||||||
|
"Custom serializer ''{0}'' can not be used for ''{1}'' since it has an invalid number of parameters in primary constructor: {2}",
|
||||||
|
Renderers.RENDER_TYPE,
|
||||||
|
Renderers.RENDER_TYPE,
|
||||||
|
CommonRenderers.STRING
|
||||||
|
)
|
||||||
|
MAP.put(
|
||||||
|
SerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE,
|
||||||
|
"Custom serializer ''{0}'' can not be used for ''{1}'', type of parameter ''{2}'' in serializer's primary constructor should be ''KSerializer''",
|
||||||
|
Renderers.RENDER_TYPE,
|
||||||
|
Renderers.RENDER_TYPE,
|
||||||
|
CommonRenderers.STRING
|
||||||
|
)
|
||||||
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"
|
||||||
@@ -194,5 +208,13 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
|||||||
Renderers.RENDER_TYPE,
|
Renderers.RENDER_TYPE,
|
||||||
Renderers.RENDER_TYPE
|
Renderers.RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
MAP.put(
|
||||||
|
SerializationErrors.EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR,
|
||||||
|
"Cannot generate external serializer ''{0}'': it must have a constructor with {2} value parameters, because class ''{1}'' has type parameters",
|
||||||
|
Renderers.RENDER_TYPE,
|
||||||
|
Renderers.RENDER_TYPE,
|
||||||
|
CommonRenderers.STRING
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -212,7 +212,7 @@ private val ClassDescriptor.hasSerializableAnnotationWithArgs: Boolean
|
|||||||
return psi.valueArguments.isNotEmpty()
|
return psi.valueArguments.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
|
internal fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
|
||||||
val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName) as? LazyAnnotationDescriptor
|
val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName) as? LazyAnnotationDescriptor
|
||||||
return lazyDesc?.annotationEntry
|
return lazyDesc?.annotationEntry
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -36,6 +36,9 @@ object FirSerializationErrors {
|
|||||||
val SERIALIZER_TYPE_INCOMPATIBLE by warning3<PsiElement, ConeKotlinType, ConeKotlinType, ConeKotlinType>()
|
val SERIALIZER_TYPE_INCOMPATIBLE by warning3<PsiElement, ConeKotlinType, ConeKotlinType, ConeKotlinType>()
|
||||||
val ABSTRACT_SERIALIZER_TYPE by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
|
val ABSTRACT_SERIALIZER_TYPE by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
|
||||||
val LOCAL_SERIALIZER_USAGE by error1<PsiElement, ConeKotlinType>()
|
val LOCAL_SERIALIZER_USAGE by error1<PsiElement, ConeKotlinType>()
|
||||||
|
val CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT by error3<PsiElement, ConeKotlinType, ConeKotlinType, String>()
|
||||||
|
val CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE by error3<PsiElement, ConeKotlinType, ConeKotlinType, String>()
|
||||||
|
|
||||||
val GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED by error0<PsiElement>()
|
val GENERIC_ARRAY_ELEMENT_NOT_SUPPORTED by error0<PsiElement>()
|
||||||
val TRANSIENT_MISSING_INITIALIZER by error0<PsiElement>()
|
val TRANSIENT_MISSING_INITIALIZER by error0<PsiElement>()
|
||||||
|
|
||||||
@@ -52,6 +55,7 @@ object FirSerializationErrors {
|
|||||||
val EXTERNAL_SERIALIZER_USELESS by warning1<PsiElement, FirClassSymbol<*>>()
|
val EXTERNAL_SERIALIZER_USELESS by warning1<PsiElement, FirClassSymbol<*>>()
|
||||||
val EXTERNAL_CLASS_NOT_SERIALIZABLE by error2<PsiElement, FirClassSymbol<*>, ConeKotlinType>()
|
val EXTERNAL_CLASS_NOT_SERIALIZABLE by error2<PsiElement, FirClassSymbol<*>, ConeKotlinType>()
|
||||||
val EXTERNAL_CLASS_IN_ANOTHER_MODULE by error2<PsiElement, FirClassSymbol<*>, ConeKotlinType>()
|
val EXTERNAL_CLASS_IN_ANOTHER_MODULE by error2<PsiElement, FirClassSymbol<*>, ConeKotlinType>()
|
||||||
|
val EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR by error3<PsiElement, FirClassSymbol<*>, ConeKotlinType, String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
RootDiagnosticRendererFactory.registerFactory(KtDefaultErrorMessagesSerialization)
|
RootDiagnosticRendererFactory.registerFactory(KtDefaultErrorMessagesSerialization)
|
||||||
|
|||||||
+102
-16
@@ -14,6 +14,9 @@ import org.jetbrains.kotlin.diagnostics.reportOn
|
|||||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
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.declaration.FirClassChecker
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.primaryConstructorSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.isSingleFieldValueClass
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.annotationPlatformSupport
|
import org.jetbrains.kotlin.fir.declarations.annotationPlatformSupport
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||||
@@ -30,6 +33,7 @@ import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_CLASS_I
|
|||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.RuntimeVersions
|
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.RuntimeVersions
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.fir.*
|
import org.jetbrains.kotlinx.serialization.compiler.fir.*
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.FirSerializationErrors.EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.FirSerializationErrors.EXTERNAL_SERIALIZER_USELESS
|
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.FirSerializationErrors.EXTERNAL_SERIALIZER_USELESS
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
|
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSerializerOrContextUnchecked
|
import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSerializerOrContextUnchecked
|
||||||
@@ -91,6 +95,25 @@ object FirSerializationPluginClassChecker : FirClassChecker(MppCheckerKind.Commo
|
|||||||
val serializableClassSymbol = serializableKType.toRegularClassSymbol(session) ?: return
|
val serializableClassSymbol = serializableKType.toRegularClassSymbol(session) ?: return
|
||||||
|
|
||||||
val declarations = classSymbol.declarationSymbols
|
val declarations = classSymbol.declarationSymbols
|
||||||
|
|
||||||
|
val parametersCount = serializableKType.typeArguments.size
|
||||||
|
if (parametersCount > 0) {
|
||||||
|
val hasSuitableConstructor = declarations.filterIsInstance<FirConstructorSymbol>().any { constructor ->
|
||||||
|
constructor.valueParameterSymbols.size == parametersCount
|
||||||
|
&& constructor.valueParameterSymbols.all { param -> param.resolvedReturnType.isKSerializer }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSuitableConstructor) {
|
||||||
|
reporter.reportOn(
|
||||||
|
classSymbol.source,
|
||||||
|
EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR,
|
||||||
|
classSymbol,
|
||||||
|
serializableKType,
|
||||||
|
parametersCount.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val descriptorOverridden = declarations.filterIsInstance<FirPropertySymbol>().singleOrNull {
|
val descriptorOverridden = declarations.filterIsInstance<FirPropertySymbol>().singleOrNull {
|
||||||
it.name == SerialEntityNames.SERIAL_DESC_FIELD_NAME
|
it.name == SerialEntityNames.SERIAL_DESC_FIELD_NAME
|
||||||
&& it.isOverride
|
&& it.isOverride
|
||||||
@@ -361,8 +384,12 @@ object FirSerializationPluginClassChecker : FirClassChecker(MppCheckerKind.Commo
|
|||||||
context(CheckerContext)
|
context(CheckerContext)
|
||||||
private fun checkClassWithCustomSerializer(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
private fun checkClassWithCustomSerializer(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
||||||
val serializerType = classSymbol.getSerializableWith(session)?.fullyExpandedType(session) ?: return
|
val serializerType = classSymbol.getSerializableWith(session)?.fullyExpandedType(session) ?: return
|
||||||
checkCustomSerializerMatch(classSymbol, source = null, classSymbol.defaultType(), serializerType, reporter)
|
|
||||||
|
val serializerForType = serializerType.serializerForType(session)?.fullyExpandedType(session)
|
||||||
|
|
||||||
|
checkCustomSerializerMatch(classSymbol, source = null, classSymbol.defaultType(), serializerType, serializerForType, reporter)
|
||||||
checkCustomSerializerIsNotLocal(source = null, classSymbol, serializerType, reporter)
|
checkCustomSerializerIsNotLocal(source = null, classSymbol, serializerType, reporter)
|
||||||
|
checkCustomSerializerParameters(classSymbol, null, serializerType, serializerForType, reporter)
|
||||||
checkCustomSerializerNotAbstract(classSymbol, source = null, serializerType, reporter)
|
checkCustomSerializerNotAbstract(classSymbol, source = null, serializerType, reporter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,25 +494,20 @@ object FirSerializationPluginClassChecker : FirClassChecker(MppCheckerKind.Commo
|
|||||||
// and would not be compatible on direct comparison
|
// and would not be compatible on direct comparison
|
||||||
if (customSerializerType.classId in SerializersClassIds.setOfSpecialSerializers) return
|
if (customSerializerType.classId in SerializersClassIds.setOfSpecialSerializers) return
|
||||||
|
|
||||||
|
val serializerForType = customSerializerType.serializerForType(session)?.fullyExpandedType(session)
|
||||||
|
|
||||||
checkCustomSerializerMatch(
|
checkCustomSerializerMatch(
|
||||||
classSymbol,
|
classSymbol,
|
||||||
source = typeRef.source ?: propertySymbol.source,
|
source = typeRef.source ?: propertySymbol.source,
|
||||||
propertyType,
|
propertyType,
|
||||||
customSerializerType,
|
customSerializerType,
|
||||||
|
serializerForType,
|
||||||
reporter
|
reporter
|
||||||
)
|
)
|
||||||
checkCustomSerializerNotAbstract(
|
val annotationElement = propertySymbol.serializableAnnotation(needArguments = false, session)?.source
|
||||||
classSymbol,
|
checkCustomSerializerNotAbstract(classSymbol, source = annotationElement, customSerializerType, reporter)
|
||||||
source = propertySymbol.serializableAnnotation(needArguments = false, session)?.source,
|
checkCustomSerializerIsNotLocal(source = annotationElement, classSymbol, customSerializerType, reporter)
|
||||||
customSerializerType,
|
checkCustomSerializerParameters(classSymbol, annotationElement, customSerializerType, serializerForType, reporter)
|
||||||
reporter
|
|
||||||
)
|
|
||||||
checkCustomSerializerIsNotLocal(
|
|
||||||
source = propertySymbol.serializableAnnotation(needArguments = false, session)?.source,
|
|
||||||
classSymbol,
|
|
||||||
customSerializerType,
|
|
||||||
reporter
|
|
||||||
)
|
|
||||||
checkSerializerNullability(propertyType, customSerializerType, source, reporter)
|
checkSerializerNullability(propertyType, customSerializerType, source, reporter)
|
||||||
} else {
|
} else {
|
||||||
checkType(typeRef, source, reporter)
|
checkType(typeRef, source, reporter)
|
||||||
@@ -543,8 +565,14 @@ object FirSerializationPluginClassChecker : FirClassChecker(MppCheckerKind.Commo
|
|||||||
if (serializer != null) {
|
if (serializer != null) {
|
||||||
val classSymbol = type.toRegularClassSymbol(session) ?: return
|
val classSymbol = type.toRegularClassSymbol(session) ?: return
|
||||||
type.serializableWith?.fullyExpandedType(session)?.let { serializerType ->
|
type.serializableWith?.fullyExpandedType(session)?.let { serializerType ->
|
||||||
checkCustomSerializerMatch(classSymbol, typeSource, type, serializerType, reporter)
|
val serializerForType = serializerType.serializerForType(session)?.fullyExpandedType(session)
|
||||||
|
|
||||||
|
checkCustomSerializerMatch(classSymbol, typeSource, type, serializerType, serializerForType, reporter)
|
||||||
checkCustomSerializerIsNotLocal(typeSource, classSymbol, serializerType, reporter)
|
checkCustomSerializerIsNotLocal(typeSource, classSymbol, serializerType, reporter)
|
||||||
|
|
||||||
|
val annotationElement = type.customAnnotations.serializableAnnotation(session)?.source ?: typeSource
|
||||||
|
checkCustomSerializerParameters(classSymbol, annotationElement, serializerType, serializerForType, reporter)
|
||||||
|
checkCustomSerializerNotAbstract(classSymbol, annotationElement, serializerType, reporter)
|
||||||
checkSerializerNullability(type, serializerType, typeSource, reporter)
|
checkSerializerNullability(type, serializerType, typeSource, reporter)
|
||||||
}
|
}
|
||||||
checkTypeArguments(typeRef, typeSource, reporter)
|
checkTypeArguments(typeRef, typeSource, reporter)
|
||||||
@@ -562,9 +590,10 @@ object FirSerializationPluginClassChecker : FirClassChecker(MppCheckerKind.Commo
|
|||||||
source: KtSourceElement?,
|
source: KtSourceElement?,
|
||||||
declarationType: ConeKotlinType,
|
declarationType: ConeKotlinType,
|
||||||
serializerType: ConeKotlinType,
|
serializerType: ConeKotlinType,
|
||||||
|
serializerForType: ConeKotlinType?,
|
||||||
reporter: DiagnosticReporter
|
reporter: DiagnosticReporter
|
||||||
) {
|
) {
|
||||||
val serializerForType = serializerType.serializerForType(session)?.fullyExpandedType(session) ?: return
|
serializerForType ?: return
|
||||||
|
|
||||||
val declarationTypeClassId = declarationType.classId
|
val declarationTypeClassId = declarationType.classId
|
||||||
if (declarationTypeClassId == null || declarationTypeClassId != serializerForType.classId) {
|
if (declarationTypeClassId == null || declarationTypeClassId != serializerForType.classId) {
|
||||||
@@ -583,7 +612,7 @@ object FirSerializationPluginClassChecker : FirClassChecker(MppCheckerKind.Commo
|
|||||||
containingClassSymbol: FirClassSymbol<*>,
|
containingClassSymbol: FirClassSymbol<*>,
|
||||||
source: KtSourceElement?,
|
source: KtSourceElement?,
|
||||||
serializerType: ConeKotlinType,
|
serializerType: ConeKotlinType,
|
||||||
reporter: DiagnosticReporter
|
reporter: DiagnosticReporter,
|
||||||
) {
|
) {
|
||||||
if (with(session) { serializerType.isAbstractOrSealedOrInterface }) {
|
if (with(session) { serializerType.isAbstractOrSealedOrInterface }) {
|
||||||
reporter.reportOn(
|
reporter.reportOn(
|
||||||
@@ -595,6 +624,63 @@ object FirSerializationPluginClassChecker : FirClassChecker(MppCheckerKind.Commo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context(CheckerContext)
|
||||||
|
private fun checkCustomSerializerParameters(
|
||||||
|
containingClassSymbol: FirClassSymbol<*>,
|
||||||
|
source: KtSourceElement?,
|
||||||
|
serializerType: ConeKotlinType,
|
||||||
|
serializerForType: ConeKotlinType?,
|
||||||
|
reporter: DiagnosticReporter,
|
||||||
|
) {
|
||||||
|
serializerForType ?: return
|
||||||
|
|
||||||
|
// Do not account for @Polymorphic and @Contextual, as they are serializers for T: Any
|
||||||
|
// and would not be compatible on direct comparison
|
||||||
|
if (serializerType.classId in SerializersClassIds.setOfSpecialSerializers) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val primaryConstructor = serializerType.toRegularClassSymbol(session)?.primaryConstructorSymbol(session) ?: return
|
||||||
|
|
||||||
|
val targetElement by lazy { source ?: containingClassSymbol.serializableOrMetaAnnotationSource }
|
||||||
|
|
||||||
|
val isExternalSerializer = serializerType.toRegularClassSymbol(session)?.getSerializerAnnotation(session) != null
|
||||||
|
|
||||||
|
if ( // for external serializer, the verification will be carried out at the definition
|
||||||
|
!isExternalSerializer
|
||||||
|
// it is allowed that parameters are not passed in regular serializers at all
|
||||||
|
&& primaryConstructor.valueParameterSymbols.isNotEmpty()
|
||||||
|
// if the parameters are still specified, then their number must match in the serializable class and constructor
|
||||||
|
&& serializerForType.typeArguments.size != primaryConstructor.valueParameterSymbols.size
|
||||||
|
) {
|
||||||
|
val message = if (serializerForType.typeArguments.isNotEmpty()) {
|
||||||
|
"expected no parameters or ${serializerForType.typeArguments.size}, but has ${primaryConstructor.valueParameterSymbols.size} parameters"
|
||||||
|
} else {
|
||||||
|
"expected no parameters but has ${primaryConstructor.valueParameterSymbols.size} parameters"
|
||||||
|
}
|
||||||
|
reporter.reportOn(
|
||||||
|
targetElement,
|
||||||
|
FirSerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT,
|
||||||
|
serializerType,
|
||||||
|
serializerForType,
|
||||||
|
message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryConstructor.valueParameterSymbols.forEach { param ->
|
||||||
|
val returnType = param.resolvedReturnType
|
||||||
|
if (!returnType.isKSerializer) {
|
||||||
|
reporter.reportOn(
|
||||||
|
targetElement,
|
||||||
|
FirSerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE,
|
||||||
|
serializerType,
|
||||||
|
serializerForType,
|
||||||
|
param.name.asString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context(CheckerContext)
|
context(CheckerContext)
|
||||||
private fun checkCustomSerializerIsNotLocal(
|
private fun checkCustomSerializerIsNotLocal(
|
||||||
source: KtSourceElement?,
|
source: KtSourceElement?,
|
||||||
|
|||||||
+22
@@ -115,6 +115,20 @@ object KtDefaultErrorMessagesSerialization : BaseDiagnosticRendererFactory() {
|
|||||||
"Class ''{0}'' can't be used as a serializer since it is local",
|
"Class ''{0}'' can't be used as a serializer since it is local",
|
||||||
FirDiagnosticRenderers.RENDER_TYPE
|
FirDiagnosticRenderers.RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
|
FirSerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT,
|
||||||
|
"Custom serializer ''{0}'' can not be used for ''{1}'' since it has an invalid number of parameters in primary constructor: {2}",
|
||||||
|
FirDiagnosticRenderers.RENDER_TYPE,
|
||||||
|
FirDiagnosticRenderers.RENDER_TYPE,
|
||||||
|
CommonRenderers.STRING
|
||||||
|
)
|
||||||
|
put(
|
||||||
|
FirSerializationErrors.CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE,
|
||||||
|
"Custom serializer ''{0}'' can not be used for ''{1}'', type of parameter ''{2}'' in serializer's primary constructor should be ''KSerializer''",
|
||||||
|
FirDiagnosticRenderers.RENDER_TYPE,
|
||||||
|
FirDiagnosticRenderers.RENDER_TYPE,
|
||||||
|
CommonRenderers.STRING
|
||||||
|
)
|
||||||
put(
|
put(
|
||||||
FirSerializationErrors.TRANSIENT_MISSING_INITIALIZER,
|
FirSerializationErrors.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"
|
||||||
@@ -183,5 +197,13 @@ object KtDefaultErrorMessagesSerialization : BaseDiagnosticRendererFactory() {
|
|||||||
FirDiagnosticRenderers.SYMBOL,
|
FirDiagnosticRenderers.SYMBOL,
|
||||||
FirDiagnosticRenderers.RENDER_TYPE
|
FirDiagnosticRenderers.RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
put(
|
||||||
|
FirSerializationErrors.EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR,
|
||||||
|
"Cannot generate external serializer ''{0}'': it must have a constructor with {2} value parameters, because class ''{1}'' has type parameters",
|
||||||
|
FirDiagnosticRenderers.SYMBOL,
|
||||||
|
FirDiagnosticRenderers.RENDER_TYPE,
|
||||||
|
CommonRenderers.STRING
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
|
||||||
|
// WITH_STDLIB
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
|
||||||
|
@Serializable(with = PolymorphicSerializer::class)
|
||||||
|
interface ExplicitlyPolymorphic
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Holder(
|
||||||
|
val poly: ExplicitlyPolymorphic
|
||||||
|
)
|
||||||
|
fun box(): String {
|
||||||
|
val kind = ExplicitlyPolymorphic.serializer().descriptor.kind.toString()
|
||||||
|
if (kind != "OPEN") return kind
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.descriptors.*
|
||||||
|
import kotlinx.serialization.encoding.*
|
||||||
|
|
||||||
|
|
||||||
|
class Prop1<T>(val t: T)
|
||||||
|
|
||||||
|
class Prop2<T, R>(val t: T, val r: R)
|
||||||
|
|
||||||
|
<!EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR!>@Serializer(forClass = Prop1::class)
|
||||||
|
class ExternalSerializer0_1<!>
|
||||||
|
|
||||||
|
@Serializer(forClass = Prop1::class)
|
||||||
|
class ExternalSerializer1_1(val typeSerial0: KSerializer<Any>)
|
||||||
|
|
||||||
|
@Serializer(forClass = Prop1::class)
|
||||||
|
class ExternalSerializer1_1Secondary() {
|
||||||
|
var typeSerial0: KSerializer<Any>? = null
|
||||||
|
constructor(typeSerial0: KSerializer<Any>) : this() {
|
||||||
|
this.typeSerial0 = typeSerial0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<!EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR!>@Serializer(forClass = Prop2::class)
|
||||||
|
class ExternalSerializer0_2<!>
|
||||||
|
|
||||||
|
<!EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR!>@Serializer(forClass = Prop2::class)
|
||||||
|
class ExternalSerializer1_2(val typeSerial0: KSerializer<Any>)<!>
|
||||||
|
|
||||||
|
<!EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR!>@Serializer(forClass = Prop2::class)
|
||||||
|
class ExternalSerializer1_2Secondary() {
|
||||||
|
var typeSerial0: KSerializer<Any>? = null
|
||||||
|
|
||||||
|
constructor(typeSerial0: KSerializer<Any>) : this() {
|
||||||
|
this.typeSerial0 = typeSerial0
|
||||||
|
}
|
||||||
|
}<!>
|
||||||
|
|
||||||
|
@Serializer(forClass = Prop2::class)
|
||||||
|
class ExternalSerializer2_2Secondary() {
|
||||||
|
var typeSerial0: KSerializer<Any>? = null
|
||||||
|
var typeSerial1: KSerializer<Any>? = null
|
||||||
|
constructor(typeSerial0: KSerializer<Any>) : this() {
|
||||||
|
this.typeSerial0 = typeSerial0
|
||||||
|
}
|
||||||
|
constructor(typeSerial0: KSerializer<Any>, typeSerial1: KSerializer<Any>) : this() {
|
||||||
|
this.typeSerial0 = typeSerial0
|
||||||
|
this.typeSerial1 = typeSerial1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<!EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR!>@Serializer(forClass = Prop1Err::class)
|
||||||
|
class ExternalSerializer0_1Err<!>
|
||||||
|
|
||||||
|
// there is no error in the discrepancy between the number of constructor parameters in the serializer
|
||||||
|
// because the checks take place at the place where the serializer is declared, and if the serializer is declared correctly, there will be no error here
|
||||||
|
@Serializable(ExternalSerializer0_1Err::class)
|
||||||
|
class Prop1Err<T>(val t: T)
|
||||||
+11
-5
@@ -27,11 +27,17 @@ class WithSealed(val i: Int)
|
|||||||
@Serializable
|
@Serializable
|
||||||
class Holder (
|
class Holder (
|
||||||
<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(InterfaceSerializer::class)<!>
|
<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(InterfaceSerializer::class)<!>
|
||||||
val withInterface: WithInterfaceSerializer,
|
val withInterface: WithInterfaceSerializer,
|
||||||
|
|
||||||
<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(AbstractSerializer::class)<!>
|
<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(AbstractSerializer::class)<!>
|
||||||
val withAbstract: WithAbstract,
|
val withAbstract: WithAbstract,
|
||||||
|
|
||||||
<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(SealedSerializer::class)<!>
|
<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(SealedSerializer::class)<!>
|
||||||
val withSealed: WithSealed
|
val withSealed: WithSealed,
|
||||||
|
|
||||||
|
val ListWithInterface: List<<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(InterfaceSerializer::class)<!> WithInterfaceSerializer>,
|
||||||
|
|
||||||
|
val ListWithAbstract: List<<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(AbstractSerializer::class)<!> WithAbstract>,
|
||||||
|
|
||||||
|
val ListWithSealed: List<<!ABSTRACT_SERIALIZER_TYPE!>@Serializable(SealedSerializer::class)<!> WithSealed>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
// FIR_DISABLE_LAZY_RESOLVE_CHECKS
|
||||||
|
// FIR_IDENTICAL
|
||||||
|
// WITH_STDLIB
|
||||||
|
// FILE: test.kt
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.descriptors.*
|
||||||
|
import kotlinx.serialization.encoding.*
|
||||||
|
|
||||||
|
/* Tests on Serializable(with) on classes */
|
||||||
|
object Param0Object: ToDoSerializer<Param0WithObject>()
|
||||||
|
@Serializable(Param0Object::class)
|
||||||
|
class Param0WithObject(val i: Int)
|
||||||
|
|
||||||
|
object Param1Object: ToDoSerializer<Param1WithObject<*>>()
|
||||||
|
@Serializable(Param1Object::class)
|
||||||
|
class Param1WithObject<T>(val i: T)
|
||||||
|
|
||||||
|
|
||||||
|
class Param0Serializer0(): ToDoSerializer<Param0WithCustom0>()
|
||||||
|
@Serializable(Param0Serializer0::class)
|
||||||
|
class Param0WithCustom0(val i: Int)
|
||||||
|
|
||||||
|
class Param0Serializer1(val serializer: KSerializer<*>): ToDoSerializer<Param0WithCustom1>()
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Param0Serializer1::class)<!>
|
||||||
|
class Param0WithCustom1(val i: Int)
|
||||||
|
|
||||||
|
class Param0Serializer1Err(val serializer: Any): ToDoSerializer<Param0WithCustom1Err>()
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Param0Serializer1Err::class)<!>
|
||||||
|
class Param0WithCustom1Err(val i: Int)
|
||||||
|
|
||||||
|
class Param1Serializer0<T: Any>: ToDoSerializer<Param1WithCustom0<T>>()
|
||||||
|
@Serializable(Param1Serializer0::class)
|
||||||
|
class Param1WithCustom0<T>(val i: T)
|
||||||
|
|
||||||
|
class Param1Serializer1<T: Any>(val serializer: KSerializer<T>): ToDoSerializer<Param1WithCustom<T>>()
|
||||||
|
@Serializable(Param1Serializer1::class)
|
||||||
|
class Param1WithCustom<T>(val i: T)
|
||||||
|
|
||||||
|
class Param1Serializer1Err<T: Any>(val serializer: Any): ToDoSerializer<Param1WithCustom1Err<T>>()
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Param1Serializer1Err::class)<!>
|
||||||
|
class Param1WithCustom1Err<T>(val i: T)
|
||||||
|
|
||||||
|
class Param1Serializer2<T: Any>(val serializer: KSerializer<T>, val serializer2: KSerializer<Any>): ToDoSerializer<Param1WithCustom2<T>>()
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Param1Serializer2::class)<!>
|
||||||
|
class Param1WithCustom2<T>(val i: T)
|
||||||
|
|
||||||
|
class Param1Serializer2Err<T: Any>(val serializer1: Any, val serializer2: Any): ToDoSerializer<Param1WithCustom2Err<T>>()
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Param1Serializer2Err::class)<!>
|
||||||
|
class Param1WithCustom2Err<T>(val i: T)
|
||||||
|
|
||||||
|
class Param2Serializer1<T: Any>(val serializer: KSerializer<T>): ToDoSerializer<Param2WithCustom1<T, T>>()
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Param2Serializer1::class)<!>
|
||||||
|
class Param2WithCustom1<T, K>(val i: T, val k: K)
|
||||||
|
|
||||||
|
class Param2Serializer2Err<T: Any>(val serializer1: Any, val serializer2: Any): ToDoSerializer<Param2WithCustom2Err<T, T>>()
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Param2Serializer2Err::class)<!>
|
||||||
|
class Param2WithCustom2Err<T, K>(val i: T, val k: K)
|
||||||
|
|
||||||
|
/* Tests on Serializable(with) on properties */
|
||||||
|
|
||||||
|
class Prop0(val t: Int)
|
||||||
|
|
||||||
|
class Prop0Serializer0(): ToDoSerializer<Prop0>()
|
||||||
|
|
||||||
|
class Prop0Serializer1(val serializer: KSerializer<*>): ToDoSerializer<Prop0>()
|
||||||
|
|
||||||
|
class Prop0Serializer1Err(val serializer: Any): ToDoSerializer<Prop0>()
|
||||||
|
|
||||||
|
object Prop0SerializerObject: ToDoSerializer<Prop0>()
|
||||||
|
|
||||||
|
|
||||||
|
class Prop1<T>(val t: T)
|
||||||
|
|
||||||
|
class Prop1Serializer0<T: Any>(): ToDoSerializer<Prop1<T>>()
|
||||||
|
class Prop1Serializer1<T: Any>(val serializer: KSerializer<T>): ToDoSerializer<Prop1<T>>()
|
||||||
|
class Prop1Serializer2<T: Any>(val serializer1: KSerializer<T>, val serializer2: KSerializer<T>): ToDoSerializer<Prop1<T>>()
|
||||||
|
|
||||||
|
class Prop1Serializer1Err<T: Any>(val serializer: Any): ToDoSerializer<Prop1<T>>()
|
||||||
|
class Prop1Serializer2Err<T: Any>(val serializer1: Any, val serializer2: Any): ToDoSerializer<Prop1<T>>()
|
||||||
|
|
||||||
|
object Prop1SerializerObject: ToDoSerializer<Prop1<*>>()
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Holder<T>(
|
||||||
|
@Serializable(Prop0SerializerObject::class) val obj0: Prop0,
|
||||||
|
|
||||||
|
@Serializable(Prop0Serializer0::class) val p0_0: Prop0,
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Prop0Serializer1::class)<!> val p0_1: Prop0,
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop0Serializer1Err::class)<!> val p0_1Err: Prop0,
|
||||||
|
|
||||||
|
@Serializable(Prop1SerializerObject::class) val obj1T: Prop1<T>,
|
||||||
|
@Serializable(Prop1SerializerObject::class) val obj1: Prop1<Int>,
|
||||||
|
|
||||||
|
@Serializable(Prop1Serializer0::class) val p1_0T: Prop1<T>,
|
||||||
|
@Serializable(Prop1Serializer0::class) val p1_0: Prop1<Int>,
|
||||||
|
@Serializable(Prop1Serializer1::class) val p1_1T: Prop1<T>,
|
||||||
|
@Serializable(Prop1Serializer1::class) val p1_1: Prop1<Int>,
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Prop1Serializer2::class)<!> val p1_2T: Prop1<T>,
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Prop1Serializer2::class)<!> val p1_2: Prop1<Int>,
|
||||||
|
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer1Err::class)<!> val p1_1TErr: Prop1<T>,
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer1Err::class)<!> val p1_1Err: Prop1<Int>,
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer2Err::class)<!> val p1_2TErr: Prop1<T>,
|
||||||
|
<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer2Err::class)<!> val p1_2Err: Prop1<Int>,
|
||||||
|
|
||||||
|
// generic params
|
||||||
|
val list_obj0: List<@Serializable(Prop0SerializerObject::class) Prop0>,
|
||||||
|
|
||||||
|
val list_p0_0: List<@Serializable(Prop0Serializer0::class) Prop0>,
|
||||||
|
val list_p0_1: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Prop0Serializer1::class)<!> Prop0>,
|
||||||
|
val list_p0_1Err: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop0Serializer1Err::class)<!> Prop0>,
|
||||||
|
|
||||||
|
val list_obj1T: List<@Serializable(Prop1SerializerObject::class) Prop1<T>>,
|
||||||
|
val list_obj1: List<@Serializable(Prop1SerializerObject::class) Prop1<Int>>,
|
||||||
|
|
||||||
|
val list_p1_0T: List<@Serializable(Prop1Serializer0::class) Prop1<T>>,
|
||||||
|
val list_p1_0: List<@Serializable(Prop1Serializer0::class) Prop1<Int>>,
|
||||||
|
val list_p1_1T: List<@Serializable(Prop1Serializer1::class) Prop1<T>>,
|
||||||
|
val list_p1_1: List<@Serializable(Prop1Serializer1::class) Prop1<Int>>,
|
||||||
|
val list_p1_2T: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Prop1Serializer2::class)<!> Prop1<T>>,
|
||||||
|
val list_p1_2: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT!>@Serializable(Prop1Serializer2::class)<!> Prop1<Int>>,
|
||||||
|
|
||||||
|
val list_p1_1TErr: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer1Err::class)<!> Prop1<T>>,
|
||||||
|
val list_p1_1Err: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer1Err::class)<!> Prop1<Int>>,
|
||||||
|
val list_p1_2TErr: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer2Err::class)<!> Prop1<T>>,
|
||||||
|
val list_p1_2Err: List<<!CUSTOM_SERIALIZER_PARAM_ILLEGAL_COUNT, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE, CUSTOM_SERIALIZER_PARAM_ILLEGAL_TYPE!>@Serializable(Prop1Serializer2Err::class)<!> Prop1<Int>>,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
abstract class ToDoSerializer<T: Any>: KSerializer<T> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("TODO SERIALIZER", PrimitiveKind.STRING)
|
||||||
|
override fun deserialize(decoder: Decoder): T = TODO()
|
||||||
|
override fun serialize(encoder: Encoder, value: T) = TODO()
|
||||||
|
}
|
||||||
+6
@@ -225,6 +225,12 @@ public class SerializationFirLightTreeBlackBoxTestGenerated extends AbstractSeri
|
|||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/namedCompanions.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/namedCompanions.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("polymorphic.kt")
|
||||||
|
public void testPolymorphic() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/polymorphic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("privateCustomSerializer.kt")
|
@TestMetadata("privateCustomSerializer.kt")
|
||||||
public void testPrivateCustomSerializer() throws Exception {
|
public void testPrivateCustomSerializer() throws Exception {
|
||||||
|
|||||||
+12
@@ -38,6 +38,12 @@ public class SerializationFirPsiDiagnosticTestGenerated extends AbstractSerializ
|
|||||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/companionObjectSerializers.kt");
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/companionObjectSerializers.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("customSerializers.kt")
|
||||||
|
public void testCustomSerializers() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/customSerializers.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("DuplicateSerialName.kt")
|
@TestMetadata("DuplicateSerialName.kt")
|
||||||
public void testDuplicateSerialName() throws Exception {
|
public void testDuplicateSerialName() throws Exception {
|
||||||
@@ -128,6 +134,12 @@ public class SerializationFirPsiDiagnosticTestGenerated extends AbstractSerializ
|
|||||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt");
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("ParametrizedExternalSerializers.kt")
|
||||||
|
public void testParametrizedExternalSerializers() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/ParametrizedExternalSerializers.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("repeatableSerialInfo.kt")
|
@TestMetadata("repeatableSerialInfo.kt")
|
||||||
public void testRepeatableSerialInfo() throws Exception {
|
public void testRepeatableSerialInfo() throws Exception {
|
||||||
|
|||||||
+6
@@ -223,6 +223,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
|
|||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/namedCompanions.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/namedCompanions.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("polymorphic.kt")
|
||||||
|
public void testPolymorphic() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/polymorphic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("privateCustomSerializer.kt")
|
@TestMetadata("privateCustomSerializer.kt")
|
||||||
public void testPrivateCustomSerializer() throws Exception {
|
public void testPrivateCustomSerializer() throws Exception {
|
||||||
|
|||||||
+12
@@ -36,6 +36,12 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
|
|||||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/companionObjectSerializers.kt");
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/companionObjectSerializers.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("customSerializers.kt")
|
||||||
|
public void testCustomSerializers() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/customSerializers.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("DuplicateSerialName.kt")
|
@TestMetadata("DuplicateSerialName.kt")
|
||||||
public void testDuplicateSerialName() throws Exception {
|
public void testDuplicateSerialName() throws Exception {
|
||||||
@@ -126,6 +132,12 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
|
|||||||
runTest("plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt");
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("ParametrizedExternalSerializers.kt")
|
||||||
|
public void testParametrizedExternalSerializers() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/diagnostics/ParametrizedExternalSerializers.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("repeatableSerialInfo.kt")
|
@TestMetadata("repeatableSerialInfo.kt")
|
||||||
public void testRepeatableSerialInfo() throws Exception {
|
public void testRepeatableSerialInfo() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user