Added diagnostic for external serializers

Relates Kotlin/kotlinx.serialization#532
This commit is contained in:
Sergey.Shanshin
2022-08-05 09:31:58 +00:00
committed by Space
parent 17bf6be807
commit c2bdd51658
6 changed files with 54 additions and 0 deletions
@@ -42,6 +42,9 @@ public interface SerializationErrors {
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> INCONSISTENT_INHERITABLE_SERIALINFO = DiagnosticFactory2.create(ERROR); DiagnosticFactory2<PsiElement, KotlinType, KotlinType> INCONSISTENT_INHERITABLE_SERIALINFO = 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);
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() { Object _initializer = new Object() {
{ {
@@ -41,6 +41,8 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor) return if (descriptor !is ClassDescriptor) return
checkExternalSerializer(descriptor, declaration, context.trace)
if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return
if (declaration !is KtPureClassOrObject) return if (declaration !is KtPureClassOrObject) return
if (!isIde) { if (!isIde) {
@@ -58,6 +60,21 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
checkInheritedAnnotations(descriptor, declaration, context.trace) 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) { private fun checkInheritedAnnotations(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
val annotationsFilter: (Annotations) -> List<Pair<FqName, AnnotationDescriptor>> = { an -> val annotationsFilter: (Annotations) -> List<Pair<FqName, AnnotationDescriptor>> = { an ->
an.map { it.annotationClass!!.fqNameSafe to it } an.map { it.annotationClass!!.fqNameSafe to it }
@@ -124,5 +124,19 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
Renderers.RENDER_TYPE, Renderers.RENDER_TYPE,
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
)
} }
} }
@@ -198,6 +198,11 @@ internal fun Annotated.findSerializableOrMetaAnnotationDeclaration(): KtAnnotati
return lazyDesc?.annotationEntry 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, // For abstract classes marked with @Serializable,
// methods are generated anyway, although they shouldn't have // methods are generated anyway, although they shouldn't have
// generated $serializer and use Polymorphic one. // generated $serializer and use Polymorphic one.
@@ -34,6 +34,11 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/DuplicateSerialName.kt"); 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") @TestMetadata("IncorrectTransient.kt")
public void testIncorrectTransient() throws Exception { public void testIncorrectTransient() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/IncorrectTransient.kt"); runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/IncorrectTransient.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)
<!EXTERNAL_CLASS_NOT_SERIALIZABLE!>@Serializer(forClass = Foo::class)<!>
object ExternalSerializer