diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt index 4ccefbd..6d87af5 100644 --- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt +++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt @@ -57,6 +57,7 @@ import me.eugeniomarletti.kotlin.metadata.kotlinMetadata import me.eugeniomarletti.kotlin.metadata.modality import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Class import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Modality.ABSTRACT +import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Modality.SEALED import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Type import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.TypeParameter import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.TypeParameter.Variance @@ -277,6 +278,11 @@ internal fun targetType(messager: Messager, ERROR, "@JsonClass can't be applied to $element: must not be an inner class", element) return null } + proto.modality == SEALED -> { + messager.printMessage( + ERROR, "@JsonClass can't be applied to $element: must not be sealed", element) + return null + } proto.modality == ABSTRACT -> { messager.printMessage( ERROR, "@JsonClass can't be applied to $element: must not be abstract", element) diff --git a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt index dbaacce..99896ea 100644 --- a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt +++ b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt @@ -118,6 +118,23 @@ class JsonClassCodegenProcessorTest { "error: @JsonClass can't be applied to AbstractClass: must not be abstract") } + @Test fun sealedClassesNotSupported() { + val call = KotlinCompilerCall(temporaryFolder.root) + call.inheritClasspath = true + call.addService(Processor::class, JsonClassCodegenProcessor::class) + call.addKt("source.kt", """ + |import com.squareup.moshi.JsonClass + | + |@JsonClass(generateAdapter = true) + |sealed class SealedClass(val a: Int) + |""".trimMargin()) + + val result = call.execute() + assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR) + assertThat(result.systemErr).contains( + "error: @JsonClass can't be applied to SealedClass: must not be sealed") + } + @Test fun innerClassesNotSupported() { val call = KotlinCompilerCall(temporaryFolder.root) call.inheritClasspath = true diff --git a/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt b/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt index 2d0d7a9..0aafe56 100644 --- a/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt +++ b/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt @@ -202,6 +202,9 @@ class KotlinJsonAdapterFactory : JsonAdapter.Factory { require(rawTypeKotlin.objectInstance == null) { "Cannot serialize object declaration ${rawType.name}" } + require(!rawTypeKotlin.isSealed) { + "Cannot reflectively serialize sealed class ${rawType.name}. Please register an adapter." + } val constructor = rawTypeKotlin.primaryConstructor ?: return null val parametersByName = constructor.parameters.associateBy { it.name } diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt index 18ce283..953d71f 100644 --- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt +++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt @@ -1113,6 +1113,21 @@ class KotlinJsonAdapterTest { @JvmSuppressWildcards(suppress = false) data class MapOfStringToClassCodegen(val map: Map = mapOf()) + @Test fun sealedClassesAreRejected() { + val moshi = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() + + try { + moshi.adapter() + fail() + } catch (e: IllegalArgumentException) { + assertThat(e).hasMessageContaining("Cannot reflectively serialize sealed class") + } + } + + sealed class SealedClass + private fun mapWildcardsParameterizedTest(type: Class, json: String, value: T) { // Ensure the map was created with the expected wildcards of a Kotlin map. val fieldType = type.getDeclaredField("map").genericType