diff --git a/idea/resources/META-INF/kotlinx-serialization.xml b/idea/resources/META-INF/kotlinx-serialization.xml
index cda0cb08d86..4ad16af7a6a 100644
--- a/idea/resources/META-INF/kotlinx-serialization.xml
+++ b/idea/resources/META-INF/kotlinx-serialization.xml
@@ -4,5 +4,6 @@
+
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt
index 13839ad2086..cfde3f05396 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt
@@ -21,13 +21,15 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors
+import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SERIALIZABLE_PROPERTIES
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
abstract class SerializableCodegen(
protected val serializableDescriptor: ClassDescriptor,
bindingContext: BindingContext
) : AbstractSerialGenerator(bindingContext, serializableDescriptor) {
- protected val properties = SerializableProperties(serializableDescriptor, bindingContext)
+ protected val properties = bindingContext.get(SERIALIZABLE_PROPERTIES, serializableDescriptor)
+ ?: SerializableProperties(serializableDescriptor, bindingContext)
fun generate() {
generateSyntheticInternalConstructor()
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt
index 15fe72bc74e..e2f76693487 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt
@@ -116,12 +116,12 @@ fun AbstractSerialGenerator.findTypeSerializerOrContext(
annotations.serializableWith(module)?.let { return it.toClassDescriptor }
additionalSerializersInScopeOfCurrentFile[kType]?.let { return it }
if (kType in contextualKClassListInCurrentFile) return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
- return analyzeSpecialSerializers(module, annotations) ?: findTypeSerializer(module, kType) ?: throw CompilationException(
+ return analyzeSpecialSerializers(module, annotations) ?: findTypeSerializer(module, kType) /*?: throw CompilationException(
"Serializer for element of type $kType has not been found.\n" +
"To use context serializer as fallback, explicitly annotate element with @ContextualSerialization",
null,
sourceElement
- )
+ )*/
}
fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/DeclarationCheckers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/DeclarationCheckers.kt
new file mode 100644
index 00000000000..58a1de1a264
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/DeclarationCheckers.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.diagnostic
+
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.descriptors.PropertyDescriptor
+import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
+import org.jetbrains.kotlin.diagnostics.reportFromPlugin
+import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.resolve.BindingTrace
+import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
+import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.util.slicedMap.Slices
+import org.jetbrains.kotlin.util.slicedMap.WritableSlice
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator
+import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext
+import org.jetbrains.kotlinx.serialization.compiler.resolve.*
+
+internal val SERIALIZABLE_PROPERTIES: WritableSlice = Slices.createSimpleSlice()
+internal val SERIALIZER_FOR_PROPERTY: WritableSlice = Slices.createSimpleSlice()
+
+class SerializationPluginDeclarationChecker : DeclarationChecker {
+ override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
+ if (descriptor !is ClassDescriptor) return
+
+ checkCanBeSerializedInternally(descriptor, context.trace)
+ val props = buildSerializableProperties(descriptor, context.trace) ?: return
+ analyzePropertiesSerializers(context.trace, descriptor, props.serializableProperties)
+ }
+
+ private fun checkCanBeSerializedInternally(descriptor: ClassDescriptor, trace: BindingTrace) {
+ if (!descriptor.hasSerializableAnnotationWithoutArgs) return
+
+ if (!descriptor.isInternalSerializable && !descriptor.hasCompanionObjectAsSerializer) {
+ trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
+ }
+ }
+
+ // todo: also check on a side of external serializer
+ private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? {
+ if (!descriptor.annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return null
+ if (!descriptor.isInternalSerializable) return null
+ if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user
+
+ val props = SerializableProperties(descriptor, trace.bindingContext)
+ if (!props.isExternallySerializable) trace.reportOnSerializableAnnotation(
+ descriptor,
+ SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY
+ )
+
+ // check that all names are unique
+ val namesSet = mutableSetOf()
+ props.serializableProperties.forEach {
+ if (!namesSet.add(it.name)) {
+ descriptor.safeReport { a ->
+ trace.reportFromPlugin(
+ SerializationErrors.DUPLICATE_SERIAL_NAME.on(a, it.name),
+ SerializationPluginErrorsRendering
+ )
+ }
+ }
+ }
+
+ trace.record(SERIALIZABLE_PROPERTIES, descriptor, props)
+ return props
+ }
+
+ private fun analyzePropertiesSerializers(trace: BindingTrace, serializableClass: ClassDescriptor, props: List) {
+ val generatorContextForAnalysis = object : AbstractSerialGenerator(trace.bindingContext, serializableClass) {}
+ props.forEach {
+ val serializer = it.serializableWith?.toClassDescriptor
+ val ktType = (it.descriptor.findPsi() as? KtCallableDeclaration)?.typeReference ?: return@forEach
+ if (serializer != null) {
+ val element = ktType.typeElement ?: return
+ generatorContextForAnalysis.checkTypeArguments(it.module, it.type, element, trace)
+ trace.record(SERIALIZER_FOR_PROPERTY, it.descriptor, serializer)
+ } else {
+ generatorContextForAnalysis.checkType(it.module, it.type, ktType, trace)
+ }
+ }
+ }
+
+ private fun AbstractSerialGenerator.checkTypeArguments(
+ module: ModuleDescriptor,
+ type: KotlinType,
+ element: KtTypeElement,
+ trace: BindingTrace
+ ) {
+ type.arguments.forEachIndexed { i, it -> checkType(module, it.type, element.typeArgumentsAsTypes[i], trace) }
+ }
+
+ private fun AbstractSerialGenerator.checkType(
+ module: ModuleDescriptor,
+ type: KotlinType,
+ ktType: KtTypeReference,
+ trace: BindingTrace
+ ) {
+ if (type.genericIndex != null) return
+ val element = ktType.typeElement ?: return
+ val serializerForType = findTypeSerializerOrContext(module, type)
+ if (serializerForType != null) {
+ checkTypeArguments(module, type, element, trace)
+ } else {
+ trace.reportFromPlugin(
+ SerializationErrors.SERIALIZER_NOT_FOUND.on(element),
+ SerializationPluginErrorsRendering
+ )
+ }
+ }
+
+ private inline fun ClassDescriptor.safeReport(report: (KtAnnotationEntry) -> Unit) {
+ findSerializableAnnotationDeclaration()?.let(report)
+ }
+
+ private fun BindingTrace.reportOnSerializableAnnotation(descriptor: ClassDescriptor, error: DiagnosticFactory0) {
+ descriptor.safeReport { e ->
+ reportFromPlugin(
+ error.on(e),
+ SerializationPluginErrorsRendering
+ )
+ }
+ }
+
+}
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
new file mode 100644
index 00000000000..64d616ed8c9
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationErrors.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.diagnostic;
+
+import com.intellij.psi.PsiElement;
+import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
+import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
+import org.jetbrains.kotlin.diagnostics.Errors;
+import org.jetbrains.kotlin.psi.KtAnnotationEntry;
+
+import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
+
+public interface SerializationErrors {
+ DiagnosticFactory0 SERIALIZABLE_ANNOTATION_IGNORED = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory1 DUPLICATE_SERIAL_NAME = DiagnosticFactory1.create(ERROR);
+ DiagnosticFactory0 SERIALIZER_NOT_FOUND = DiagnosticFactory0.create(ERROR);
+
+ @SuppressWarnings("UnusedDeclaration")
+ Object _initializer = new Object() {
+ {
+ Errors.Initializer.initializeFactoryNames(SerializationErrors.class);
+ }
+ };
+
+}
\ No newline at end of file
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
new file mode 100644
index 00000000000..08ef1bfc58b
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginErrorsRendering.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.diagnostic
+
+import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
+import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
+import org.jetbrains.kotlin.diagnostics.rendering.Renderers
+
+object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
+ private val MAP = DiagnosticFactoryToRendererMap("SerializationPlugin")
+ override fun getMap() = MAP
+
+ init {
+ MAP.put(
+ SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED,
+ "@Serializable annotation would be ignored because it is impossible to serialize automatically interfaces or enums. " +
+ "Provide serializer manually via e.g. companion object"
+ )
+ MAP.put(
+ SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY,
+ "This class is not serializable automatically because it has primary constructor parameters which are not properties."
+ )
+ MAP.put(
+ SerializationErrors.DUPLICATE_SERIAL_NAME,
+ "Serializable class has duplicate serial name of property ''{0}'', either in it or its parents.",
+ Renderers.STRING
+ )
+ MAP.put(
+ SerializationErrors.SERIALIZER_NOT_FOUND,
+ "Serializer has not been found for type of this property. " +
+ "To use context serializer as fallback, explicitly annotate element with @ContextualSerialization"
+ )
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt
index 9b3a73cab85..3a6bb822744 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationComponentRegistrar.kt
@@ -21,14 +21,33 @@ import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.container.StorageComponentContainer
+import org.jetbrains.kotlin.container.useInstance
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
+import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
+import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationPluginDeclarationChecker
class SerializationComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
- ExpressionCodegenExtension.registerExtension(project, SerializationCodegenExtension())
SyntheticResolveExtension.registerExtension(project, SerializationResolveExtension())
+
+ ExpressionCodegenExtension.registerExtension(project, SerializationCodegenExtension())
JsSyntheticTranslateExtension.registerExtension(project, SerializationJsExtension())
IrGenerationExtension.registerExtension(project, SerializationLoweringExtension())
+
+ StorageComponentContainerContributor.registerExtension(project, SerializationPluginComponentContainerContributor())
}
-}
\ No newline at end of file
+}
+
+class SerializationPluginComponentContainerContributor : StorageComponentContainerContributor {
+ override fun registerModuleComponents(
+ container: StorageComponentContainer,
+ platform: TargetPlatform,
+ moduleDescriptor: ModuleDescriptor
+ ) {
+ container.useInstance(SerializationPluginDeclarationChecker())
+ }
+}
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 2e782af7bed..9ec47fbe279 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
@@ -18,9 +18,11 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
@@ -113,12 +115,15 @@ internal val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean
if (!annotations.hasAnnotation(SerializationAnnotations.serializableAnnotationFqName)) return false
// If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive.
// Otherwise, this descriptor is deserialized from another module and it is OK to check value right away.
- val lazyDesc = annotations.findAnnotation(SerializationAnnotations.serializableAnnotationFqName)
- as? LazyAnnotationDescriptor ?: return (serializableWith == null)
- val psi = lazyDesc.annotationEntry
+ val psi = findSerializableAnnotationDeclaration() ?: return (serializableWith == null)
return psi.valueArguments.isEmpty()
}
+internal fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
+ val lazyDesc = annotations.findAnnotation(SerializationAnnotations.serializableAnnotationFqName) 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/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt
index 6e06e428ed0..c77ffe4f71f 100644
--- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt
+++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt
@@ -51,15 +51,6 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
else
SerializableProperties(supers, bindingContext).serializableProperties + first + second
}
- .also(::validateUniqueSerialNames)
-
- private fun validateUniqueSerialNames(props: List) {
- val namesSet = mutableSetOf()
- props.forEach {
- if (!namesSet.add(it.name)) throw IllegalStateException("$serializableClass has duplicate serial name of property ${it.name}, either in it or its parents.")
- }
- }
-
private fun isPropSerializable(it: PropertyDescriptor) =
if (serializableClass.isInternalSerializable) !it.annotations.serialTransient
diff --git a/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationIDEContainerContributor.kt b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationIDEContainerContributor.kt
new file mode 100644
index 00000000000..47b9a25821b
--- /dev/null
+++ b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationIDEContainerContributor.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlinx.serialization.compiler.extensions
+
+import org.jetbrains.kotlin.container.StorageComponentContainer
+import org.jetbrains.kotlin.container.useInstance
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
+import org.jetbrains.kotlin.platform.TargetPlatform
+import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationPluginDeclarationChecker
+
+class SerializationIDEContainerContributor : StorageComponentContainerContributor {
+ override fun registerModuleComponents(
+ container: StorageComponentContainer,
+ platform: TargetPlatform,
+ moduleDescriptor: ModuleDescriptor
+ ) {
+ container.useInstance(SerializationPluginDeclarationChecker())
+ }
+}
\ No newline at end of file