From 306664fb6aa97edc0ee2f8a1f659b2cad436d684 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Sun, 9 Sep 2018 17:03:39 -0400 Subject: [PATCH] Call Types.newParameterizedTypeWithOwner when necessary. Otherwise we crash with an exception attempting to create an adapter for an enclosed type that has a type parameter. I ran into this looking for a test case for issue 615. https://github.com/square/moshi/issues/615 --- .../moshi/kotlin/codegen/TypeRenderer.kt | 21 +++++--- .../kotlin/codgen/GeneratedAdaptersTest.kt | 50 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/TypeRenderer.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/TypeRenderer.kt index d6957fc..1beebb4 100644 --- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/TypeRenderer.kt +++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/TypeRenderer.kt @@ -66,12 +66,21 @@ abstract class TypeRenderer { Types::class, render(typeName.typeArguments[0].objectType())) } else { - val placeholders = typeName.typeArguments.joinToString(", ") { "%L" } - CodeBlock.of( - "%T.newParameterizedType(%T::class.java, $placeholders)", - Types::class, - typeName.rawType.objectType(), - *(typeName.typeArguments.map { render(it.objectType()) }.toTypedArray())) + val builder = CodeBlock.builder().apply { + add("%T.", Types::class) + val enclosingClassName = typeName.rawType.enclosingClassName() + if (enclosingClassName != null) { + add("newParameterizedTypeWithOwner(%L, ", render(enclosingClassName)) + } else { + add("newParameterizedType(") + } + add("%T::class.java", typeName.rawType.objectType()) + for (typeArgument in typeName.typeArguments) { + add(", %L", render(typeArgument.objectType())) + } + add(")") + } + builder.build() } } diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codgen/GeneratedAdaptersTest.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codgen/GeneratedAdaptersTest.kt index ab9a92d..c0cf8c0 100644 --- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codgen/GeneratedAdaptersTest.kt +++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codgen/GeneratedAdaptersTest.kt @@ -28,6 +28,7 @@ import com.squareup.moshi.ToJson import com.squareup.moshi.Types import org.assertj.core.api.Assertions.assertThat import org.intellij.lang.annotations.Language +import org.junit.Assert.assertNull import org.junit.Assert.fail import org.junit.Ignore import org.junit.Test @@ -901,6 +902,55 @@ class GeneratedAdaptersTest { } } + @Test fun propertyIsNothing() { + val moshi = Moshi.Builder() + .add(NothingAdapter()) + .build() + val jsonAdapter = moshi.adapter(HasNothingProperty::class.java).serializeNulls() + + val toJson = HasNothingProperty() + toJson.a = "1" + assertThat(jsonAdapter.toJson(toJson)).isEqualTo("""{"a":"1","b":null}""") + + val fromJson = jsonAdapter.fromJson("""{"a":"3","b":null}""")!! + assertThat(fromJson.a).isEqualTo("3") + assertNull(fromJson.b) + } + + class NothingAdapter { + @ToJson fun toJson(jsonWriter: JsonWriter, unused: Nothing?) { + jsonWriter.nullValue() + } + + @FromJson fun fromJson(jsonReader: JsonReader) : Nothing? { + jsonReader.skipValue() + return null + } + } + + @JsonClass(generateAdapter = true) + class HasNothingProperty { + var a: String? = null + var b: Nothing? = null + } + + @Test fun enclosedParameterizedType() { + val jsonAdapter = moshi.adapter(HasParameterizedProperty::class.java) + + assertThat(jsonAdapter.toJson(HasParameterizedProperty(Twins("1", "2")))) + .isEqualTo("""{"twins":{"a":"1","b":"2"}}""") + + val hasParameterizedProperty = jsonAdapter.fromJson("""{"twins":{"a":"3","b":"4"}}""")!! + assertThat(hasParameterizedProperty.twins.a).isEqualTo("3") + assertThat(hasParameterizedProperty.twins.b).isEqualTo("4") + } + + @JsonClass(generateAdapter = true) + class Twins(var a: T, var b: T) + + @JsonClass(generateAdapter = true) + class HasParameterizedProperty(val twins: Twins) + @JsonQualifier annotation class Uppercase(val inFrench: Boolean, val onSundays: Boolean = false)