Fix nullability not being preserved and clean up from shadowed names (#529)

* Add helper TypeName.asNullableIf extension

* Add missing nullability preservers to TypeResolver

* Fix shadowed names and add more missing nullable stuff
This commit is contained in:
Zac Sweers
2018-05-13 11:38:02 -07:00
committed by Jesse Wilson
parent 986cc4c794
commit 298aff24f5
4 changed files with 63 additions and 25 deletions

View File

@@ -35,15 +35,18 @@ open class TypeResolver {
is ParameterizedTypeName -> { is ParameterizedTypeName -> {
ParameterizedTypeName.get( ParameterizedTypeName.get(
typeName.rawType, *(typeName.typeArguments.map { resolve(it) }.toTypedArray())) typeName.rawType, *(typeName.typeArguments.map { resolve(it) }.toTypedArray()))
.asNullableIf(typeName.nullable)
} }
is WildcardTypeName -> { is WildcardTypeName -> {
when { when {
typeName.lowerBounds.size == 1 -> { typeName.lowerBounds.size == 1 -> {
WildcardTypeName.supertypeOf(resolve(typeName.lowerBounds[0])) WildcardTypeName.supertypeOf(resolve(typeName.lowerBounds[0]))
.asNullableIf(typeName.nullable)
} }
typeName.upperBounds.size == 1 -> { typeName.upperBounds.size == 1 -> {
WildcardTypeName.subtypeOf(resolve(typeName.upperBounds[0])) WildcardTypeName.subtypeOf(resolve(typeName.upperBounds[0]))
.asNullableIf(typeName.nullable)
} }
else -> { else -> {
throw IllegalArgumentException( throw IllegalArgumentException(
@@ -57,4 +60,4 @@ open class TypeResolver {
else -> throw IllegalArgumentException("Unrepresentable type: $typeName") else -> throw IllegalArgumentException("Unrepresentable type: $typeName")
} }
} }
} }

View File

@@ -26,3 +26,7 @@ internal fun TypeName.rawType(): ClassName {
else -> throw IllegalArgumentException("Cannot get raw type from $this") else -> throw IllegalArgumentException("Cannot get raw type from $this")
} }
} }
internal fun TypeName.asNullableIf(condition: Boolean): TypeName {
return if (condition) asNullable() else this
}

View File

@@ -72,15 +72,17 @@ internal fun Type.asTypeName(
if (hasFlexibleUpperBound()) { if (hasFlexibleUpperBound()) {
return WildcardTypeName.subtypeOf( return WildcardTypeName.subtypeOf(
flexibleUpperBound.asTypeName(nameResolver, getTypeParameter, resolveAliases)) flexibleUpperBound.asTypeName(nameResolver, getTypeParameter, resolveAliases))
.asNullableIf(nullable)
} else if (hasOuterType()) { } else if (hasOuterType()) {
return WildcardTypeName.supertypeOf( return WildcardTypeName.supertypeOf(
outerType.asTypeName(nameResolver, getTypeParameter, resolveAliases)) outerType.asTypeName(nameResolver, getTypeParameter, resolveAliases))
.asNullableIf(nullable)
} }
val realType = when { val realType = when {
hasTypeParameter() -> return getTypeParameter(typeParameter) hasTypeParameter() -> return getTypeParameter(typeParameter)
.asTypeName(nameResolver, getTypeParameter, resolveAliases) .asTypeName(nameResolver, getTypeParameter, resolveAliases)
.let { if (nullable) it.asNullable() else it } .asNullableIf(nullable)
hasTypeParameterName() -> typeParameterName hasTypeParameterName() -> typeParameterName
hasAbbreviatedType() && !resolveAliases -> abbreviatedType.typeAliasName hasAbbreviatedType() && !resolveAliases -> abbreviatedType.typeAliasName
else -> className else -> className
@@ -91,43 +93,36 @@ internal fun Type.asTypeName(
.replace("/", ".")) .replace("/", "."))
if (argumentList.isNotEmpty()) { if (argumentList.isNotEmpty()) {
val remappedArgs: Array<TypeName> = argumentList.map { val remappedArgs: Array<TypeName> = argumentList.map { argumentType ->
val projection = if (it.hasProjection()) { val nullableProjection = if (argumentType.hasProjection()) {
it.projection argumentType.projection
} else null } else null
if (it.hasType()) { if (argumentType.hasType()) {
it.type.asTypeName(nameResolver, getTypeParameter, resolveAliases) argumentType.type.asTypeName(nameResolver, getTypeParameter, resolveAliases)
.let { typeName -> .let { argumentTypeName ->
projection?.let { nullableProjection?.let { projection ->
when (it) { when (projection) {
Type.Argument.Projection.IN -> WildcardTypeName.supertypeOf( Type.Argument.Projection.IN -> WildcardTypeName.supertypeOf(argumentTypeName)
typeName)
Type.Argument.Projection.OUT -> { Type.Argument.Projection.OUT -> {
if (typeName == ANY) { if (argumentTypeName == ANY) {
// This becomes a *, which we actually don't want here. // This becomes a *, which we actually don't want here.
// List<Any> works with List<*>, but List<*> doesn't work with List<Any> // List<Any> works with List<*>, but List<*> doesn't work with List<Any>
typeName argumentTypeName
} else { } else {
WildcardTypeName.subtypeOf(typeName) WildcardTypeName.subtypeOf(argumentTypeName)
} }
} }
Type.Argument.Projection.STAR -> WildcardTypeName.subtypeOf( Type.Argument.Projection.STAR -> WildcardTypeName.subtypeOf(ANY)
ANY)
Type.Argument.Projection.INV -> TODO("INV projection is unsupported") Type.Argument.Projection.INV -> TODO("INV projection is unsupported")
} }
} ?: typeName } ?: argumentTypeName
} }
} else { } else {
WildcardTypeName.subtypeOf(ANY) WildcardTypeName.subtypeOf(ANY)
} }
}.toTypedArray() }.toTypedArray()
typeName = ParameterizedTypeName.get( typeName = ParameterizedTypeName.get(typeName as ClassName, *remappedArgs)
typeName as ClassName, *remappedArgs)
} }
if (nullable) { return typeName.asNullableIf(nullable)
typeName = typeName.asNullable()
}
return typeName
} }

View File

@@ -0,0 +1,36 @@
package com.squareup.moshi
import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.WildcardTypeName
import com.squareup.kotlinpoet.asClassName
import org.junit.Test
class TypeResolverTest {
private val resolver = TypeResolver()
@Test
fun ensureClassNameNullabilityIsPreserved() {
assertThat(resolver.resolve(Int::class.asClassName().asNullable()).nullable).isTrue()
}
@Test
fun ensureParameterizedNullabilityIsPreserved() {
val nullableTypeName = ParameterizedTypeName.get(
List::class.asClassName(),
String::class.asClassName())
.asNullable()
assertThat(resolver.resolve(nullableTypeName).nullable).isTrue()
}
@Test
fun ensureWildcardNullabilityIsPreserved() {
val nullableTypeName = WildcardTypeName.subtypeOf(List::class.asClassName())
.asNullable()
assertThat(resolver.resolve(nullableTypeName).nullable).isTrue()
}
}