Upstream KSP implementation (#1393)

This commit is contained in:
Zac Sweers
2021-10-16 01:52:04 -04:00
committed by GitHub
parent de8bbf12f5
commit 2db351f8ed
25 changed files with 2164 additions and 131 deletions

View File

@@ -18,7 +18,15 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm")
kotlin("kapt")
kotlin("kapt") apply false
alias(libs.plugins.ksp) apply false
}
val useKsp = hasProperty("useKsp")
if (useKsp) {
apply(plugin = "com.google.devtools.ksp")
} else {
apply(plugin = "org.jetbrains.kotlin.kapt")
}
tasks.withType<Test>().configureEach {
@@ -37,9 +45,14 @@ tasks.withType<KotlinCompile>().configureEach {
}
dependencies {
kaptTest(project(":kotlin:codegen"))
if (useKsp) {
"kspTest"(project(":kotlin:codegen"))
} else {
"kaptTest"(project(":kotlin:codegen"))
}
testImplementation(project(":moshi"))
testImplementation(project(":kotlin:reflect"))
testImplementation(project(":kotlin:tests:extra-moshi-test-module"))
testImplementation(kotlin("reflect"))
testImplementation(libs.junit)
testImplementation(libs.assertj)

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2021 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
kotlin("jvm")
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright (C) 2021 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.moshi.kotlin.codegen.test.extra
public abstract class AbstractClassInModuleA

View File

@@ -22,12 +22,13 @@ import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonAdapter.Factory
import com.squareup.moshi.JsonClass
import com.squareup.moshi.JsonDataException
import com.squareup.moshi.JsonQualifier
import com.squareup.moshi.Moshi
import com.squareup.moshi.ToJson
import com.squareup.moshi.Types
import com.squareup.moshi.adapter
import com.squareup.moshi.kotlin.codegen.test.extra.AbstractClassInModuleA
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.squareup.moshi.rawType
import com.squareup.moshi.supertypeOf
import org.intellij.lang.annotations.Language
import org.junit.Assert.fail
import org.junit.Test
@@ -63,11 +64,11 @@ class DualKotlinTest(useReflection: Boolean) {
object : Factory {
override fun create(
type: Type,
annotations: Set<Annotation>,
annotations: MutableSet<out Annotation>,
moshi: Moshi
): JsonAdapter<*>? {
// Prevent falling back to generated adapter lookup
val rawType = type.rawType
val rawType = Types.getRawType(type)
val metadataClass = Class.forName("kotlin.Metadata") as Class<out Annotation>
check(rawType.isEnum || !rawType.isAnnotationPresent(metadataClass)) {
"Unhandled Kotlin type in reflective test! $rawType"
@@ -248,21 +249,21 @@ class DualKotlinTest(useReflection: Boolean) {
val hasNonNullConstructorParameterAdapter =
localMoshi.adapter<HasNonNullConstructorParameter>()
assertThat(
//language=JSON
hasNonNullConstructorParameterAdapter
//language=JSON
.fromJson("{\"a\":null}")
).isEqualTo(HasNonNullConstructorParameter("fallback"))
val hasNullableConstructorParameterAdapter =
localMoshi.adapter<HasNullableConstructorParameter>()
assertThat(
//language=JSON
hasNullableConstructorParameterAdapter
//language=JSON
.fromJson("{\"a\":null}")
).isEqualTo(HasNullableConstructorParameter("fallback"))
//language=JSON
assertThat(
hasNullableConstructorParameterAdapter
//language=JSON
.toJson(HasNullableConstructorParameter(null))
).isEqualTo("{\"a\":\"fallback\"}")
}
@@ -284,7 +285,7 @@ class DualKotlinTest(useReflection: Boolean) {
assertThat(decoded.a).isEqualTo(null)
}
@Test fun inlineClass() {
@Test fun valueClass() {
val adapter = moshi.adapter<ValueClass>()
val inline = ValueClass(5)
@@ -297,6 +298,13 @@ class DualKotlinTest(useReflection: Boolean) {
"""{"i":6}"""
val result = adapter.fromJson(testJson)!!
assertThat(result.i).isEqualTo(6)
// TODO doesn't work yet.
// need to invoke the constructor_impl$default static method, invoke constructor with result
// val testEmptyJson =
// """{}"""
// val result2 = adapter.fromJson(testEmptyJson)!!
// assertThat(result2.i).isEqualTo(0)
}
@JsonClass(generateAdapter = true)
@@ -339,6 +347,50 @@ class DualKotlinTest(useReflection: Boolean) {
abstract class Asset<A : Asset<A>>
abstract class AssetMetaData<A : Asset<A>>
// Regression test for https://github.com/ZacSweers/MoshiX/issues/125
@Test fun selfReferencingTypeVars() {
val adapter = moshi.adapter<StringNodeNumberNode>()
val data = StringNodeNumberNode().also {
it.t = StringNodeNumberNode().also {
it.text = "child 1"
}
it.text = "root"
it.r = NumberStringNode().also {
it.number = 0
it.t = NumberStringNode().also {
it.number = 1
}
it.r = StringNodeNumberNode().also {
it.text = "grand child 1"
}
}
}
assertThat(adapter.toJson(data))
//language=JSON
.isEqualTo(
"""
{"text":"root","t":{"text":"child 1"},"r":{"number":0,"t":{"number":1},"r":{"text":"grand child 1"}}}
""".trimIndent()
)
}
@JsonClass(generateAdapter = true)
open class Node<T : Node<T, R>, R : Node<R, T>> {
var t: T? = null
var r: R? = null
}
@JsonClass(generateAdapter = true)
class StringNodeNumberNode : Node<StringNodeNumberNode, NumberStringNode>() {
var text: String = ""
}
@JsonClass(generateAdapter = true)
class NumberStringNode : Node<NumberStringNode, StringNodeNumberNode>() {
var number: Int = 0
}
// Regression test for https://github.com/square/moshi/issues/968
@Test fun abstractSuperProperties() {
val adapter = moshi.adapter<InternalAbstractProperty>()
@@ -447,7 +499,7 @@ class DualKotlinTest(useReflection: Boolean) {
@Test fun typeAliasUnwrapping() {
val adapter = moshi
.newBuilder()
.add(supertypeOf<Int>(), moshi.adapter<Int>())
.add(Types.supertypeOf(Int::class.javaObjectType), moshi.adapter<Int>())
.build()
.adapter<TypeAliasUnwrapping>()
@@ -587,8 +639,7 @@ class DualKotlinTest(useReflection: Boolean) {
@JsonClass(generateAdapter = true)
data class OutDeclaration<out T>(val input: T)
// Regression test for https://github.com/square/moshi/issues/1244
@Test fun backwardReferencingTypeVarsAndIntersectionTypes() {
@Test fun intersectionTypes() {
val adapter = moshi.adapter<IntersectionTypes<IntersectionTypesEnum>>()
@Language("JSON")
@@ -625,7 +676,7 @@ data class GenericClass<T>(val value: T)
// Has to be outside since value classes are only allowed on top level
@JvmInline
@JsonClass(generateAdapter = true)
value class ValueClass(val i: Int)
value class ValueClass(val i: Int = 0)
typealias A = Int
typealias NullableA = A?
@@ -634,3 +685,23 @@ typealias NullableB = B?
typealias C = NullableA
typealias D = C
typealias E = D
// Regression test for enum constants in annotations and array types
// https://github.com/ZacSweers/MoshiX/issues/103
@Retention(RUNTIME)
@JsonQualifier
annotation class UpperCase(val foo: Array<Foo>)
enum class Foo { BAR }
@JsonClass(generateAdapter = true)
data class ClassWithQualifier(
@UpperCase(foo = [Foo.BAR])
val a: Int
)
// Regression for https://github.com/ZacSweers/MoshiX/issues/120
@JsonClass(generateAdapter = true)
data class DataClassInModuleB(
val id: String
) : AbstractClassInModuleA()

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2021 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.moshi.kotlin.codegen
import com.google.common.truth.Truth.assertThat
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import org.junit.Test
// Regression tests specific to Moshi-KSP
class MoshiKspTest {
private val moshi = Moshi.Builder().build()
// Regression test for https://github.com/ZacSweers/MoshiX/issues/44
@Test
fun onlyInterfaceSupertypes() {
val adapter = moshi.adapter<SimpleImpl>()
//language=JSON
val json = """{"a":"aValue","b":"bValue"}"""
val expected = SimpleImpl("aValue", "bValue")
val instance = adapter.fromJson(json)!!
assertThat(instance).isEqualTo(expected)
val encoded = adapter.toJson(instance)
assertThat(encoded).isEqualTo(json)
}
interface SimpleInterface {
val a: String
}
// NOTE the Any() superclass is important to test that we're detecting the farthest parent class
// correct.y
@JsonClass(generateAdapter = true)
data class SimpleImpl(override val a: String, val b: String) : Any(), SimpleInterface
}