mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 16:09:21 +08:00
Fix reading property function types (#1311)
This commit is contained in:
@@ -17,6 +17,7 @@ package com.squareup.moshi.kotlin.codegen.api
|
|||||||
|
|
||||||
import com.squareup.kotlinpoet.ARRAY
|
import com.squareup.kotlinpoet.ARRAY
|
||||||
import com.squareup.kotlinpoet.AnnotationSpec
|
import com.squareup.kotlinpoet.AnnotationSpec
|
||||||
|
import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.FILE
|
||||||
import com.squareup.kotlinpoet.CodeBlock
|
import com.squareup.kotlinpoet.CodeBlock
|
||||||
import com.squareup.kotlinpoet.FileSpec
|
import com.squareup.kotlinpoet.FileSpec
|
||||||
import com.squareup.kotlinpoet.FunSpec
|
import com.squareup.kotlinpoet.FunSpec
|
||||||
@@ -82,9 +83,12 @@ internal class AdapterGenerator(
|
|||||||
// like for stylistic reasons.
|
// like for stylistic reasons.
|
||||||
"LocalVariableName",
|
"LocalVariableName",
|
||||||
// KotlinPoet always generates explicit public modifiers for public members.
|
// 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 ->
|
).let { suppressions ->
|
||||||
AnnotationSpec.builder(Suppress::class)
|
AnnotationSpec.builder(Suppress::class)
|
||||||
|
.useSiteTarget(FILE)
|
||||||
.addMember(
|
.addMember(
|
||||||
suppressions.indices.joinToString { "%S" },
|
suppressions.indices.joinToString { "%S" },
|
||||||
*suppressions
|
*suppressions
|
||||||
@@ -175,6 +179,7 @@ internal class AdapterGenerator(
|
|||||||
val generatedAdapter = generateType().let(typeHook)
|
val generatedAdapter = generateType().let(typeHook)
|
||||||
val result = FileSpec.builder(className.packageName, adapterName)
|
val result = FileSpec.builder(className.packageName, adapterName)
|
||||||
result.addComment("Code generated by moshi-kotlin-codegen. Do not edit.")
|
result.addComment("Code generated by moshi-kotlin-codegen. Do not edit.")
|
||||||
|
result.addAnnotation(COMMON_SUPPRESS)
|
||||||
result.addType(generatedAdapter)
|
result.addType(generatedAdapter)
|
||||||
return PreparedAdapter(result.build(), generatedAdapter.createProguardRule())
|
return PreparedAdapter(result.build(), generatedAdapter.createProguardRule())
|
||||||
}
|
}
|
||||||
@@ -224,7 +229,6 @@ internal class AdapterGenerator(
|
|||||||
|
|
||||||
private fun generateType(): TypeSpec {
|
private fun generateType(): TypeSpec {
|
||||||
val result = TypeSpec.classBuilder(adapterName)
|
val result = TypeSpec.classBuilder(adapterName)
|
||||||
.addAnnotation(COMMON_SUPPRESS)
|
|
||||||
|
|
||||||
result.superclass(jsonAdapterTypeName)
|
result.superclass(jsonAdapterTypeName)
|
||||||
|
|
||||||
|
@@ -27,6 +27,7 @@ import com.squareup.kotlinpoet.FLOAT
|
|||||||
import com.squareup.kotlinpoet.INT
|
import com.squareup.kotlinpoet.INT
|
||||||
import com.squareup.kotlinpoet.KModifier
|
import com.squareup.kotlinpoet.KModifier
|
||||||
import com.squareup.kotlinpoet.LONG
|
import com.squareup.kotlinpoet.LONG
|
||||||
|
import com.squareup.kotlinpoet.LambdaTypeName
|
||||||
import com.squareup.kotlinpoet.NOTHING
|
import com.squareup.kotlinpoet.NOTHING
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||||
@@ -48,6 +49,18 @@ internal fun TypeName.findRawType(): ClassName? {
|
|||||||
return when (this) {
|
return when (this) {
|
||||||
is ClassName -> this
|
is ClassName -> this
|
||||||
is ParameterizedTypeName -> rawType
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,6 +106,7 @@ internal fun TypeName.asTypeBlock(): CodeBlock {
|
|||||||
val bound = bounds.firstOrNull() ?: ANY
|
val bound = bounds.firstOrNull() ?: ANY
|
||||||
return bound.asTypeBlock()
|
return bound.asTypeBlock()
|
||||||
}
|
}
|
||||||
|
is LambdaTypeName -> return rawType().asTypeBlock()
|
||||||
is ClassName -> {
|
is ClassName -> {
|
||||||
// Check against the non-nullable version for equality, but we'll keep the nullability in
|
// Check against the non-nullable version for equality, but we'll keep the nullability in
|
||||||
// consideration when creating the CodeBlock if needed.
|
// 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 {
|
internal interface TypeVariableResolver {
|
||||||
val parametersMap: Map<String, TypeVariableName>
|
val parametersMap: Map<String, TypeVariableName>
|
||||||
operator fun get(index: String): TypeVariableName
|
operator fun get(index: String): TypeVariableName
|
||||||
|
@@ -18,6 +18,7 @@ package com.squareup.moshi.kotlin.codegen
|
|||||||
import com.squareup.kotlinpoet.AnnotationSpec
|
import com.squareup.kotlinpoet.AnnotationSpec
|
||||||
import com.squareup.kotlinpoet.ClassName
|
import com.squareup.kotlinpoet.ClassName
|
||||||
import com.squareup.kotlinpoet.KModifier
|
import com.squareup.kotlinpoet.KModifier
|
||||||
|
import com.squareup.kotlinpoet.LambdaTypeName
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
import com.squareup.kotlinpoet.TypeName
|
import com.squareup.kotlinpoet.TypeName
|
||||||
import com.squareup.kotlinpoet.TypeSpec
|
import com.squareup.kotlinpoet.TypeSpec
|
||||||
@@ -530,6 +531,7 @@ internal fun TypeName.unwrapTypeAlias(): TypeName {
|
|||||||
is ParameterizedTypeName -> deepCopy(TypeName::unwrapTypeAlias)
|
is ParameterizedTypeName -> deepCopy(TypeName::unwrapTypeAlias)
|
||||||
is TypeVariableName -> deepCopy(transform = TypeName::unwrapTypeAlias)
|
is TypeVariableName -> deepCopy(transform = TypeName::unwrapTypeAlias)
|
||||||
is WildcardTypeName -> deepCopy(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.")
|
else -> throw UnsupportedOperationException("Type '${javaClass.simpleName}' is illegal. Only classes, parameterized types, wildcard types, or type variables are allowed.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1379,6 +1379,26 @@ class GeneratedAdaptersTest {
|
|||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MultipleGenerics<A, B, C, D>(val prop: String)
|
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
|
// Regression test for https://github.com/square/moshi/issues/1277
|
||||||
|
Reference in New Issue
Block a user