[go: nahoru, domu]

Treat platform types as nullable by default

This causes KotlinPoet names to be nullable when the type is a platform type, i.e. from Java sources or compiled Java code. Some exceptions are added for types that are never null, these are: annotation value types, exception types in 'throws' declarations, super types and in Kotlin only the array type of a vararg.

Two additional changes that contribute to this CL are:
 * Remove workaround for https://github.com/google/ksp/issues/1004, it is no longer needed and the KSType for annotation values are correctly returned by the latest version of KSP along with the correct nullability.
 * Remove 3 lazy properties in KspProcessingEnv, these are the KSTypeVarianceResolver, KspArrayType.Factory and CommonTypes. All 3 helpers retained KSP's resolver across rounds, which shouldn't be done as it can lead to stale caches.
 * Avoid usages of toKS() to check for KSP 2, instead create a helper internal function.

See also https://kotlinlang.org/docs/java-interop.html#notation-for-platform-types

Bug: 331427836
Test: ./gradlew :room:room-compiler-processing:test
Change-Id: I643ba1d9c005b1b5c5084d08a23288298bec7489
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt
index 9e5fa7d..5699b25 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt
@@ -34,22 +34,10 @@
     env: KspProcessingEnv,
     containing: KspType?
 ): KspType {
-    return if (containing?.typeElement?.isAnnotationClass() == true) {
-        // Calling #getOriginatingReference() or #returnTypeAsMemberOf() currently fails for
-        // annotation classes due to https://github.com/google/ksp/issues/1004. Thus, we avoid
-        // calling those methods and just return the return type directly. This should be safe for
-        // annotation classes since they can't extend other types or use generics.
-        val returnTypeReference = checkNotNull(returnType)
-        env.wrap(
-            originatingReference = returnTypeReference,
-            ksType = returnTypeReference.resolve()
-        )
-    } else {
-        env.wrap(
-            originatingReference = checkNotNull(getOriginatingReference()),
-            ksType = returnTypeAsMemberOf(ksType = containing?.ksType)
-        )
-    }
+    return env.wrap(
+        originatingReference = checkNotNull(getOriginatingReference()),
+        ksType = returnTypeAsMemberOf(ksType = containing?.ksType)
+    )
 }
 
 private fun KSFunctionDeclaration.getOriginatingReference(): KSTypeReference? {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt
index 7a80d08..286b0b7 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeKotlinPoetExt.kt
@@ -94,15 +94,9 @@
     val typeName = createModifiableTypeVariableName(name = name.asString(), bounds = mutableBounds)
     typeArgumentTypeLookup[name] = typeName
     val resolvedBounds = bounds.map { typeReference ->
-        typeReference.asKTypeName(resolver, typeArgumentTypeLookup).let { kTypeName ->
-            typeReference.resolve().let {
-                if (it.nullability == Nullability.PLATFORM) {
-                    kTypeName.copy(nullable = true)
-                } else {
-                    kTypeName
-                }
-            }
-        }
+        val ksType = typeReference.resolve()
+        typeReference.asKTypeName(resolver, typeArgumentTypeLookup)
+            .copy(nullable = ksType.isMarkedNullable || ksType.nullability == Nullability.PLATFORM)
     }.toList()
     if (resolvedBounds.isNotEmpty()) {
         mutableBounds.addAll(resolvedBounds)
@@ -164,7 +158,7 @@
         typeName.parameterizedBy(args)
     } else {
         this.declaration.asKTypeName(resolver, typeArgumentTypeLookup)
-    }.copy(nullable = isMarkedNullable)
+    }.copy(nullable = isMarkedNullable || nullability == Nullability.PLATFORM)
 }
 
 /**
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt
index ee283c1a..ca9fac4 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt
@@ -51,7 +51,8 @@
     override val thrownTypes: List<XType> by lazy {
         env.resolver.getJvmCheckedException(declaration).map {
             env.wrap(
-                ksType = it,
+                // Thrown exception types are never nullable
+                ksType = it.makeNotNullable(),
                 allowPrimitives = false
             )
         }.toList()
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
index bcadc1a..df2511a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableParameterElement.kt
@@ -27,6 +27,7 @@
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 import com.google.devtools.ksp.symbol.KSPropertySetter
 import com.google.devtools.ksp.symbol.KSValueParameter
+import com.google.devtools.ksp.symbol.Origin
 
 internal class KspExecutableParameterElement(
     env: KspProcessingEnv,
@@ -92,10 +93,20 @@
                 asMemberOf = container,
             )
         )
-        // In KSP2 the varargs have the component type instead of the array type. We make it always
-        // return the array type in XProcessing.
-        return if (isVarArgs() && !type.isArray()) {
-            env.getArrayType(env.getWildcardType(producerExtends = type))
+        return if (isVarArgs()) {
+            if (!type.isArray()) {
+                // In KSP2 the varargs have the component type instead of the array type.
+                // We make it always return the array type in XProcessing.
+                env.getArrayType(env.getWildcardType(producerExtends = type))
+            } else {
+                type
+            }.run {
+                // In Kotlin the vararg array is never null
+                when (parameter.origin) {
+                    Origin.KOTLIN, Origin.KOTLIN_LIB -> makeNonNullable()
+                    else -> makeNullable()
+                }
+            }
         } else {
             type
         }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
index 5627679..707b085 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspProcessingEnv.kt
@@ -82,14 +82,17 @@
        }
     }
 
+    internal val isKsp2 by lazy {
+        delegate.kspVersion >= KotlinVersion(2, 0)
+    }
+
     private val ksFileMemberContainers = mutableMapOf<KSFile, KspFileMemberContainer>()
 
     /**
      * Variance resolver to find JVM types of KSType. See [KSTypeVarianceResolver] docs for details.
      */
