Merge "Fix Java TypeName for @JvmInline types" into androidx-main
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt
index 1aaaee0..6bcc71f 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeJavaPoetExt.kt
@@ -21,6 +21,7 @@
import androidx.room.compiler.processing.tryBox
import androidx.room.compiler.processing.util.ISSUE_TRACKER_LINK
import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.isAnnotationPresent
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSName
@@ -29,6 +30,7 @@
import com.google.devtools.ksp.symbol.KSTypeArgument
import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.symbol.Variance
import com.squareup.kotlinpoet.javapoet.JClassName
import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName
@@ -45,7 +47,10 @@
* To handle self referencing types and avoid infinite recursion, we keep a lookup map for
* TypeVariables.
*/
-private typealias JTypeArgumentTypeLookup = LinkedHashMap<KSName, JTypeName>
+private class TypeResolutionContext(
+ val originalType: KSType? = null,
+ val typeArgumentTypeLookup: MutableMap<KSName, JTypeName> = LinkedHashMap(),
+)
/**
* Turns a KSTypeReference into a TypeName in java's type system.
@@ -53,17 +58,17 @@
internal fun KSTypeReference?.asJTypeName(resolver: Resolver): JTypeName =
asJTypeName(
resolver = resolver,
- typeArgumentTypeLookup = JTypeArgumentTypeLookup()
+ typeResolutionContext = TypeResolutionContext()
)
private fun KSTypeReference?.asJTypeName(
resolver: Resolver,
- typeArgumentTypeLookup: JTypeArgumentTypeLookup
+ typeResolutionContext: TypeResolutionContext
): JTypeName {
return if (this == null) {
ERROR_JTYPE_NAME
} else {
- resolve().asJTypeName(resolver, typeArgumentTypeLookup)
+ resolve().asJTypeName(resolver, typeResolutionContext)
}
}
@@ -73,27 +78,37 @@
internal fun KSDeclaration.asJTypeName(resolver: Resolver): JTypeName =
asJTypeName(
resolver = resolver,
- typeArgumentTypeLookup = JTypeArgumentTypeLookup()
+ typeResolutionContext = TypeResolutionContext()
)
@OptIn(KspExperimental::class)
private fun KSDeclaration.asJTypeName(
resolver: Resolver,
- typeArgumentTypeLookup: JTypeArgumentTypeLookup
+ typeResolutionContext: TypeResolutionContext
): JTypeName {
if (this is KSTypeAlias) {
- return this.type.asJTypeName(resolver, typeArgumentTypeLookup)
+ return this.type.asJTypeName(resolver, typeResolutionContext)
}
if (this is KSTypeParameter) {
- return this.asJTypeName(resolver, typeArgumentTypeLookup)
+ return this.asJTypeName(resolver, typeResolutionContext)
}
// if there is no qualified name, it is a resolution error so just return shared instance
// KSP may improve that later and if not, we can improve it in Room
// TODO: https://issuetracker.google.com/issues/168639183
val qualified = qualifiedName?.asString() ?: return ERROR_JTYPE_NAME
- val jvmSignature = resolver.mapToJvmSignature(this)
- if (jvmSignature != null && jvmSignature.isNotBlank()) {
- return jvmSignature.typeNameFromJvmSignature()
+
+ // Note: To match KAPT behavior, a type annotated with @JvmInline is only replaced with the
+ // underlying type if the inline type is used directly (e.g. MyInlineType) rather than in the
+ // type args of another type, (e.g. List<MyInlineType>).
+ val isInline = isAnnotationPresent(JvmInline::class) || modifiers.contains(Modifier.INLINE)
+ val isOriginalType =
+ typeResolutionContext.originalType?.declaration?.qualifiedName?.asString() == qualified
+ if (!isInline || isOriginalType) {
+ resolver.mapToJvmSignature(this).let { jvmSignature ->
+ if (!jvmSignature.isNullOrBlank()) {
+ return jvmSignature.typeNameFromJvmSignature()
+ }
+ }
}
// fallback to custom generation, it is very likely that this is an unresolved type
@@ -112,40 +127,38 @@
/**
* Turns a KSTypeArgument into a TypeName in java's type system.
*/
-internal fun KSTypeArgument.asJTypeName(
- resolver: Resolver
-): JTypeName = asJTypeName(
+internal fun KSTypeArgument.asJTypeName(resolver: Resolver): JTypeName = asJTypeName(
resolver = resolver,
- typeArgumentTypeLookup = JTypeArgumentTypeLookup()
+ typeResolutionContext = TypeResolutionContext()
)
private fun KSTypeParameter.asJTypeName(
resolver: Resolver,
- typeArgumentTypeLookup: JTypeArgumentTypeLookup
+ typeResolutionContext: TypeResolutionContext
): JTypeName {
// see https://github.com/square/javapoet/issues/842
- typeArgumentTypeLookup[name]?.let {
+ typeResolutionContext.typeArgumentTypeLookup[name]?.let {
return it
}
val mutableBounds = mutableListOf<JTypeName>()
val typeName = createModifiableTypeVariableName(name = name.asString(), bounds = mutableBounds)
- typeArgumentTypeLookup[name] = typeName
+ typeResolutionContext.typeArgumentTypeLookup[name] = typeName
val resolvedBounds = bounds.map {
- it.asJTypeName(resolver, typeArgumentTypeLookup).tryBox()
+ it.asJTypeName(resolver, typeResolutionContext).tryBox()
}.toList()
if (resolvedBounds.isNotEmpty()) {
mutableBounds.addAll(resolvedBounds)
mutableBounds.remove(JTypeName.OBJECT)
}
- typeArgumentTypeLookup.remove(name)
+ typeResolutionContext.typeArgumentTypeLookup.remove(name)
return typeName
}
private fun KSTypeArgument.asJTypeName(
resolver: Resolver,
- typeArgumentTypeLookup: JTypeArgumentTypeLookup
+ typeResolutionContext: TypeResolutionContext
): JTypeName {
- fun resolveTypeName() = type.asJTypeName(resolver, typeArgumentTypeLookup).tryBox()
+ fun resolveTypeName() = type.asJTypeName(resolver, typeResolutionContext).tryBox()
return when (variance) {
Variance.CONTRAVARIANT -> JWildcardTypeName.supertypeOf(resolveTypeName())
Variance.COVARIANT -> JWildcardTypeName.subtypeOf(resolveTypeName())
@@ -160,29 +173,29 @@
internal fun KSType.asJTypeName(resolver: Resolver): JTypeName =
asJTypeName(
resolver = resolver,
- typeArgumentTypeLookup = JTypeArgumentTypeLookup()
+ typeResolutionContext = TypeResolutionContext(this)
)
@OptIn(KspExperimental::class)
private fun KSType.asJTypeName(
resolver: Resolver,
- typeArgumentTypeLookup: JTypeArgumentTypeLookup
+ typeResolutionContext: TypeResolutionContext,
): JTypeName {
return if (declaration is KSTypeAlias) {
- replaceTypeAliases(resolver).asJTypeName(resolver, typeArgumentTypeLookup)
+ replaceTypeAliases(resolver).asJTypeName(resolver, typeResolutionContext)
} else if (this.arguments.isNotEmpty() && !resolver.isJavaRawType(this)) {
val args: Array<JTypeName> = this.arguments
- .map { typeArg -> typeArg.asJTypeName(resolver, typeArgumentTypeLookup) }
+ .map { typeArg -> typeArg.asJTypeName(resolver, typeResolutionContext) }
.map { it.tryBox() }
.toTypedArray()
- when (val typeName = declaration.asJTypeName(resolver, typeArgumentTypeLookup).tryBox()) {
+ when (val typeName = declaration.asJTypeName(resolver, typeResolutionContext).tryBox()) {
is JArrayTypeName -> JArrayTypeName.of(args.single())
is JClassName -> JParameterizedTypeName.get(typeName, *args)
else -> error("Unexpected type name for KSType: $typeName")
}
} else {
- this.declaration.asJTypeName(resolver, typeArgumentTypeLookup)
+ this.declaration.asJTypeName(resolver, typeResolutionContext)
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspClassFileUtility.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspClassFileUtility.kt
deleted file mode 100644
index f01c0fd..0000000
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspClassFileUtility.kt
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.compiler.processing.ksp
-
-import androidx.room.compiler.processing.XMethodElement
-import androidx.room.compiler.processing.XProcessingConfig
-import com.google.devtools.ksp.symbol.ClassKind
-import com.google.devtools.ksp.symbol.KSClassDeclaration
-import com.google.devtools.ksp.symbol.KSFunctionDeclaration
-import com.google.devtools.ksp.symbol.KSNode
-import com.google.devtools.ksp.symbol.KSPropertyDeclaration
-import com.google.devtools.ksp.symbol.Origin
-import java.lang.reflect.InvocationHandler
-import java.lang.reflect.Method
-import java.lang.reflect.Proxy
-
-/**
- * When a compiled kotlin class is loaded from a `.class` file, its fields are not ordered in the
- * same way they are declared in code.
- * This particularly hurts Room where we generate the table structure in that order.
- *
- * This class implements a port of https://github.com/google/ksp/pull/260 via reflection until KSP
- * (or kotlin compiler) fixes the problem. As this uses reflection, it is fail safe such that if it
- * cannot find the correct order, it will just return in the order KSP returned instead of crashing.
- *
- * KSP Bugs:
- * * https://github.com/google/ksp/issues/250
- */
-internal object KspClassFileUtility {
- /**
- * Sorts the given fields in the order they are declared in the backing class declaration.
- */
- fun orderFields(
- owner: KSClassDeclaration,
- fields: List<KspFieldElement>
- ): List<KspFieldElement> {
- // no reason to try to load .class if we don't have any fields to sort
- if (fields.isEmpty()) return fields
- if (owner.origin == Origin.KOTLIN) {
- // this is simple and temporary case that KSP can fix. No reason to create a more
- // complicated comparator for it since it will be removed
- // https://github.com/google/ksp/issues/727
- return orderKotlinSourceFields(owner, fields)
- }
- val comparator = getNamesComparator(owner, Type.FIELD, KspFieldElement::name)
- return if (comparator == null) {
- fields
- } else {
- fields.forEach {
- // make sure each name gets registered so that if we didn't find it in .class for
- // whatever reason, we keep the order given from KSP.
- comparator.register(it.name)
- }
- fields.sortedWith(comparator)
- }
- }
-
- /**
- * KSP returns properties from primary constructor last instead of first.
- * https://github.com/google/ksp/issues/727
- * This function reverts that. This method traverses declaration hierarchy instead of looking at
- * the primary constructor's fields as some of them may not be fields.
- */
- private fun orderKotlinSourceFields(
- owner: KSClassDeclaration,
- fields: List<KspFieldElement>
- ): List<KspFieldElement> {
- val primaryConstructor = owner.primaryConstructor ?: return fields
- return fields.sortedBy {
- if (it.declaration.isDeclaredInside(primaryConstructor)) {
- 0
- } else {
- 1
- }
- }
- }
-
- private fun KSPropertyDeclaration.isDeclaredInside(
- functionDeclaration: KSFunctionDeclaration
- ): Boolean {
- var current: KSNode? = this
- while (current != null) {
- if (current == functionDeclaration) {
- return true
- }
- current = current.parent
- }
- return false
- }
-
- /**
- * Sorts the given methods in the order they are declared in the backing class declaration.
- */
- fun orderMethods(
- owner: KSClassDeclaration,
- methods: List<KspMethodElement>
- ): List<KspMethodElement> {
- // no reason to try to load .class if we don't have any fields to sort
- if (methods.isEmpty()) return methods
- val comparator = getNamesComparator(owner, Type.METHOD, XMethodElement::jvmDescriptor)
- return if (comparator == null) {
- methods
- } else {
- methods.forEach {
- // make sure each descriptor gets registered so that if we didn't find it in .class
- // for whatever reason, we keep the order given from KSP.
- comparator.register(it.jvmDescriptor())
- }
- methods.sortedWith(comparator)
- }
- }
-
- /**
- * Builds a field names comparator from the given class declaration if and only if its origin
- * is Kotlin .class.
- * If it fails to find the order, returns null.
- */
- @Suppress("BanUncheckedReflection")
- private fun <T> getNamesComparator(
- ksClassDeclaration: KSClassDeclaration,
- type: Type,
- getName: T.() -> String,
- ): MemberNameComparator<T>? {
- return try {
- // this is needed only for compiled kotlin classes
- // https://github.com/google/ksp/issues/250#issuecomment-761108924
- if (ksClassDeclaration.origin != Origin.KOTLIN_LIB) return null
-
- // A companion object's `declarations` contains fields declared in the object in KSP,
- // while KotlinJvmBinaryClass.visitMembers() does not. This leads to unsorted fields.
- // As a workaround we register all the fields of an enclosing type in hope that we
- // cover every field in a companion object's KSDeclarationContainer.declarations.
- val classDeclaration = if (
- ksClassDeclaration.isCompanionObject &&
- type == Type.FIELD &&
- ksClassDeclaration.parentDeclaration is KSClassDeclaration) {
- ksClassDeclaration.parentDeclaration as KSClassDeclaration
- } else {
- ksClassDeclaration
- }
-
- val typeReferences = ReflectionReferences.getInstance(classDeclaration) ?: return null
- val binarySource = getBinarySource(typeReferences, classDeclaration) ?: return null
-
- val memberNameComparator = MemberNameComparator(
- getName = getName,
- // we can do strict mode only in classes. For Interfaces, private methods won't
- // show up in the binary.
- strictMode = XProcessingConfig.STRICT_MODE &&
- classDeclaration.classKind != ClassKind.INTERFACE
- )
- val invocationHandler = InvocationHandler { _, method, args ->
- if (method.name == type.visitorName) {
- val nameAsString = typeReferences.asStringMethod.invoke(args[0])
- if (nameAsString is String) {
- when (type) {
- Type.FIELD -> memberNameComparator.register(nameAsString)
- Type.METHOD -> {
- val methodTypeDescriptor = args[1]
- memberNameComparator.register(nameAsString + methodTypeDescriptor)
- }
- }
- }
- }
- null
- }
-
- val proxy = Proxy.newProxyInstance(
- classDeclaration.javaClass.classLoader,
- arrayOf(typeReferences.memberVisitor),
- invocationHandler
- )
-
- typeReferences.visitMembersMethod.invoke(binarySource, proxy, null)
- memberNameComparator.seal()
- memberNameComparator
- } catch (ignored: Throwable) {
- // this is best effort, if it failed, just ignore
- if (XProcessingConfig.STRICT_MODE) {
- throw RuntimeException("failed to get fields", ignored)
- }
- null
- }
- }
-
- private fun getBinarySource(
- typeReferences: ReflectionReferences,
- ksClassDeclaration: KSClassDeclaration
- ): Any? {
- val descriptor = typeReferences.getDescriptorMethod.invoke(ksClassDeclaration)
- ?: return null
- if (!typeReferences.deserializedClassDescriptor.isInstance(descriptor)) {
- return null
- }
- val descriptorSrc = typeReferences.descriptorSourceMethod.invoke(descriptor)
- ?: return null
- if (!typeReferences.kotlinJvmBinarySourceElement.isInstance(descriptorSrc)) {
- return null
- }
- return typeReferences.binaryClassMethod.invoke(descriptorSrc)
- ?: return null
- }
-
- /**
- * Holder object to keep references to class/method instances.
- */
- private class ReflectionReferences private constructor(
- classLoader: ClassLoader
- ) {
-
- val deserializedClassDescriptor: Class<*> = classLoader.loadClass(
- "org.jetbrains.kotlin.serialization.deserialization.descriptors" +
- ".DeserializedClassDescriptor"
- )
-
- val ksClassDeclarationDescriptorImpl: Class<*> = classLoader.loadClass(
- "com.google.devtools.ksp.symbol.impl.binary.KSClassDeclarationDescriptorImpl"
- )
- val kotlinJvmBinarySourceElement: Class<*> = classLoader.loadClass(
- "org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement"
- )
-
- val kotlinJvmBinaryClass: Class<*> = classLoader.loadClass(
- "org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass"
- )
-
- val memberVisitor: Class<*> = classLoader.loadClass(
- "org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass\$MemberVisitor"
- )
-
- val name: Class<*> = classLoader.loadClass(
- "org.jetbrains.kotlin.name.Name"
- )
-
- val getDescriptorMethod: Method = ksClassDeclarationDescriptorImpl
- .getDeclaredMethod("getDescriptor")
-
- val descriptorSourceMethod: Method = deserializedClassDescriptor.getMethod("getSource")
-
- val binaryClassMethod: Method = kotlinJvmBinarySourceElement.getMethod("getBinaryClass")
-
- val visitMembersMethod: Method = kotlinJvmBinaryClass.getDeclaredMethod(
- "visitMembers",
- memberVisitor, ByteArray::class.java
- )
-
- val asStringMethod: Method = name.getDeclaredMethod("asString")
-
- companion object {
- private val FAILED = Any()
- private var instance: Any? = null
-
- /**
- * Gets the cached instance or create a new one using the class loader of the given
- * [ref] parameter.
- */
- fun getInstance(ref: Any): ReflectionReferences? {
- if (instance == null) {
- instance = try {
- ReflectionReferences(ref::class.java.classLoader)
- } catch (ignored: Throwable) {
- FAILED
- }
- }
- return instance as? ReflectionReferences
- }
- }
- }
-
- private class MemberNameComparator<T>(
- val getName: T.() -> String,
- val strictMode: Boolean
- ) : Comparator<T> {
- private var nextOrder: Int = 0
- private var sealed: Boolean = false
- private val orders = mutableMapOf<String, Int>()
-
- /**
- * Called when fields are read to lock the ordering.
- * This is only relevant in tests as at runtime, we just do a best effort (add a new id
- * for it) and continue.
- */
- fun seal() {
- sealed = true
- }
-
- /**
- * Registers the name with the next order id
- */
- fun register(name: String) {
- getOrder(name)
- }
-
- /**
- * Gets the order of the name. If it was not seen before, adds it to the list, giving it a
- * new ID.
- */
- private fun getOrder(name: String) = orders.getOrPut(name) {
- if (sealed && strictMode) {
- error("expected to find field/method $name but it is non-existent: $orders")
- }
- nextOrder++
- }
-
- override fun compare(elm1: T, elm2: T): Int {
- return getOrder(elm1.getName()).compareTo(getOrder(elm2.getName()))
- }
- }
-
- /**
- * The type of declaration that we want to extract from class descriptor.
- */
- private enum class Type(
- val visitorName: String
- ) {
- FIELD("visitField"),
- METHOD("visitMethod")
- }
-}
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 8fb91f6..2318b4d 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
@@ -35,13 +35,14 @@
import androidx.room.compiler.processing.ksp.KspAnnotated.UseSiteFilter.Companion.NO_USE_SITE
import androidx.room.compiler.processing.tryBox
import androidx.room.compiler.processing.util.MemoizedSequence
+import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getConstructors
-import com.google.devtools.ksp.getDeclaredFunctions
-import com.google.devtools.ksp.getDeclaredProperties
import com.google.devtools.ksp.isConstructor
import com.google.devtools.ksp.isOpen
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSDeclarationContainer
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.Modifier
import com.squareup.javapoet.ClassName
@@ -162,34 +163,19 @@
* properties. They are not necessarily fields as it might include properties of interfaces.
*/
private val _declaredProperties by lazy {
- val declaredProperties = declaration.getDeclaredProperties()
+ buildList {
+ addAll(declaration.getDeclarationsInSourceOrder())
+ addAll(
+ declaration.findCompanionObject().getDeclarationsInSourceOrder()
+ .filter { it.isStatic() }
+ )
+ }.filterIsInstance(KSPropertyDeclaration::class.java)
.map {
KspFieldElement(
env = env,
- declaration = it,
- )
- }.let {
- // only order instance properties with backing fields, we don't care about the order
- // of companion properties or properties without backing fields.
- val (withBackingField, withoutBackingField) = it.partition {
- it.declaration.hasBackingFieldFixed
- }
- KspClassFileUtility.orderFields(declaration, withBackingField) + withoutBackingField
- }
-
- val companionProperties = declaration
- .findCompanionObject()
- ?.getDeclaredProperties()
- ?.filter {
- it.isStatic()
- }.orEmpty()
- .map {
- KspFieldElement(
- env = env,
- declaration = it,
+ declaration = it
)
}
- declaredProperties + companionProperties
}
private val _declaredFields by lazy {
@@ -280,29 +266,22 @@
}
private val _declaredMethods by lazy {
- val instanceMethods = declaration.getDeclaredFunctions().asSequence()
+ val declaredMethods = buildList {
+ addAll(declaration.getDeclarationsInSourceOrder())
+ addAll(
+ declaration.findCompanionObject().getDeclarationsInSourceOrder()
+ .filter { it.hasJvmStaticAnnotation() }
+ )
+ }.filterIsInstance(KSFunctionDeclaration::class.java)
.filterNot { it.isConstructor() }
- val companionMethods = declaration.findCompanionObject()
- ?.getDeclaredFunctions()
- ?.filterNot {
- it.isConstructor()
- }
- ?.filter {
- it.hasJvmStaticAnnotation()
- } ?: emptySequence()
- val declaredMethods = (instanceMethods + companionMethods)
- .filterNot {
- // filter out constructors
- it.simpleName.asString() == name
- }.map {
+ .map {
KspMethodElement.create(
env = env,
declaration = it
)
- }.toList()
+ }
.filterMethodsByConfig(env)
- KspClassFileUtility.orderMethods(declaration, declaredMethods) +
- syntheticGetterSetterMethods
+ declaredMethods + syntheticGetterSetterMethods
}
override fun getDeclaredMethods(): List<XMethodElement> {
@@ -319,14 +298,11 @@
}
override fun getSuperInterfaceElements(): List<XTypeElement> {
- return declaration.superTypes.asSequence().mapNotNull {
- it.resolve().declaration
- }.filterIsInstance<KSClassDeclaration>()
- .filter {
- it.classKind == ClassKind.INTERFACE
- }.mapTo(mutableListOf()) {
- env.wrapClassDeclaration(it)
- }
+ return declaration.superTypes
+ .mapNotNull { it.resolve().declaration }
+ .filterIsInstance<KSClassDeclaration>()
+ .filter { it.classKind == ClassKind.INTERFACE }
+ .mapTo(mutableListOf()) { env.wrapClassDeclaration(it) }
}
override fun getEnclosedTypeElements(): List<XTypeElement> {
@@ -362,6 +338,11 @@
private val KSPropertyDeclaration.hasBackingFieldFixed
get() = hasBackingField || modifiers.contains(Modifier.LATEINIT)
+ @OptIn(KspExperimental::class)
+ fun KSDeclarationContainer?.getDeclarationsInSourceOrder() = this?.let {
+ env.resolver.getDeclarationsInSourceOrder(it)
+ } ?: emptySequence()
+
companion object {
fun create(
env: KspProcessingEnv,
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
index b0c73fd..c793faf 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
@@ -184,6 +184,7 @@
typealias JSW = JvmSuppressWildcards
typealias JW = JvmWildcard
typealias MyLambdaTypeAlias = (@JvmWildcard MyType) -> @JvmWildcard MyType
+ @JvmInline value class MyInlineType(val value: MyType)
enum class MyEnum {
VAL1,
VAL2;
@@ -364,6 +365,10 @@
fun method52(
param: MyGenericIn<MyGeneric2ParametersAlias<MyGenericOut<MyType>, MyGenericIn<MyType>>>
): MyGenericIn<MyGeneric2ParametersAlias<MyGenericOut<MyType>, MyGenericIn<MyType>>> = TODO()
+ @JvmName("method53") // Needed to prevent obfuscation due to inline type.
+ fun method53(param: MyInlineType): MyInlineType = TODO()
+ fun method54(param: MyGenericOut<MyInlineType>): MyGenericOut<MyInlineType> = TODO()
+ fun method55(param: MyGenericIn<MyInlineType>): MyGenericIn<MyInlineType> = TODO()
}
""".trimIndent()
), listOf("Subject")