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> EXTERNAL_CLASS_NOT_SERIALIZABLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{
@@ -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<Pair<FqName, AnnotationDescriptor>> = { an ->
an.map { it.annotationClass!!.fqNameSafe to it }
@@ -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
)
}
}
@@ -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.
@@ -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");
@@ -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