-    private val ksTypeVarianceResolver by lazy {
-        KSTypeVarianceResolver(resolver)
-    }
+    private val ksTypeVarianceResolver
+        get() = KSTypeVarianceResolver(resolver)
 
     private var _resolver: Resolver? = null
 
@@ -125,15 +128,11 @@
 
     override val messager: XMessager = KspMessager(logger)
 
-    private val arrayTypeFactory by lazy {
-        KspArrayType.Factory(this)
-    }
+    private val arrayTypeFactory
+        get() = KspArrayType.Factory(this)
 
     override val filer: XFiler = KspFiler(codeGenerator, messager)
 
-    val commonTypes
-        get() = CommonTypes()
-
     val voidType
         get() = KspVoidType(
             env = this,
@@ -384,10 +383,6 @@
         return returnType(type1).isSameType(returnType(type2))
     }
 
-    inner class CommonTypes() {
-        val anyType: XType = requireType("kotlin.Any")
-    }
-
     internal enum class JvmDefaultMode(val option: String) {
         DISABLE("disable"),
         ALL_COMPATIBILITY("all-compatibility"),
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
index 18ce9fa..d9a5710 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
@@ -113,7 +113,8 @@
     }
 
     override val superTypes: List<XType> by lazy {
-        if (xTypeName == XTypeName.ANY_OBJECT) {
+        val anyType = env.requireType(Any::class)
+        if (this == anyType) {
             // The object class doesn't have any supertypes.
             return@lazy emptyList<XType>()
         }
@@ -125,16 +126,16 @@
             env.wrap(
                 ksType = resolveTypeArguments(it.resolve(), resolvedTypeArguments),
                 allowPrimitives = false
-            )
+            ).makeNonNullable()
         } ?: emptyList()
         val (superClasses, superInterfaces) = superTypes.partition {
             it.typeElement?.isClass() == true
         }
         // Per documentation, always return the class before the interfaces.
         if (superClasses.isEmpty()) {
-            // Return Object when there's no explicit super class specified on the class/interface.
-            // This matches javac's Types#directSupertypes().
-            listOf(env.requireType(TypeName.OBJECT)) + superInterfaces
+            // Return Any / Object when there's no explicit super class specified on the\
+            // class/interface. This matches javac's Types#directSupertypes().
+            listOf(anyType) + superInterfaces
         } else {
             check(superClasses.size == 1) {
                 "Class ${this.typeName} should have only one super class. Found" +
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 93b7fb7..99c141a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -102,27 +102,28 @@
     }
 
     override val superClass: XType? by lazy {
+        val anyTypeElement = env.requireTypeElement(Any::class)
         if (isInterface()) {
             // interfaces don't have super classes (they do have super types)
             null
-        } else if (this == env.commonTypes.anyType.typeElement) {
+        } else if (this == anyTypeElement) {
             null
         } else {
             declaration.superTypes
                 .singleOrNull {
                     val declaration = it.resolve().declaration.replaceTypeAliases()
                     declaration is KSClassDeclaration && declaration.classKind == ClassKind.CLASS
-                }?.let { env.wrap(it) }
-                ?: env.commonTypes.anyType
+                }?.let { env.wrap(it).makeNonNullable() }
+                ?: anyTypeElement.type
         }
     }
 
     override val superInterfaces by lazy {
-        declaration.superTypes.asSequence()
+        declaration.superTypes
             .filter {
                 val declaration = it.resolve().declaration.replaceTypeAliases()
                 declaration is KSClassDeclaration && declaration.classKind == ClassKind.INTERFACE
-            }.mapTo(mutableListOf()) { env.wrap(it) }
+            }.mapTo(mutableListOf()) { env.wrap(it).makeNonNullable() }
     }
 
     @Deprecated(
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
index 3f57122..f6ffe01 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
@@ -125,7 +125,8 @@
     override val thrownTypes: List<XType> by lazy {
         env.resolver.getJvmCheckedException(accessor).map {
             env.wrap(
-                ksType = it,
+                // Thrown exception types are never nullable
+                ksType = it.makeNotNullable(),
                 allowPrimitives = false
             )
         }.toList()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
index 46e753d..be10792 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
@@ -18,7 +18,6 @@
 
 import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.JArrayTypeName
-import androidx.room.compiler.processing.compat.XConverters.toKS
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.asJClassName
@@ -1284,45 +1283,40 @@
             )
             val kClassKTypeName = kotlin.reflect.KClass::class.asKClassName().parameterizedBy(STAR)
             fun checkSingleValue(annotationValue: XAnnotationValue, expectedValue: String) {
-                // TODO(bcorso): Consider making the value types match in this case.
-                if (!invocation.isKsp ||
-                        (invocation.processingEnv.toKS().kspVersion < KotlinVersion(2, 0) &&
-                        sourceKind == SourceKind.JAVA &&
-                        !isPreCompiled)) {
-                    assertThat(annotationValue.valueType.asTypeName().java)
-                        .isEqualTo(classJTypeName)
-                } else {
+                if (invocation.isKsp) {
                     assertThat(annotationValue.valueType.asTypeName().java)
                         .isEqualTo(kClassJTypeName)
                     assertThat(annotationValue.valueType.asTypeName().kotlin)
                         .isEqualTo(kClassKTypeName)
+                } else {
+                    assertThat(annotationValue.valueType.asTypeName().java)
+                        .isEqualTo(classJTypeName)
                 }
                 assertThat(annotationValue.hasTypeValue()).isTrue()
                 assertThat(annotationValue.asType().typeElement?.name).isEqualTo(expectedValue)
             }
 
             fun checkListValues(annotationValue: XAnnotationValue, vararg expectedValues: String) {
-                // TODO(bcorso): Consider making the value types match in this case.
-                if (!invocation.isKsp ||
-                        (invocation.processingEnv.toKS().kspVersion < KotlinVersion(2, 0) &&
-                        sourceKind == SourceKind.JAVA &&
-                        !isPreCompiled)) {
-                    assertThat(annotationValue.valueType.asTypeName().java)
-                        .isEqualTo(JArrayTypeName.of(classJTypeName))
-                } else {
+                if (invocation.isKsp) {
                     assertThat(annotationValue.valueType.asTypeName().java)
                         .isEqualTo(JArrayTypeName.of(kClassJTypeName))
                     if (sourceKind == SourceKind.KOTLIN &&
-                        annotationValue.name.contains("VarArgs")) {
+                        annotationValue.name.contains("VarArgs")
+                    ) {
                         // Kotlin vararg are producers
                         assertThat(annotationValue.valueType.asTypeName().kotlin)
-                            .isEqualTo(ARRAY.parameterizedBy(
-                                KWildcardTypeName.producerOf(kClassKTypeName))
+                            .isEqualTo(
+                                ARRAY.parameterizedBy(
+                                    KWildcardTypeName.producerOf(kClassKTypeName)
+                                )
                             )
                     } else {
                         assertThat(annotationValue.valueType.asTypeName().kotlin)
                             .isEqualTo(ARRAY.parameterizedBy(kClassKTypeName))
                     }
+                } else {
+                    assertThat(annotationValue.valueType.asTypeName().java)
+                        .isEqualTo(JArrayTypeName.of(classJTypeName))
                 }
                 assertThat(annotationValue.hasTypeListValue()).isTrue()
                 // Check the list of values
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
index d37d558..0f5ca1c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
@@ -19,7 +19,8 @@
 import androidx.kruth.assertThat
 import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.JArrayTypeName
-import androidx.room.compiler.processing.compat.XConverters.toKS
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import androidx.room.compiler.processing.ksp.createTypeReference
 import androidx.room.compiler.processing.util.Source
@@ -55,25 +56,21 @@
                 .getField("param")
                 .type
             assertThat(type.isArray()).isTrue()
-            assertThat(type.asTypeName().java).isEqualTo(
-                JArrayTypeName.of(String::class.java)
+            val arrComponentTypeName = if (
+                invocation.isKsp &&
+                (invocation.processingEnv as KspProcessingEnv).isKsp2
+            ) {
+                XTypeName.getProducerExtendsName(String::class.asClassName())
+            } else {
+                String::class.asClassName()
+            }.copy(nullable = true)
+            assertThat(type.asTypeName()).isEqualTo(
+                XTypeName.getArrayName(arrComponentTypeName).copy(nullable = true)
             )
-            if (invocation.isKsp) {
-                assertThat(type.asTypeName().kotlin).isEqualTo(
-                    if (invocation.processingEnv.toKS().kspVersion >= KotlinVersion(2, 0)) {
-                        com.squareup.kotlinpoet.ARRAY.parameterizedBy(
-                            KWildcardTypeName.producerOf(String::class.asKTypeName()))
-                    } else {
-                        com.squareup.kotlinpoet.ARRAY.parameterizedBy(String::class.asKTypeName())
-                    }
-                )
-            }
             check(type.isArray())
             type.componentType.let { component ->
-                assertThat(component.asTypeName().java).isEqualTo(String::class.asJTypeName())
-                if (invocation.isKsp) {
-                    assertThat(component.asTypeName().kotlin).isEqualTo(String::class.asKTypeName())
-                }
+                assertThat(component.asTypeName())
+                    .isEqualTo(String::class.asClassName().copy(nullable = true))
                 assertThat(component.nullability).isEqualTo(XNullability.UNKNOWN)
             }
         }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index 9a83501..38933b7 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -266,7 +266,7 @@
             )
             validateMethodTypeAsMemberOf(
                 element = it.processingEnv.requireTypeElement("foo.bar.Child"),
-                tTypeName = String::class.asClassName(),
+                tTypeName = String::class.asClassName().copy(nullable = true),
                 rTypeName = XTypeName.getTypeVariableName("R", listOf(
                     XTypeName.ANY_OBJECT.copy(nullable = true)))
             )
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index 77161da..55020b7 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -20,7 +20,7 @@
 import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
-import androidx.room.compiler.processing.compat.XConverters.toKS
+import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.UNIT_JCLASS_NAME
@@ -89,10 +89,11 @@
                     val paramType = param.type
                     check(paramType.isArray())
                     assertThat(paramType.componentType.asTypeName())
-                        .isEqualTo(String::class.asClassName())
+                        .isEqualTo(String::class.asClassName().copy(nullable = true))
                     assertThat(param.enclosingElement).isEqualTo(method)
                 }
-                assertThat(method.returnType.asTypeName()).isEqualTo(String::class.asClassName())
+                assertThat(method.returnType.asTypeName())
+                    .isEqualTo(String::class.asClassName().copy(nullable = true))
             }
             element.getConstructors().single().let { ctor ->
                 assertThat(ctor.parameters).hasSize(1)
@@ -120,12 +121,16 @@
             element.getMethodByJvmName("method").let { method ->
                 assertThat(method.isVarArgs()).isTrue()
                 assertThat(method.parameters.single().type.asTypeName()).isEqualTo(
-                    XTypeName.getArrayName(String::class.asClassName()))
+                    XTypeName.getArrayName(
+                        String::class.asClassName().copy(nullable = true)
+                    ).copy(nullable = true)
+                )
             }
             element.getMethodByJvmName("methodPrimitive").let { method ->
                 assertThat(method.isVarArgs()).isTrue()
                 assertThat(method.parameters.single().type.asTypeName()).isEqualTo(
-                    XTypeName.getArrayName(XTypeName.PRIMITIVE_INT))
+                    XTypeName.getArrayName(XTypeName.PRIMITIVE_INT).copy(nullable = true)
+                )
             }
         }
     }
