From 56aa90e2401434e4a375122ad51f60ba7a025276 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Thu, 16 Jun 2022 20:59:01 -0400 Subject: [PATCH 1/2] Add tests --- .../kotlinpoet/TypeVariableNameTest.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt index 4a45e3df2c..4eb2125272 100644 --- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt +++ b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt @@ -19,6 +19,7 @@ import com.google.common.truth.Truth.assertThat import com.squareup.kotlinpoet.TypeVariableName.Companion.NULLABLE_ANY_LIST import java.io.Serializable import kotlin.test.Test +import kotlin.test.assertFailsWith class TypeVariableNameTest { @Test fun nullableAnyIsImplicitBound() { @@ -251,4 +252,34 @@ class TypeVariableNameTest { } class GenericClass + + @Test + fun definitelyNonNullableType_simple() { + val typeName = TypeVariableName("T").asDefinitelyNonNullableType() + assertThat(typeName.toString()).isEqualTo("T & Any") + } + + @Test + fun definitelyNonNullableType_errorWhenNonNullableMadeNullable() { + assertFailsWith { + TypeVariableName("T").asDefinitelyNonNullableType() + .copy(nullable = true) + } + } + + @Test + fun definitelyNonNullableType_errorWhenNullableMadeNonNullable() { + assertFailsWith { + (TypeVariableName("T").copy(nullable = true) as TypeVariableName) + .asDefinitelyNonNullableType() + } + } + + @Test + fun definitelyNonNullableType_errorWhenNonNullAny() { + assertFailsWith { + TypeVariableName("T", listOf(ANY)) + .asDefinitelyNonNullableType() + } + } } From e1a6209b35dd1155ea2f52be28d958ed39f43276 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Thu, 16 Jun 2022 21:00:43 -0400 Subject: [PATCH 2/2] Implement copy(isDefinitelyNonNullable) --- .../squareup/kotlinpoet/TypeVariableName.kt | 63 +++++++++++++++++-- .../kotlinpoet/TypeVariableNameTest.kt | 8 +-- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt index 726350e8fc..5c590be9f1 100644 --- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt +++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt @@ -34,6 +34,7 @@ public class TypeVariableName private constructor( /** Either [KModifier.IN], [KModifier.OUT], or null. */ public val variance: KModifier? = null, public val isReified: Boolean = false, + public val isDefinitelyNonNullable: Boolean = false, nullable: Boolean = false, annotations: List = emptyList(), tags: Map, Any> = emptyMap() @@ -44,7 +45,7 @@ public class TypeVariableName private constructor( annotations: List, tags: Map, Any> ): TypeVariableName { - return copy(nullable, annotations, this.bounds, this.isReified, tags) + return copy(nullable, annotations, bounds, isReified, tags) } public fun copy( @@ -53,10 +54,57 @@ public class TypeVariableName private constructor( bounds: List = this.bounds.toList(), reified: Boolean = this.isReified, tags: Map, Any> = this.tagMap.tags + ): TypeVariableName = copy( + nullable = nullable, + annotations = annotations, + bounds = bounds, + reified = reified, + tags = tags, + isDefinitelyNonNullable = isDefinitelyNonNullable + ) + + private fun copy( + nullable: Boolean = this.isNullable, + annotations: List = this.annotations.toList(), + bounds: List = this.bounds.toList(), + reified: Boolean = this.isReified, + tags: Map, Any> = this.tagMap.tags, + isDefinitelyNonNullable: Boolean = this.isDefinitelyNonNullable, ): TypeVariableName { + if (isDefinitelyNonNullable) { + require(!nullable) { + "Cannot make a TypeVariableName nullable if it is already definitely non-nullable" + } + } return TypeVariableName( - name, bounds.withoutImplicitBound(), variance, reified, nullable, - annotations, tags + name = name, + bounds = bounds.withoutImplicitBound(), + variance = variance, + isReified = reified, + isDefinitelyNonNullable = isDefinitelyNonNullable, + nullable = nullable, + annotations = annotations, + tags = tags + ) + } + + /** + * Returns a new [TypeVariableName] copy of this that will emit as a "definitely non-nullable" + * type with the `T & Any` syntax. + * + * See https://kotlinlang.org/docs/whatsnew17.html#stable-definitely-non-nullable-types. + */ + public fun copy(isDefinitelyNonNullable: Boolean = true): TypeVariableName { + if (isDefinitelyNonNullable) { + require(!isNullable) { + "Cannot make a TypeVariableName non-nullable if it is already definitely non-nullable" + } + require(bounds.none { it == ANY }) { + "Cannot make a TypeVariableName non-nullable if it already has a non-nullable Any bound" + } + } + return copy( + isDefinitelyNonNullable = isDefinitelyNonNullable ) } @@ -64,7 +112,14 @@ public class TypeVariableName private constructor( return if (size == 1) this else filterNot { it == NULLABLE_ANY } } - override fun emit(out: CodeWriter) = out.emit(name) + override fun emit(out: CodeWriter): CodeWriter { + if (isDefinitelyNonNullable) { + out.emit("$name·&·Any") + } else { + out.emit(name) + } + return out + } public companion object { internal fun of( diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt index 4eb2125272..70c159e190 100644 --- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt +++ b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt @@ -255,14 +255,14 @@ class TypeVariableNameTest { @Test fun definitelyNonNullableType_simple() { - val typeName = TypeVariableName("T").asDefinitelyNonNullableType() + val typeName = TypeVariableName("T").copy() assertThat(typeName.toString()).isEqualTo("T & Any") } @Test fun definitelyNonNullableType_errorWhenNonNullableMadeNullable() { assertFailsWith { - TypeVariableName("T").asDefinitelyNonNullableType() + TypeVariableName("T").copy() .copy(nullable = true) } } @@ -271,7 +271,7 @@ class TypeVariableNameTest { fun definitelyNonNullableType_errorWhenNullableMadeNonNullable() { assertFailsWith { (TypeVariableName("T").copy(nullable = true) as TypeVariableName) - .asDefinitelyNonNullableType() + .copy() } } @@ -279,7 +279,7 @@ class TypeVariableNameTest { fun definitelyNonNullableType_errorWhenNonNullAny() { assertFailsWith { TypeVariableName("T", listOf(ANY)) - .asDefinitelyNonNullableType() + .copy() } } }