Fix reading property function types (#1311)

This commit is contained in:
Zac Sweers
2021-03-13 20:47:52 -05:00
committed by GitHub
parent 935f8b872a
commit 3bc47519ab
4 changed files with 50 additions and 2 deletions

View File

@@ -17,6 +17,7 @@ package com.squareup.moshi.kotlin.codegen.api
import com.squareup.kotlinpoet.ARRAY
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.FILE
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
@@ -82,9 +83,12 @@ internal class AdapterGenerator(
// like for stylistic reasons.
"LocalVariableName",
// KotlinPoet always generates explicit public modifiers for public members.
"RedundantVisibilityModifier"
"RedundantVisibilityModifier",
// For LambdaTypeNames we have to import kotlin.functions.* types
"PLATFORM_CLASS_MAPPED_TO_KOTLIN"
).let { suppressions ->
AnnotationSpec.builder(Suppress::class)
.useSiteTarget(FILE)
.addMember(
suppressions.indices.joinToString { "%S" },
*suppressions
@@ -175,6 +179,7 @@ internal class AdapterGenerator(
val generatedAdapter = generateType().let(typeHook)
val result = FileSpec.builder(className.packageName, adapterName)
result.addComment("Code generated by moshi-kotlin-codegen. Do not edit.")
result.addAnnotation(COMMON_SUPPRESS)
result.addType(generatedAdapter)
return PreparedAdapter(result.build(), generatedAdapter.createProguardRule())
}
@@ -224,7 +229,6 @@ internal class AdapterGenerator(
private fun generateType(): TypeSpec {
val result = TypeSpec.classBuilder(adapterName)
.addAnnotation(COMMON_SUPPRESS)
result.superclass(jsonAdapterTypeName)

View File

@@ -27,6 +27,7 @@ import com.squareup.kotlinpoet.FLOAT
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.LONG
import com.squareup.kotlinpoet.LambdaTypeName
import com.squareup.kotlinpoet.NOTHING
import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
@@ -48,6 +49,18 @@ internal fun TypeName.findRawType(): ClassName? {
return when (this) {
is ClassName -> this
is ParameterizedTypeName -> rawType
is LambdaTypeName -> {
var count = parameters.size
if (receiver != null) {
count++
}
val functionSimpleName = if (count >= 23) {
"FunctionN"
} else {
"Function$count"
}
ClassName("kotlin.jvm.functions", functionSimpleName)
}
else -> null
}
}
@@ -93,6 +106,7 @@ internal fun TypeName.asTypeBlock(): CodeBlock {
val bound = bounds.firstOrNull() ?: ANY
return bound.asTypeBlock()
}
is LambdaTypeName -> return rawType().asTypeBlock()
is ClassName -> {
// Check against the non-nullable version for equality, but we'll keep the nullability in
// consideration when creating the CodeBlock if needed.
@@ -164,6 +178,14 @@ internal fun WildcardTypeName.deepCopy(transform: (TypeName) -> TypeName): TypeN
}
}
internal fun LambdaTypeName.deepCopy(transform: (TypeName) -> TypeName): TypeName {
return LambdaTypeName.get(
receiver?.let(transform),
parameters.map { it.toBuilder(type = transform(it.type)).build() },
transform(returnType)
).copy(nullable = isNullable, annotations = annotations, suspending = isSuspending)
}
internal interface TypeVariableResolver {
val parametersMap: Map<String, TypeVariableName>
operator fun get(index: String): TypeVariableName

View File

@@ -18,6 +18,7 @@ package com.squareup.moshi.kotlin.codegen
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.LambdaTypeName
import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
@@ -530,6 +531,7 @@ internal fun TypeName.unwrapTypeAlias(): TypeName {
is ParameterizedTypeName -> deepCopy(TypeName::unwrapTypeAlias)
is TypeVariableName -> deepCopy(transform = TypeName::unwrapTypeAlias)
is WildcardTypeName -> deepCopy(TypeName::unwrapTypeAlias)
is LambdaTypeName -> deepCopy(TypeName::unwrapTypeAlias)
else -> throw UnsupportedOperationException("Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.")
}
}

View File

@@ -1379,6 +1379,26 @@ class GeneratedAdaptersTest {
@JsonClass(generateAdapter = true)
data class MultipleGenerics<A, B, C, D>(val prop: String)
@Test fun functionPropertyTypes() {
val adapter = moshi.adapter<LambdaTypeNames>()
val json = "{\"id\":\"value\"}"
assertThat(adapter.fromJson(json)).isEqualTo(LambdaTypeNames("value"))
}
// Regression test for https://github.com/square/moshi/issues/1265
@JsonClass(generateAdapter = true)
data class LambdaTypeNames(
val id: String,
@Transient
val simple: ((String) -> Boolean)? = null,
// Receivers count as the first param, just annotated with a special annotation to indicate it's a receiver
@Transient
val receiver: (String.(String) -> Boolean)? = null,
// Tests that we use `FunctionN` since it has more than 23 params
@Transient
val arity: (String.(String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String, String) -> Boolean)? = null,
)
}
// Regression test for https://github.com/square/moshi/issues/1277