[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);
|
||||
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, KotlinType> SERIALIZER_TYPE_INCOMPATIBLE = DiagnosticFactory3.create(WARNING);
|
||||
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> 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_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, String> EXTERNAL_SERIALIZER_NO_SUITABLE_CONSTRUCTOR = DiagnosticFactory3.create(ERROR);
|
||||
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
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.types.KotlinType
|
||||
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.util.slicedMap.Slices
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||
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.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.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.LOAD_NAME
|
||||
@@ -89,6 +91,25 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
val serializableDescriptor = serializableKType.toClassDescriptor ?: return
|
||||
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
|
||||
.getContributedVariables(SERIAL_DESC_FIELD_NAME, NoLookupLocation.FROM_BACKEND).singleOrNull {
|
||||
it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||
@@ -325,6 +346,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration()
|
||||
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, 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)
|
||||
}
|
||||
|
||||
@@ -441,14 +463,9 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
if (serializer != null) {
|
||||
val element = ktType?.typeElement
|
||||
checkCustomSerializerMatch(it.module, it.type, it.descriptor, element, trace, propertyPsi)
|
||||
checkCustomSerializerNotAbstract(
|
||||
it.module,
|
||||
it.type,
|
||||
it.descriptor,
|
||||
it.descriptor.findSerializableOrMetaAnnotationDeclaration(),
|
||||
trace,
|
||||
propertyPsi
|
||||
)
|
||||
val annotationPsi = it.descriptor.findSerializableOrMetaAnnotationDeclaration()
|
||||
checkCustomSerializerNotAbstract(it.module, it.type, it.descriptor, annotationPsi, trace, propertyPsi)
|
||||
checkCustomSerializerParameters(it.module, it.descriptor, it.type, annotationPsi, propertyPsi, trace)
|
||||
checkCustomSerializerIsNotLocal(it.module, it.descriptor, trace, propertyPsi)
|
||||
checkSerializerNullability(it.type, serializer.defaultType, 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)
|
||||
if (serializer != null) {
|
||||
checkCustomSerializerMatch(module, type, type, element, trace, fallbackElement)
|
||||
checkCustomSerializerIsNotLocal(module, type, trace, fallbackElement)
|
||||
type.annotations.serializableWith(module)?.let {
|
||||
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)
|
||||
checkTypeArguments(module, type, element, trace, fallbackElement)
|
||||
} 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(
|
||||
classType: 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",
|
||||
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(
|
||||
SerializationErrors.TRANSIENT_MISSING_INITIALIZER,
|
||||
"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
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
private fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
|
||||
internal fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
|
||||
val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName) as? LazyAnnotationDescriptor
|
||||
return lazyDesc?.annotationEntry
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user