From c2bdd516584b2c446ee2283953af0243784030b0 Mon Sep 17 00:00:00 2001 From: "Sergey.Shanshin" Date: Fri, 5 Aug 2022 09:31:58 +0000 Subject: [PATCH] Added diagnostic for external serializers Relates Kotlin/kotlinx.serialization#532 --- .../diagnostic/SerializationErrors.java | 3 +++ .../SerializationPluginDeclarationChecker.kt | 17 +++++++++++++++++ .../SerializationPluginErrorsRendering.kt | 14 ++++++++++++++ .../compiler/resolve/KSerializationUtil.kt | 5 +++++ ...ializationPluginDiagnosticTestGenerated.java | 5 +++++ .../testData/diagnostics/ExternalSerializers.kt | 10 ++++++++++ 6 files changed, 54 insertions(+) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/ExternalSerializers.kt diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java index d113a0d1fbb..f6c61fe6904 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java @@ -42,6 +42,9 @@ public interface SerializationErrors { DiagnosticFactory2 INCONSISTENT_INHERITABLE_SERIALINFO = DiagnosticFactory2.create(ERROR); + DiagnosticFactory2 EXTERNAL_CLASS_NOT_SERIALIZABLE = DiagnosticFactory2.create(ERROR); + DiagnosticFactory2 EXTERNAL_CLASS_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR); + @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt index 817bdf80ff7..0af26c289ad 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt @@ -41,6 +41,8 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { if (descriptor !is ClassDescriptor) return + checkExternalSerializer(descriptor, declaration, context.trace) + if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return if (declaration !is KtPureClassOrObject) return if (!isIde) { @@ -58,6 +60,21 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { checkInheritedAnnotations(descriptor, declaration, context.trace) } + private fun checkExternalSerializer(classDescriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) { + val serializableKType = classDescriptor.serializerForClass ?: return + val serializableDescriptor = serializableKType.toClassDescriptor ?: return + val props = SerializableProperties(serializableDescriptor, trace.bindingContext) + + if (!props.isExternallySerializable) { + val entry = classDescriptor.findAnnotationDeclaration(SerializationAnnotations.serializerAnnotationFqName) + val inSameModule = + trace.bindingContext[BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, serializableDescriptor.fqNameUnsafe] != null + val diagnostic = if (inSameModule) SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE else SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE + + trace.report(diagnostic.on(entry ?: declaration, classDescriptor.defaultType, serializableKType)) + } + } + private fun checkInheritedAnnotations(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) { val annotationsFilter: (Annotations) -> List> = { an -> an.map { it.annotationClass!!.fqNameSafe to it } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt index 1230d0f7ce4..8f41c4ff74e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt @@ -124,5 +124,19 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension { Renderers.RENDER_TYPE, Renderers.RENDER_TYPE ) + + MAP.put( + SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE, + "Cannot generate external serializer ''{0}'': class ''{1}'' have constructor parameters which are not properties and therefore it is not serializable automatically", + Renderers.RENDER_TYPE, + Renderers.RENDER_TYPE + ) + + MAP.put( + SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE, + "Cannot generate external serializer ''{0}'': class ''{1}'' is defined in another module", + Renderers.RENDER_TYPE, + Renderers.RENDER_TYPE + ) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index 5081a254c8d..56aa214060a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -198,6 +198,11 @@ internal fun Annotated.findSerializableOrMetaAnnotationDeclaration(): KtAnnotati return lazyDesc?.annotationEntry } +internal fun Annotated.findAnnotationDeclaration(fqName: FqName): KtAnnotationEntry? { + val lazyDesc = annotations.findAnnotation(fqName) as? LazyAnnotationDescriptor + return lazyDesc?.annotationEntry +} + // For abstract classes marked with @Serializable, // methods are generated anyway, although they shouldn't have // generated $serializer and use Polymorphic one. diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java index 7d63ac83601..fbbf97894d4 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/SerializationPluginDiagnosticTestGenerated.java @@ -34,6 +34,11 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/DuplicateSerialName.kt"); } + @TestMetadata("ExternalSerializers.kt") + public void testExternalSerializers() throws Exception { + runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/ExternalSerializers.kt"); + } + @TestMetadata("IncorrectTransient.kt") public void testIncorrectTransient() throws Exception { runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/IncorrectTransient.kt"); diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/ExternalSerializers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/ExternalSerializers.kt new file mode 100644 index 00000000000..d3b4f709b41 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/ExternalSerializers.kt @@ -0,0 +1,10 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE, -OPT_IN_USAGE +// SKIP_TXT + +// FILE: test.kt +import kotlinx.serialization.* + +class Foo(i: Int, val j: Int) + +@Serializer(forClass = Foo::class) +object ExternalSerializer