@@ -808,13 +813,13 @@
             val elm = invocation.processingEnv.requireTypeElement("JavaImpl")
             assertThat(
                 elm.getMethodByJvmName("getX").returnType.asTypeName()
-            ).isEqualTo(Int::class.asClassName())
+            ).isEqualTo(Int::class.asClassName().copy(nullable = true))
             assertThat(
                 elm.getMethodByJvmName("getY").returnType.asTypeName()
-            ).isEqualTo(Int::class.asClassName())
+            ).isEqualTo(Int::class.asClassName().copy(nullable = true))
             assertThat(
                 elm.getMethodByJvmName("setY").parameters.first().type.asTypeName()
-            ).isEqualTo(Int::class.asClassName())
+            ).isEqualTo(Int::class.asClassName().copy(nullable = true))
         }
     }
 
@@ -1477,9 +1482,7 @@
                             assertThat(parameterName).isEqualTo("param1")
                         } else {
                             if (it.isKsp) {
-                                if (hasDebugFlag &&
-                                        it.processingEnv.toKS().kspVersion >=
-                                        KotlinVersion(2, 0)) {
+                                if (hasDebugFlag && (it.processingEnv as KspProcessingEnv).isKsp2) {
                                     if (isAbstract || isJavaNative) {
                                         assertThat(parameterName).isEqualTo("p0")
                                     } else {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index 165deab..9e873e7 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -29,7 +29,6 @@
 import com.squareup.kotlinpoet.INT
 import com.squareup.kotlinpoet.UNIT
 import com.squareup.kotlinpoet.javapoet.JTypeName
-import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -320,15 +319,7 @@
                     assertThat(it.nullability).isEqualTo(NULLABLE)
                 }
                 if (invocation.isKsp) {
-                    assertThat(typeArg.asTypeName().kotlin).isEqualTo(
-                        when (it) {
-                            "KotlinClass" -> INT.copy(nullable = true)
-                            // A type arg from Java has unknown nullability,
-                            // so name defaults to not-null
-                            "JavaClass" -> INT
-                            else -> fail("Unknown src $it")
-                        }
-                    )
+                    assertThat(typeArg.asTypeName().kotlin).isEqualTo(INT.copy(nullable = true))
 
                     typeArg.makeNonNullable().let {
                         assertThat(it.asTypeName().kotlin).isEqualTo(INT)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index f1dfb5f..84d6cd4 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -21,8 +21,8 @@
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
-import androidx.room.compiler.processing.compat.XConverters.toKS
 import androidx.room.compiler.processing.javac.JavacType
+import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.asKClassName
@@ -1889,9 +1889,7 @@
                 // TODO(kuanyingchou): https://github.com/google/ksp/issues/1761
                 val parent = typeElement.superClass!!.typeElement!!
                 if (qName == "test.KotlinEnum" && !isPreCompiled && invocation.isKsp) {
-                    if (invocation.isKsp &&
-                            invocation.processingEnv.toKS().kspVersion >=
-                            KotlinVersion(2, 0)) {
+                    if (invocation.isKsp && (invocation.processingEnv as KspProcessingEnv).isKsp2) {
                         assertThat(parent.asClassName()).isEqualTo(XTypeName.ENUM)
                     } else {
                         assertThat(parent.asClassName()).isEqualTo(Any::class.asClassName())
@@ -1903,17 +1901,19 @@
                 val methodNames = typeElement.getDeclaredMethods().map { it.jvmName }
                 if (qName == "test.KotlinEnum") {
                     if (invocation.isKsp) {
-                        if (!isPreCompiled && invocation.processingEnv.toKS().kspVersion <
-                                KotlinVersion(2, 0)) {
-                            assertThat(methodNames).containsExactly(
-                                "enumMethod",
-                            )
-                        } else {
+                        if (
+                            isPreCompiled ||
+                            (invocation.processingEnv as KspProcessingEnv).isKsp2
+                        ) {
                             assertThat(methodNames).containsExactly(
                                 "enumMethod",
                                 "values",
                                 "valueOf",
                             )
+                        } else {
+                            assertThat(methodNames).containsExactly(
+                                "enumMethod",
+                            )
                         }
                     } else {
                         assertThat(methodNames).containsExactly(
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 30422ec..7662e9f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -19,6 +19,7 @@
 import androidx.kruth.assertThat
 import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeName.Companion.ANY_OBJECT
 import androidx.room.compiler.codegen.XTypeName.Companion.UNAVAILABLE_KTYPE_NAME
 import androidx.room.compiler.processing.ksp.ERROR_JTYPE_NAME
@@ -144,7 +145,7 @@
                 )
                 if (it.isKsp) {
                     assertThat(wildcardParam.type.asTypeName().kotlin).isEqualTo(
-                        MUTABLE_SET.parameterizedBy(STAR)
+                        MUTABLE_SET.parameterizedBy(STAR).copy(nullable = true)
                     )
                     assertThat(extendsBoundOrSelf.rawType)
                         .isEqualTo(
@@ -165,7 +166,7 @@
                 )
                 if (it.isKsp) {
                     assertThat(rawParamType.type.asTypeName().kotlin).isEqualTo(
-                        KClassName("kotlin.collections", "MutableSet")
+                        KClassName("kotlin.collections", "MutableSet").copy(nullable = true)
                     )
                 }
             }
@@ -180,8 +181,8 @@
                 if (it.isKsp) {
                     assertThat(rawParamTypeArgument.type.asTypeName().kotlin).isEqualTo(
                         KClassName("kotlin.collections", "MutableList").parameterizedBy(
-                            KClassName("kotlin.collections", "MutableSet")
-                        )
+                            KClassName("kotlin.collections", "MutableSet").copy(nullable = true)
+                        ).copy(nullable = true)
                     )
                 }
                 val rawTypeArgument = rawParamTypeArgument.type.typeArguments.single()
@@ -190,7 +191,7 @@
                 )
                 if (it.isKsp) {
                     assertThat(rawTypeArgument.asTypeName().kotlin).isEqualTo(
-                        KClassName("kotlin.collections", "MutableSet")
+                        KClassName("kotlin.collections", "MutableSet").copy(nullable = true)
                     )
                 }
             }
@@ -628,22 +629,19 @@
                 val expectedTypeStringDumpKotlin = """
                 SelfReferencing<T>
                 | T
-                | > SelfReferencing<T>?
-                | > | T
-                | > | > SelfReferencing<T>?
-                | > | > | T
+                | > SelfReferencing<T?>?
+                | > | T?
+                | > | > SelfReferencing<T?>?
+                | > | > | T?
                 """.trimIndent()
                 assertThat(typeElement.type.asTypeName().kotlin.dumpToString(5))
                     .isEqualTo(expectedTypeStringDumpKotlin)
             }
-            val expectedParamStringDump = """
-                SelfReferencing
-                """.trimIndent()
             assertThat(parameter.type.asTypeName().java.dumpToString(5))
-                .isEqualTo(expectedParamStringDump)
+                .isEqualTo("SelfReferencing")
             if (invocation.isKsp) {
                 assertThat(parameter.type.asTypeName().kotlin.dumpToString(5))
-                    .isEqualTo(expectedParamStringDump)
+                    .isEqualTo("SelfReferencing?")
             }
         }
     }
@@ -1381,38 +1379,48 @@
 
     @Test
     fun getWildcardType() {
-        fun XTestInvocation.checkType() {
+        fun XTestInvocation.checkType(isJavaSrc: Boolean) {
             val usageElement = processingEnv.requireTypeElement("test.Usage")
             val fooElement = processingEnv.requireTypeElement("test.Foo")
-            val barType = processingEnv.requireType("test.Bar")
+            val barType = processingEnv.requireType("test.Bar").run {
+                if (isJavaSrc) makeNullable() else makeNonNullable()
+            }
 
             // Test a manually constructed Foo<Bar>
-            val fooBarType = processingEnv.getDeclaredType(fooElement, barType)
+            val fooBarType = processingEnv.getDeclaredType(fooElement, barType).run {
+                if (isJavaSrc) makeNullable() else makeNonNullable()
+            }
             val fooBarUsageType = usageElement.getDeclaredField("fooBar").type
-            assertThat(fooBarType.asTypeName()).isEqualTo(fooBarUsageType.asTypeName())
+            assertThat(fooBarUsageType.asTypeName()).isEqualTo(fooBarType.asTypeName())
 
             // Test a manually constructed Foo<? extends Bar>
             val fooExtendsBarType = processingEnv.getDeclaredType(
                 fooElement,
                 processingEnv.getWildcardType(producerExtends = barType)
-            )
+            ).run {
+                if (isJavaSrc) makeNullable() else makeNonNullable()
+            }
             val fooExtendsBarUsageType = usageElement.getDeclaredField("fooExtendsBar").type
-            assertThat(fooExtendsBarType.asTypeName())
-                .isEqualTo(fooExtendsBarUsageType.asTypeName())
+            assertThat(fooExtendsBarUsageType.asTypeName())
+                .isEqualTo(fooExtendsBarType.asTypeName())
 
             // Test a manually constructed Foo<? super Bar>
             val fooSuperBarType = processingEnv.getDeclaredType(
                 fooElement,
                 processingEnv.getWildcardType(consumerSuper = barType)
-            )
+            ).run {
+                if (isJavaSrc) makeNullable() else makeNonNullable()
+            }
             val fooSuperBarUsageType = usageElement.getDeclaredField("fooSuperBar").type
-            assertThat(fooSuperBarType.asTypeName()).isEqualTo(fooSuperBarUsageType.asTypeName())
+            assertThat(fooSuperBarUsageType.asTypeName()).isEqualTo(fooSuperBarType.asTypeName())
 
             // Test a manually constructed Foo<?>
             val fooUnboundedType = processingEnv.getDeclaredType(
                 fooElement,
                 processingEnv.getWildcardType()
-            )
+            ).run {
+                if (isJavaSrc) makeNullable() else makeNonNullable()
+            }
             val fooUnboundedUsageType = usageElement.getDeclaredField("fooUnbounded").type
             assertThat(fooUnboundedType.asTypeName()).isEqualTo(fooUnboundedUsageType.asTypeName())
         }
@@ -1430,10 +1438,10 @@
             interface Foo<T> {}
             interface Bar {}
             """.trimIndent()
-        ))) { it.checkType() }
+        ))) { it.checkType(isJavaSrc = true) }
 
         runProcessorTest(listOf(Source.kotlin(
-            "test.Usage.kt",
+            "Usage.kt",
             """
             package test
             class Usage {
@@ -1445,7 +1453,7 @@
             interface Foo<T>
             interface Bar
             """.trimIndent()
-        ))) { it.checkType() }
+        ))) { it.checkType(isJavaSrc = false) }
     }
 
     @Test
@@ -1599,45 +1607,87 @@
             """.trimIndent()
         )
         runProcessorTest(sources = listOf(src)) { invocation ->
-            fun assertHasTypeName(type: XType, expectedTypeName: String) {
-                assertThat(type.asTypeName().java.toString()).isEqualTo(expectedTypeName)
-                if (invocation.isKsp) {
-                    assertThat(type.asTypeName().kotlin.toString()).isEqualTo(expectedTypeName)
-                }
+            val fooTypeName = XClassName.get("test", "Foo")
+            val barTypeName = XClassName.get("test", "Bar")
+
+            fun assertHasTypeName(type: XType, expectedTypeName: XTypeName) {
+                assertThat(type.asTypeName()).isEqualTo(expectedTypeName)
             }
 
             val subject = invocation.processingEnv.requireTypeElement("test.Subject")
-            assertHasTypeName(subject.getDeclaredField("foo").type, "test.Foo")
-            assertHasTypeName(subject.getDeclaredField("fooFoo").type, "test.Foo<test.Foo>")
             assertHasTypeName(
-                subject.getDeclaredField("fooFooFoo").type, "test.Foo<test.Foo<test.Foo>>")
+                type = subject.getDeclaredField("foo").type,
+                expectedTypeName = fooTypeName.copy(nullable = true)
+            )
             assertHasTypeName(
-                subject.getDeclaredField("barFooFoo").type, "test.Bar<test.Foo, test.Foo>")
+                type = subject.getDeclaredField("fooFoo").type,
+                expectedTypeName = fooTypeName.parametrizedBy(
+                    fooTypeName.copy(nullable = true)
+                ).copy(nullable = true)
+            )
+            assertHasTypeName(
+                type = subject.getDeclaredField("fooFooFoo").type,
+                expectedTypeName = fooTypeName.parametrizedBy(
+                    fooTypeName.parametrizedBy(
+                        fooTypeName.copy(nullable = true)
+                    ).copy(nullable = true)
+                ).copy(nullable = true)
+            )
+            assertHasTypeName(
+                type = subject.getDeclaredField("barFooFoo").type,
+                expectedTypeName = barTypeName.parametrizedBy(
+                    fooTypeName.copy(nullable = true), fooTypeName.copy(nullable = true)
+                ).copy(nullable = true)
+            )
 
             // Test manually wrapping raw type using XProcessingEnv#getDeclaredType()
             subject.getDeclaredField("foo").type.let { foo ->
                 val fooTypeElement = invocation.processingEnv.requireTypeElement("test.Foo")
                 val fooFoo: XType = invocation.processingEnv.getDeclaredType(fooTypeElement, foo)
-                assertHasTypeName(fooFoo, "test.Foo<test.Foo>")
+                assertHasTypeName(
+                    type = fooFoo,
+                    expectedTypeName = fooTypeName.parametrizedBy(
+                        fooTypeName.copy(nullable = true)
+                    )
+                )
 
                 val fooFooFoo: XType =
                     invocation.processingEnv.getDeclaredType(fooTypeElement, fooFoo)
-                assertHasTypeName(fooFooFoo, "test.Foo<test.Foo<test.Foo>>")
+                assertHasTypeName(
+                    type = fooFooFoo,
+                    expectedTypeName = fooTypeName.parametrizedBy(
+                        fooTypeName.parametrizedBy(fooTypeName.copy(nullable = true))
+                    )
+                )
 
                 val barTypeElement = invocation.processingEnv.requireTypeElement("test.Bar")
                 val barFooFoo: XType =
                     invocation.processingEnv.getDeclaredType(barTypeElement, foo, foo)
-                assertHasTypeName(barFooFoo, "test.Bar<test.Foo, test.Foo>")
+                assertHasTypeName(
+                    type = barFooFoo,
+                    expectedTypeName = barTypeName.parametrizedBy(
+                        fooTypeName.copy(nullable = true), fooTypeName.copy(nullable = true)
+                    )
+                )
             }
 
             // Test manually unwrapping a type with a raw type argument:
             subject.getDeclaredField("fooFoo").type.let { fooFoo ->
-                assertHasTypeName(fooFoo.typeArguments.single(), "test.Foo")
+                assertHasTypeName(
+                    type = fooFoo.typeArguments.single(),
+                    expectedTypeName = fooTypeName.copy(nullable = true)
+                )
             }
             subject.getDeclaredField("barFooFoo").type.let { barFooFoo ->
                 assertThat(barFooFoo.typeArguments).hasSize(2)
-                assertHasTypeName(barFooFoo.typeArguments[0], "test.Foo")
-                assertHasTypeName(barFooFoo.typeArguments[1], "test.Foo")
+                assertHasTypeName(
+                    type = barFooFoo.typeArguments[0],
+                    expectedTypeName = fooTypeName.copy(nullable = true)
+                )
+                assertHasTypeName(
+                    type = barFooFoo.typeArguments[1],
+                    expectedTypeName = fooTypeName.copy(nullable = true)
+                )
             }
         }
     }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
index 0bb58a2..84acf17 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
@@ -605,13 +605,11 @@
                     JClassName.get("java.lang", "Number")
                 )
                 if (invocation.isKsp) {
+                    val numberKClassName = KClassName("kotlin", "Number").copy(nullable = true)
                     assertThat(arg1.asTypeName().kotlin)
-                        .isEqualTo(
-                            KWildcardTypeName.producerOf(Number::class)
-                        )
-                    assertThat(arg1.extendsBound()?.asTypeName()?.kotlin).isEqualTo(
-                        KClassName("kotlin", "Number")
-                    )
+                        .isEqualTo(KWildcardTypeName.producerOf(numberKClassName))
+                    assertThat(arg1.extendsBound()?.asTypeName()?.kotlin)
+                        .isEqualTo(numberKClassName)
                 }
                 assertThat(
                     arg1.extendsBound()?.extendsBound()