Skip to content

Commit

Permalink
Fix reading property function types (#1311)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZacSweers authored Mar 14, 2021
1 parent 935f8b8 commit 3bc4751
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
}
Expand Down Expand Up @@ -224,7 +229,6 @@ internal class AdapterGenerator(

private fun generateType(): TypeSpec {
val result = TypeSpec.classBuilder(adapterName)
.addAnnotation(COMMON_SUPPRESS)

result.superclass(jsonAdapterTypeName)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 3bc4751

Please sign in to comment.