Add checker for companion objects inside serializable class:
Warning for old FE, error in FIR #KT-54441 Fixed Merged-by: Leonid Startsev <leonid.startsev@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
3832c6a520
commit
4cf50d7d23
+8
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlinx.serialization.compiler.diagnostic;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.*;
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
@@ -19,6 +20,13 @@ public interface SerializationErrors {
|
||||
DiagnosticFactory0<PsiElement> ANONYMOUS_OBJECTS_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INNER_CLASSES_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
|
||||
DiagnosticFactory1<PsiElement, ClassDescriptor> COMPANION_OBJECT_AS_CUSTOM_SERIALIZER_DEPRECATED = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> COMPANION_OBJECT_SERIALIZER_INSIDE_OTHER_SERIALIZABLE_CLASS = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> COMPANION_OBJECT_SERIALIZER_INSIDE_NON_SERIALIZABLE_CLASS = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<PsiElement> EXPLICIT_SERIALIZABLE_IS_REQUIRED = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<KtAnnotationEntry> SERIALIZABLE_ANNOTATION_IGNORED = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
+50
@@ -171,6 +171,8 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
return false
|
||||
}
|
||||
|
||||
checkCompanionSerializerDependency(descriptor, declaration, trace)
|
||||
|
||||
if (!descriptor.hasSerializableOrMetaAnnotation) return false
|
||||
|
||||
if (!serializationPluginEnabledOn(descriptor)) {
|
||||
@@ -200,6 +202,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (!descriptor.hasSerializableOrMetaAnnotationWithoutArgs) {
|
||||
// defined custom serializer
|
||||
checkClassWithCustomSerializer(descriptor, declaration, trace)
|
||||
@@ -222,6 +225,53 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun checkCompanionSerializerDependency(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
|
||||
val companionObjectDescriptor = descriptor.companionObjectDescriptor ?: return
|
||||
val serializerForInCompanion = companionObjectDescriptor.serializerForClass ?: return
|
||||
val serializerAnnotationSource =
|
||||
companionObjectDescriptor.findAnnotationDeclaration(SerializationAnnotations.serializerAnnotationFqName)
|
||||
val serializableWith = descriptor.serializableWith
|
||||
if (descriptor.hasSerializableOrMetaAnnotationWithoutArgs) {
|
||||
if (serializerForInCompanion == descriptor.defaultType) {
|
||||
// @Serializable class Foo / @Serializer(Foo::class) companion object — prohibited due to problems with recursive resolve
|
||||
descriptor.onSerializableOrMetaAnnotation {
|
||||
trace.report(SerializationErrors.COMPANION_OBJECT_AS_CUSTOM_SERIALIZER_DEPRECATED.on(it, descriptor))
|
||||
}
|
||||
} else {
|
||||
// @Serializable class Foo / @Serializer(Bar::class) companion object — prohibited as vague and confusing
|
||||
trace.report(
|
||||
SerializationErrors.COMPANION_OBJECT_SERIALIZER_INSIDE_OTHER_SERIALIZABLE_CLASS.on(
|
||||
serializerAnnotationSource ?: declaration,
|
||||
descriptor.defaultType,
|
||||
serializerForInCompanion
|
||||
)
|
||||
)
|
||||
}
|
||||
} else if (serializableWith != null) {
|
||||
if (serializableWith == companionObjectDescriptor.defaultType && serializerForInCompanion == descriptor.defaultType) {
|
||||
// @Serializable(Foo.Companion) class Foo / @Serializer(Foo::class) companion object — the only case that is allowed
|
||||
} else {
|
||||
// @Serializable(anySer) class Foo / @Serializer(anyOtherClass) companion object — prohibited as vague and confusing
|
||||
trace.report(
|
||||
SerializationErrors.COMPANION_OBJECT_SERIALIZER_INSIDE_OTHER_SERIALIZABLE_CLASS.on(
|
||||
serializerAnnotationSource ?: declaration,
|
||||
descriptor.defaultType,
|
||||
serializerForInCompanion
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// (regular) class Foo / @Serializer(something) companion object - not recommended
|
||||
trace.report(
|
||||
SerializationErrors.COMPANION_OBJECT_SERIALIZER_INSIDE_NON_SERIALIZABLE_CLASS.on(
|
||||
serializerAnnotationSource ?: declaration,
|
||||
descriptor.defaultType,
|
||||
serializerForInCompanion
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkClassWithCustomSerializer(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
|
||||
val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration()
|
||||
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
|
||||
|
||||
+25
@@ -34,6 +34,31 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
||||
SerializationErrors.INNER_CLASSES_NOT_SUPPORTED,
|
||||
"Inner (with reference to outer this) serializable classes are not supported. Remove @Serializable annotation or 'inner' keyword."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.COMPANION_OBJECT_AS_CUSTOM_SERIALIZER_DEPRECATED,
|
||||
"Class ''{0}'' has implicit custom serializer as its companion object. This behaviour is not properly reflected by @Serializable annotation without arguments and therefore is deprecated. " +
|
||||
"To be able to use companion object as the ''{0}'' default serializer, please explicitly mention it in the annotation on ''{0}'': @Serializable(''{0}''.Companion::class). " +
|
||||
"For more details, refer to this YouTrack ticket: https://youtrack.jetbrains.com/issue/KT-54441",
|
||||
Renderers.NAME
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.COMPANION_OBJECT_SERIALIZER_INSIDE_OTHER_SERIALIZABLE_CLASS,
|
||||
"This class is a Companion object for @Serializable class ''{0}'', but itself is an external serializer for another class ''{1}''. " +
|
||||
"Such declarations are potentially problematic and user-confusing and therefore are deprecated. " +
|
||||
"Please define external serializers as non-companion, preferably top-level objects. " +
|
||||
"For more details, refer to this YouTrack ticket: https://youtrack.jetbrains.com/issue/KT-54441",
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.COMPANION_OBJECT_SERIALIZER_INSIDE_NON_SERIALIZABLE_CLASS,
|
||||
"This class is a Companion object for non-serializable class ''{0}'', but itself is an external serializer for another class ''{1}''. " +
|
||||
"Such declarations are potentially problematic and user-confusing and therefore are deprecated. " +
|
||||
"Please define external serializers as non-companion, preferably top-level objects. " +
|
||||
"For more details, refer to this YouTrack ticket: https://youtrack.jetbrains.com/issue/KT-54441",
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED,
|
||||
"Explicit @Serializable annotation on enum class is required when @SerialName or @SerialInfo annotations are used on its members."
|
||||
|
||||
Reference in New Issue
Block a user