diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java index 7a2028c7457..1954bd228cc 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java @@ -52,6 +52,7 @@ public interface SerializationErrors { DiagnosticFactory3 PROVIDED_RUNTIME_TOO_LOW = DiagnosticFactory3.create(ERROR); DiagnosticFactory2 INCONSISTENT_INHERITABLE_SERIALINFO = DiagnosticFactory2.create(ERROR); + DiagnosticFactory0 META_SERIALIZABLE_NOT_APPLICABLE = DiagnosticFactory0.create(WARNING); DiagnosticFactory2 EXTERNAL_CLASS_NOT_SERIALIZABLE = DiagnosticFactory2.create(ERROR); DiagnosticFactory2 EXTERNAL_CLASS_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR); diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt index 695fc71b8e9..c38b4186194 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt @@ -40,6 +40,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { if (descriptor !is ClassDescriptor) return + checkMetaSerializableApplicable(descriptor, context.trace) checkEnum(descriptor, declaration, context.trace) checkExternalSerializer(descriptor, declaration, context.trace) @@ -60,6 +61,13 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { checkInheritedAnnotations(descriptor, declaration, context.trace) } + private fun checkMetaSerializableApplicable(descriptor: ClassDescriptor, trace: BindingTrace) { + if (descriptor.kind != ClassKind.ANNOTATION_CLASS) return + if (descriptor.classId?.isNestedClass != true) return + val entry = descriptor.findAnnotationDeclaration(SerializationAnnotations.metaSerializableAnnotationFqName) ?: return + trace.report(SerializationErrors.META_SERIALIZABLE_NOT_APPLICABLE.on(entry)) + } + private fun checkExternalSerializer(classDescriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) { val serializableKType = classDescriptor.serializerForClass ?: return val serializableDescriptor = serializableKType.toClassDescriptor ?: return diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt index 4f41f428bff..9119b1ea94d 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt @@ -160,6 +160,10 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension { Renderers.RENDER_TYPE, Renderers.RENDER_TYPE ) + MAP.put( + SerializationErrors.META_SERIALIZABLE_NOT_APPLICABLE, + "@MetaSerializable annotation should be used only on top-level annotation classes. Usage on nested annotation classes is deprecated and will yield errors in the future." + ) MAP.put( SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE, diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationErrors.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationErrors.kt index b8238a9c31b..8342313cb4b 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationErrors.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationErrors.kt @@ -45,6 +45,7 @@ object FirSerializationErrors { val PROVIDED_RUNTIME_TOO_LOW by error3() val INCONSISTENT_INHERITABLE_SERIALINFO by error2() + val META_SERIALIZABLE_NOT_APPLICABLE by error0() val EXTERNAL_CLASS_NOT_SERIALIZABLE by error2, ConeKotlinType>() val EXTERNAL_CLASS_IN_ANOTHER_MODULE by error2, ConeKotlinType>() diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt index 0e7283bba90..0145c48a012 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt @@ -7,11 +7,13 @@ package org.jetbrains.kotlinx.serialization.compiler.fir.checkers import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.KotlinCompilerVersion +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.* import org.jetbrains.kotlin.fir.FirSession 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.isInlineClass +import org.jetbrains.kotlin.fir.analysis.checkers.outerClassSymbol import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.* @@ -43,6 +45,7 @@ object FirSerializationPluginClassChecker : FirClassChecker() { override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { with(context) { val classSymbol = declaration.symbol + checkMetaSerializableApplicable(classSymbol, reporter) checkEnum(classSymbol, reporter) checkExternalSerializer(classSymbol, reporter) if (!canBeSerializedInternally(classSymbol, reporter)) return @@ -58,6 +61,15 @@ object FirSerializationPluginClassChecker : FirClassChecker() { } } + context(CheckerContext) + private fun checkMetaSerializableApplicable(classSymbol: FirClassSymbol, reporter: DiagnosticReporter) { + if (classSymbol.classKind != ClassKind.ANNOTATION_CLASS) return + if (!classSymbol.classId.isNestedClass) return + val anno = classSymbol.resolvedAnnotationsWithClassIds.find { it.classId == SerializationAnnotations.metaSerializableAnnotationClassId } + if (anno == null) return + reporter.reportOn(anno.source, FirSerializationErrors.META_SERIALIZABLE_NOT_APPLICABLE) + } + context(CheckerContext) @Suppress("IncorrectFormatting") // KTIJ-22227 private fun checkExternalSerializer(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) { diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/KtDefaultErrorMessagesSerialization.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/KtDefaultErrorMessagesSerialization.kt index 09b62197242..c5cc124a256 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/KtDefaultErrorMessagesSerialization.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/KtDefaultErrorMessagesSerialization.kt @@ -151,6 +151,10 @@ object KtDefaultErrorMessagesSerialization : BaseDiagnosticRendererFactory() { FirDiagnosticRenderers.RENDER_TYPE, FirDiagnosticRenderers.RENDER_TYPE ) + put( + FirSerializationErrors.META_SERIALIZABLE_NOT_APPLICABLE, + "@MetaSerializable annotation can be used only on top-level annotation classes." + ) put( FirSerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE, diff --git a/plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.fir.kt new file mode 100644 index 00000000000..f8b2959bbf7 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.fir.kt @@ -0,0 +1,21 @@ +// WITH_STDLIB +// SKIP_TXT + +import kotlinx.serialization.* + +@MetaSerializable +annotation class TopLevel + +class MetaSerializableNestedTest { + @MetaSerializable + @Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) + annotation class JsonComment(val comment: String) + + object Nested2 { + @MetaSerializable + annotation class Nested3 + } + + @JsonComment("class_comment") + data class IntDataCommented(val i: Int) +} diff --git a/plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.kt b/plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.kt new file mode 100644 index 00000000000..0595798824f --- /dev/null +++ b/plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.kt @@ -0,0 +1,21 @@ +// WITH_STDLIB +// SKIP_TXT + +import kotlinx.serialization.* + +@MetaSerializable +annotation class TopLevel + +class MetaSerializableNestedTest { + @MetaSerializable + @Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS) + annotation class JsonComment(val comment: String) + + object Nested2 { + @MetaSerializable + annotation class Nested3 + } + + @JsonComment("class_comment") + data class IntDataCommented(val i: Int) +} diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java index 78f145b5023..5d60a2de820 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirDiagnosticTestGenerated.java @@ -92,6 +92,12 @@ public class SerializationFirDiagnosticTestGenerated extends AbstractSerializati runTest("plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.kt"); } + @Test + @TestMetadata("metaSerializableNested.kt") + public void testMetaSerializableNested() throws Exception { + runTest("plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.kt"); + } + @Test @TestMetadata("NoSuitableCtorInParent.kt") public void testNoSuitableCtorInParent() throws Exception { diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationPluginDiagnosticTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationPluginDiagnosticTestGenerated.java index 414107b749a..1890bda9274 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationPluginDiagnosticTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationPluginDiagnosticTestGenerated.java @@ -90,6 +90,12 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ runTest("plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.kt"); } + @Test + @TestMetadata("metaSerializableNested.kt") + public void testMetaSerializableNested() throws Exception { + runTest("plugins/kotlinx-serialization/testData/diagnostics/metaSerializableNested.kt"); + } + @Test @TestMetadata("NoSuitableCtorInParent.kt") public void testNoSuitableCtorInParent() throws Exception {