Merge "Add synthetic element in Ksp corresponds to @JvmStatic annotated companion method copied to enclosing class by Compiler in KAPT" into androidx-main
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
index 4cc1c25..3aa3852 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
@@ -159,12 +159,40 @@
}
private val _declaredMethods by lazy {
- ElementFilter.methodsIn(element.enclosedElements).map {
+ val companionObjectMethodDescriptors =
+ getEnclosedTypeElements()
+ .firstOrNull {
+ it.isCompanionObject()
+ }?.getDeclaredMethods()
+ ?.map { it.jvmDescriptor } ?: emptyList()
+
+ val declaredMethods =
+ ElementFilter.methodsIn(element.enclosedElements)
+ .map {
JavacMethodElement(
env = env,
element = it
)
}.filterMethodsByConfig(env)
+ if (companionObjectMethodDescriptors.isEmpty()) {
+ declaredMethods
+ } else {
+ buildList {
+ addAll(
+ declaredMethods.filterNot { method ->
+ companionObjectMethodDescriptors.any { it == method.jvmDescriptor }
+ }
+ )
+ companionObjectMethodDescriptors.forEach {
+ for (method in declaredMethods) {
+ if (method.jvmDescriptor == it) {
+ add(method)
+ break
+ }
+ }
+ }
+ }
+ }
}
override fun getDeclaredMethods(): List<JavacMethodElement> {
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 a840aa4..8aaabb3 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
@@ -110,7 +110,7 @@
)
}
is KSPropertySetter -> KspSyntheticPropertyMethodElement.create(
- env, parent
+ env, parent, isSyntheticStatic = false
).parameters.single()
else -> error(
"Don't know how to create a parameter element whose parent is a " +
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
index beab250..aec7c3c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
@@ -78,7 +78,8 @@
KspSyntheticPropertyMethodElement.create(
env = env,
field = this,
- accessor = accessor
+ accessor = accessor,
+ isSyntheticStatic = false
)
}.toList()
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
index 841154a..11719f1 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMethodElement.kt
@@ -31,7 +31,8 @@
internal sealed class KspMethodElement(
env: KspProcessingEnv,
- declaration: KSFunctionDeclaration
+ declaration: KSFunctionDeclaration,
+ val isSyntheticStatic: Boolean
) : KspExecutableElement(env, declaration), XMethodElement {
override val name: String
@@ -72,6 +73,18 @@
}
}
+ override val enclosingElement: KspMemberContainer by lazy {
+ if (isSyntheticStatic) {
+ actualEnclosingElement.declaration!!.requireEnclosingMemberContainer(env)
+ } else {
+ actualEnclosingElement
+ }
+ }
+
+ private val actualEnclosingElement: KspMemberContainer by lazy {
+ declaration.requireEnclosingMemberContainer(env)
+ }
+
override val executableType: XMethodType by lazy {
KspMethodType.create(
env = env,
@@ -116,8 +129,9 @@
private class KspNormalMethodElement(
env: KspProcessingEnv,
- declaration: KSFunctionDeclaration
- ) : KspMethodElement(env, declaration) {
+ declaration: KSFunctionDeclaration,
+ isSyntheticStatic: Boolean
+ ) : KspMethodElement(env, declaration, isSyntheticStatic) {
override val returnType: KspType by lazy {
declaration.returnKspType(
env = env,
@@ -134,8 +148,9 @@
private class KspSuspendMethodElement(
env: KspProcessingEnv,
- declaration: KSFunctionDeclaration
- ) : KspMethodElement(env, declaration) {
+ declaration: KSFunctionDeclaration,
+ isSyntheticStatic: Boolean
+ ) : KspMethodElement(env, declaration, isSyntheticStatic) {
override fun isSuspendFunction() = true
override val returnType: KspType by lazy {
@@ -160,12 +175,13 @@
companion object {
fun create(
env: KspProcessingEnv,
- declaration: KSFunctionDeclaration
+ declaration: KSFunctionDeclaration,
+ isSyntheticStatic: Boolean = false
): KspMethodElement {
return if (declaration.modifiers.contains(Modifier.SUSPEND)) {
- KspSuspendMethodElement(env, declaration)
+ KspSuspendMethodElement(env, declaration, isSyntheticStatic)
} else {
- KspNormalMethodElement(env, declaration)
+ KspNormalMethodElement(env, declaration, isSyntheticStatic)
}
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspRoundEnv.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspRoundEnv.kt
index f884671..094650c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspRoundEnv.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspRoundEnv.kt
@@ -44,30 +44,60 @@
if (annotationQualifiedName == "*") {
return emptySet()
}
- return env.resolver.getSymbolsWithAnnotation(annotationQualifiedName)
- .map { symbol ->
- when (symbol) {
- is KSPropertyDeclaration -> {
- KspFieldElement.create(env, symbol)
- }
- is KSClassDeclaration -> {
- when (symbol.classKind) {
- ClassKind.ENUM_ENTRY -> KspEnumEntry.create(env, symbol)
- else -> KspTypeElement.create(env, symbol)
+ return buildSet {
+ env.resolver.getSymbolsWithAnnotation(annotationQualifiedName)
+ .forEach { symbol ->
+ when (symbol) {
+ is KSPropertyDeclaration -> {
+ add(KspFieldElement.create(env, symbol))
}
+ is KSClassDeclaration -> {
+ when (symbol.classKind) {
+ ClassKind.ENUM_ENTRY ->
+ add(KspEnumEntry.create(env, symbol))
+ else -> add(KspTypeElement.create(env, symbol))
+ }
+ }
+ is KSFunctionDeclaration -> {
+ add(KspExecutableElement.create(env, symbol))
+ }
+ is KSPropertyAccessor -> {
+ if (symbol.receiver.isStatic() &&
+ symbol.receiver.parentDeclaration is KSClassDeclaration &&
+ (symbol.receiver.hasJvmStaticAnnotation() ||
+ symbol.hasJvmStaticAnnotation())) {
+ // Getter/setter can be copied from companion object to its
+ // outer class if the field is annotated with @JvmStatic.
+ add(
+ KspSyntheticPropertyMethodElement.create(
+ env, symbol, isSyntheticStatic = true
+ )
+ )
+ }
+ // static fields are the properties that are coming from the companion.
+ // Whether we'll generate method for it or not depends on the JVMStatic
+ // annotation
+ if (!symbol.receiver.isStatic() ||
+ symbol.receiver.hasJvmStaticAnnotation() ||
+ symbol.hasJvmStaticAnnotation() ||
+ symbol.receiver.parentDeclaration !is KSClassDeclaration
+ ) {
+ add(
+ KspSyntheticPropertyMethodElement.create(
+ env, symbol, isSyntheticStatic = false
+ )
+ )
+ }
+ }
+ is KSValueParameter -> {
+ add(KspExecutableParameterElement.create(env, symbol))
+ }
+ else ->
+ error("Unsupported $symbol with annotation $annotationQualifiedName")
}
- is KSFunctionDeclaration -> {
- KspExecutableElement.create(env, symbol)
- }
- is KSPropertyAccessor -> {
- KspSyntheticPropertyMethodElement.create(env, symbol)
- }
- is KSValueParameter -> {
- KspExecutableParameterElement.create(env, symbol)
- }
- else -> error("Unsupported $symbol with annotation $annotationQualifiedName")
}
- }.filter {
+ }
+ .filter {
// Due to the bug in https://github.com/google/ksp/issues/1198, KSP may incorrectly
// copy annotations from a constructor KSValueParameter to its KSPropertyDeclaration
// which we remove manually, so check here to make sure this is in sync with the
@@ -75,4 +105,4 @@
it.getAllAnnotations().any { it.qualifiedName == annotationQualifiedName }
}.toSet()
}
-}
\ No newline at end of file
+}
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 2318b4d..54e64ab 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
@@ -33,6 +33,7 @@
import androidx.room.compiler.processing.collectFieldsIncludingPrivateSupers
import androidx.room.compiler.processing.filterMethodsByConfig
import androidx.room.compiler.processing.ksp.KspAnnotated.UseSiteFilter.Companion.NO_USE_SITE
+import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
import androidx.room.compiler.processing.tryBox
import androidx.room.compiler.processing.util.MemoizedSequence
import com.google.devtools.ksp.KspExperimental
@@ -184,25 +185,27 @@
}
}
- private val syntheticGetterSetterMethods: List<XMethodElement> by lazy {
- if (declaration.isCompanionObject) {
- _declaredProperties.flatMap { field ->
- field.syntheticAccessors
- }
- } else {
- _declaredProperties.flatMap { field ->
- // static fields are the properties that are coming from the
- // companion. Whether we'll generate method for it or not depends on
- // the JVMStatic annotation
- if (field.isStatic() && !field.declaration.hasJvmStaticAnnotation()) {
- field.syntheticAccessors.filter {
- it.accessor.hasJvmStaticAnnotation()
- }
- } else {
- field.syntheticAccessors
- }
- }
- }.filterMethodsByConfig(env)
+ private fun syntheticGetterSetterMethods(field: KspFieldElement): List<XMethodElement> {
+ if (declaration.isCompanionObject) {
+ return field.syntheticAccessors
+ }
+ if (field.isStatic() && !field.declaration.hasJvmStaticAnnotation()) {
+ return field.syntheticAccessors.filter {
+ it.accessor.hasJvmStaticAnnotation()
+ }
+ }
+ if (field.isStatic() && field.declaration.hasJvmStaticAnnotation()) {
+ // Getter and setter are copied from companion object into current type
+ // element by Compiler in KAPT when @JvmStatic is present, in this case, the
+ // copied over method element should swap its enclosing element to be
+ // current type element instead of companion object.
+ return field.syntheticAccessors.map { element ->
+ KspSyntheticPropertyMethodElement.create(
+ env, field, element.accessor, isSyntheticStatic = true
+ )
+ }
+ }
+ return field.syntheticAccessors
}
override fun isNested(): Boolean {
@@ -266,22 +269,51 @@
}
private val _declaredMethods by lazy {
- val declaredMethods = buildList {
- addAll(declaration.getDeclarationsInSourceOrder())
- addAll(
- declaration.findCompanionObject().getDeclarationsInSourceOrder()
- .filter { it.hasJvmStaticAnnotation() }
- )
- }.filterIsInstance(KSFunctionDeclaration::class.java)
- .filterNot { it.isConstructor() }
- .map {
- KspMethodElement.create(
+ buildList {
+ declaration.getDeclarationsInSourceOrder()
+ .forEach {
+ if (it is KSFunctionDeclaration && !it.isConstructor()) {
+ add(
+ KspMethodElement.create(
env = env,
declaration = it
+ )
+ )
+ } else if (it is KSPropertyDeclaration) {
+ addAll(
+ syntheticGetterSetterMethods(
+ KspFieldElement(
+ env = env,
+ declaration = it
+ )
+ )
)
}
- .filterMethodsByConfig(env)
- declaredMethods + syntheticGetterSetterMethods
+ }
+ declaration.findCompanionObject().getDeclarationsInSourceOrder()
+ .forEach {
+ if (it.hasJvmStaticAnnotation() &&
+ it is KSFunctionDeclaration &&
+ !it.isConstructor()) {
+ add(
+ KspMethodElement.create(
+ env = env,
+ declaration = it,
+ isSyntheticStatic = true
+ )
+ )
+ } else if (it is KSPropertyDeclaration) {
+ addAll(
+ syntheticGetterSetterMethods(
+ KspFieldElement(
+ env = env,
+ declaration = it
+ )
+ )
+ )
+ }
+ }
+ }.filterMethodsByConfig(env)
}
override fun getDeclaredMethods(): List<XMethodElement> {
@@ -343,6 +375,13 @@
env.resolver.getDeclarationsInSourceOrder(it)
} ?: emptySequence()
+ @OptIn(KspExperimental::class)
+ fun KSDeclarationContainer?.getDeclaredMethods(): Sequence<KSFunctionDeclaration> {
+ return this.getDeclarationsInSourceOrder()
+ .filterIsInstance(KSFunctionDeclaration::class.java)
+ .filterNot { it.isConstructor() }
+ }
+
companion object {
fun create(
env: KspProcessingEnv,
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 2067b96..ec93a24 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
@@ -40,6 +40,7 @@
import androidx.room.compiler.processing.ksp.findEnclosingMemberContainer
import androidx.room.compiler.processing.ksp.jvmDescriptor
import androidx.room.compiler.processing.ksp.overrides
+import androidx.room.compiler.processing.ksp.requireEnclosingMemberContainer
import androidx.room.compiler.processing.util.sanitizeAsJavaParameterName
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.symbol.KSPropertyAccessor
@@ -57,6 +58,7 @@
internal sealed class KspSyntheticPropertyMethodElement(
val env: KspProcessingEnv,
val field: KspFieldElement,
+ val isSyntheticStatic: Boolean,
open val accessor: KSPropertyAccessor
) : XMethodElement,
XEquality,
@@ -71,7 +73,7 @@
}
override val equalityItems: Array<out Any?> by lazy {
- arrayOf(field, accessor)
+ arrayOf(field, accessor, isSyntheticStatic)
}
// NOTE: modifiers of the property are not necessarily my modifiers.
@@ -86,6 +88,14 @@
final override fun isExtensionFunction() = false
final override val enclosingElement: KspMemberContainer
+ get() = if (isSyntheticStatic) {
+ actualEnclosingElement.declaration!!
+ .requireEnclosingMemberContainer(env)
+ } else {
+ actualEnclosingElement
+ }
+
+ private val actualEnclosingElement: KspMemberContainer
get() = this.field.enclosingElement
final override val closestMemberContainer: XMemberContainer by lazy {
@@ -149,11 +159,14 @@
internal class Getter(
env: KspProcessingEnv,
field: KspFieldElement,
- override val accessor: KSPropertyGetter
+ override val accessor: KSPropertyGetter,
+ isSyntheticStatic: Boolean
) : KspSyntheticPropertyMethodElement(
env = env,
field = field,
- accessor = accessor
+ accessor = accessor,
+ isSyntheticStatic = isSyntheticStatic
+
),
XAnnotated by KspAnnotated.create(
env = env,
@@ -191,11 +204,13 @@
internal class Setter(
env: KspProcessingEnv,
field: KspFieldElement,
- override val accessor: KSPropertySetter
+ override val accessor: KSPropertySetter,
+ isSyntheticStatic: Boolean
) : KspSyntheticPropertyMethodElement(
env = env,
field = field,
- accessor = accessor
+ accessor = accessor,
+ isSyntheticStatic = isSyntheticStatic
),
XAnnotated by KspAnnotated.create(
env = env,
@@ -299,7 +314,8 @@
companion object {
fun create(
env: KspProcessingEnv,
- accessor: KSPropertyAccessor
+ accessor: KSPropertyAccessor,
+ isSyntheticStatic: Boolean
): KspSyntheticPropertyMethodElement {
val enclosingType = accessor.receiver.findEnclosingMemberContainer(env)
@@ -315,28 +331,32 @@
return create(
env = env,
field = field,
- accessor = accessor
+ accessor = accessor,
+ isSyntheticStatic = isSyntheticStatic
)
}
fun create(
env: KspProcessingEnv,
field: KspFieldElement,
- accessor: KSPropertyAccessor
+ accessor: KSPropertyAccessor,
+ isSyntheticStatic: Boolean
): KspSyntheticPropertyMethodElement {
return when (accessor) {
is KSPropertyGetter -> {
Getter(
env = env,
field = field,
- accessor = accessor
+ accessor = accessor,
+ isSyntheticStatic = isSyntheticStatic
)
}
is KSPropertySetter -> {
Setter(
env = env,
field = field,
- accessor = accessor
+ accessor = accessor,
+ isSyntheticStatic = isSyntheticStatic
)
}
else -> error("Unsupported property accessor $accessor")
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 f7c0e52..03d1b39 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
@@ -854,11 +854,7 @@
"companionObjectFunctionJvmStatic"
)
methods.forEach {
- if (invocation.isKsp && it.name.lowercase().contains("companion")) {
- assertThat(it.enclosingElement).isEqualTo(companionObj)
- } else {
- assertThat(it.enclosingElement).isEqualTo(enclosingElement)
- }
+ assertThat(it.enclosingElement).isEqualTo(enclosingElement)
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
index 9fba451..3630e49 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
@@ -26,6 +26,7 @@
import androidx.room.compiler.processing.util.runKspTest
import androidx.room.compiler.processing.util.runProcessorTest
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.UNIT
import com.squareup.kotlinpoet.javapoet.JTypeName
@@ -91,6 +92,13 @@
var myProperty2: Int = 0
@field:OtherAnnotation(value="xx")
var myProperty3: Int = 0
+ companion object {
+ @get:OtherAnnotation(value="xx")
+ @JvmStatic
+ val myProperty4: String = ""
+ @get:OtherAnnotation(value="xx")
+ const val myProperty5: String = ""
+ }
}
""".trimIndent()
)
@@ -100,16 +108,21 @@
OtherAnnotation::class
)
- val targetElement = testInvocation.processingEnv.requireTypeElement("Baz")
+ val baz = testInvocation.processingEnv.requireTypeElement("Baz")
assertThat(
- annotatedElements
- ).apply {
- hasSize(3)
-
- contains(targetElement.getDeclaredMethodByJvmName("getMyProperty1"))
- contains(targetElement.getDeclaredMethodByJvmName("setMyProperty2"))
- contains(targetElement.getField("myProperty3"))
+ annotatedElements.map { it.name }
+ ).containsExactly(
+ "getMyProperty4",
+ "myProperty3",
+ "getMyProperty1",
+ "setMyProperty2",
+ "getMyProperty4"
+ )
+ baz.getDeclaredMethods().forEach { method ->
+ assertWithMessage("Enclosing element of method ${method.jvmName}")
+ .that(method.enclosingElement.name)
+ .isEqualTo("Baz")
}
}
}
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 e4f68fba..079024d 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
@@ -780,12 +780,15 @@
open class Base(x:Int) {
open fun baseFun(): Int = TODO()
suspend fun suspendFun(): Int = TODO()
- private fun privateBaseFun(): Int = TODO()
companion object {
@JvmStatic
fun staticBaseFun(): Int = TODO()
fun companionMethod(): Int = TODO()
+ @JvmStatic val name: String get() = "hello"
+ @JvmStatic suspend fun suspendFun2():Int = TODO()
+ @JvmStatic fun String.extFun(): Int = TODO()
}
+ private fun privateBaseFun(): Int = TODO()
}
open class SubClass : Base {
constructor(y:Int): super(y) {
@@ -804,10 +807,28 @@
)
runTest(sources = listOf(src)) { invocation ->
val base = invocation.processingEnv.requireTypeElement("Base")
+ val baseCompanion = invocation.processingEnv.requireTypeElement("Base.Companion")
val objectMethodNames = invocation.objectMethodNames()
- assertThat(base.getDeclaredMethods().jvmNames()).containsExactly(
- "baseFun", "suspendFun", "privateBaseFun", "staticBaseFun"
- )
+ val declaredMethods = base.getDeclaredMethods()
+ assertThat(declaredMethods.jvmNames()).containsExactly(
+ "baseFun",
+ "suspendFun",
+ "privateBaseFun",
+ "staticBaseFun",
+ "getName",
+ "suspendFun2",
+ "extFun"
+ ).inOrder()
+ declaredMethods.forEach { method ->
+ assertWithMessage("Enclosing element of method ${method.jvmName}")
+ .that(method.enclosingElement.name)
+ .isEqualTo("Base")
+ }
+ baseCompanion.getDeclaredMethods().forEach { method ->
+ assertWithMessage("Enclosing element of method ${method.jvmName}")
+ .that(method.enclosingElement.name)
+ .isEqualTo("Companion")
+ }
val sub = invocation.processingEnv.requireTypeElement("SubClass")
assertThat(sub.getDeclaredMethods().jvmNames()).containsExactly(