Merge "Fix modifiers of properties & accessors" into androidx-master-dev
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
index 1c6e4e4..5e66046 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
@@ -22,6 +22,10 @@
it.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.jvm.JvmStatic"
}
+internal fun KSAnnotated.isJvmField() = annotations.any {
+ it.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.jvm.JvmField"
+}
+
internal fun KSAnnotated.isJvmDefault() = annotations.any {
it.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.jvm.JvmDefault"
}
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt
index 259f26d..c1570e1 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspExecutableElement.kt
@@ -33,7 +33,7 @@
declaration = declaration
),
XExecutableElement,
- XHasModifiers by KspHasModifiers(declaration),
+ XHasModifiers by KspHasModifiers.create(declaration),
XAnnotated by KspAnnotated.create(
env = env,
delegate = declaration,
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
index e997671..8f8e32d 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFieldElement.kt
@@ -31,7 +31,7 @@
val containing: KspTypeElement
) : KspElement(env, declaration),
XFieldElement,
- XHasModifiers by KspHasModifiers(declaration),
+ XHasModifiers by KspHasModifiers.create(declaration),
XAnnotated by KspAnnotated.create(env, declaration, FIELD) {
override val equalityItems: Array<out Any?> by lazy {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt
index a01effdf..34cf55c 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspHasModifiers.kt
@@ -21,14 +21,18 @@
import com.google.devtools.ksp.isPrivate
import com.google.devtools.ksp.isProtected
import com.google.devtools.ksp.isPublic
+import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSPropertyAccessor
+import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.Modifier
/**
* Implementation of [XHasModifiers] for ksp declarations.
*/
-class KspHasModifiers(
- private val declaration: KSDeclaration
+sealed class KspHasModifiers(
+ protected val declaration: KSDeclaration
) : XHasModifiers {
override fun isPublic(): Boolean {
return declaration.isPublic()
@@ -57,4 +61,79 @@
override fun isFinal(): Boolean {
return !declaration.isOpen()
}
-}
\ No newline at end of file
+
+ private class Declaration(declaration: KSDeclaration) : KspHasModifiers(declaration)
+
+ private class PropertyField(
+ declaration: KSDeclaration
+ ) : KspHasModifiers(declaration) {
+ private val jvmField by lazy {
+ declaration.isJvmField()
+ }
+
+ override fun isPublic(): Boolean {
+ return jvmField && super.isPublic()
+ }
+
+ override fun isProtected(): Boolean {
+ return jvmField && super.isProtected()
+ }
+
+ override fun isPrivate(): Boolean {
+ return if (jvmField) {
+ super.isPrivate()
+ } else {
+ // it is always private unless it is a jvm field
+ true
+ }
+ }
+ }
+
+ /**
+ * Handles accessor visibility when there is an accessor declared in code.
+ * We cannot simply merge modifiers of the property and the accessor as the visibility rules
+ * of the declaration is more complicated than just looking at modifiers.
+ */
+ private class PropertyFieldAccessor(
+ private val accessor: KSPropertyAccessor
+ ) : KspHasModifiers(accessor.receiver) {
+ override fun isPublic(): Boolean {
+ return accessor.modifiers.contains(Modifier.PUBLIC) ||
+ (!isPrivate() && !isProtected() && super.isPublic())
+ }
+
+ override fun isProtected(): Boolean {
+ return accessor.modifiers.contains(Modifier.PROTECTED) ||
+ (!isPrivate() && super.isProtected())
+ }
+
+ override fun isPrivate(): Boolean {
+ return accessor.modifiers.contains(Modifier.PRIVATE) ||
+ super.isPrivate()
+ }
+ }
+
+ companion object {
+ fun createForSyntheticAccessor(
+ property: KSPropertyDeclaration,
+ accessor: KSPropertyAccessor?
+ ): XHasModifiers {
+ if (accessor != null) {
+ return PropertyFieldAccessor(accessor)
+ }
+ return Declaration(property)
+ }
+
+ fun create(owner: KSPropertyDeclaration): XHasModifiers {
+ return PropertyField(owner)
+ }
+
+ fun create(owner: KSFunctionDeclaration): XHasModifiers {
+ return Declaration(owner)
+ }
+
+ fun create(owner: KSClassDeclaration): XHasModifiers {
+ return Declaration(owner)
+ }
+ }
+}
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index c3bfde7..699dd95 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -43,7 +43,7 @@
override val declaration: KSClassDeclaration
) : KspElement(env, declaration),
XTypeElement,
- XHasModifiers by KspHasModifiers(declaration),
+ XHasModifiers by KspHasModifiers.create(declaration),
XAnnotated by KspAnnotated.create(env, declaration, NO_USE_SITE) {
override val name: String by lazy {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
index dd1b778..a8932b4 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
@@ -35,6 +35,7 @@
import androidx.room.compiler.processing.ksp.KspProcessingEnv
import androidx.room.compiler.processing.ksp.KspTypeElement
import androidx.room.compiler.processing.ksp.overrides
+import com.google.devtools.ksp.symbol.KSPropertyAccessor
import java.util.Locale
/**
@@ -47,11 +48,13 @@
*/
internal sealed class KspSyntheticPropertyMethodElement(
val env: KspProcessingEnv,
- val field: KspFieldElement
+ val field: KspFieldElement,
+ accessor: KSPropertyAccessor?
) : XMethodElement,
XEquality,
- XHasModifiers by KspHasModifiers(
- field.declaration
+ XHasModifiers by KspHasModifiers.createForSyntheticAccessor(
+ field.declaration,
+ accessor
) {
// NOTE: modifiers of the property are not necessarily my modifiers.
// that being said, it only matters if it is private in which case KAPT does not generate the
@@ -98,7 +101,8 @@
field: KspFieldElement
) : KspSyntheticPropertyMethodElement(
env = env,
- field = field
+ field = field,
+ accessor = field.declaration.getter
),
XAnnotated by KspAnnotated.create(
env = env,
@@ -148,7 +152,8 @@
field: KspFieldElement
) : KspSyntheticPropertyMethodElement(
env = env,
- field = field
+ field = field,
+ accessor = field.declaration.setter
),
XAnnotated by KspAnnotated.create(
env = env,
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index 0e88d66..cbce886 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -237,6 +237,12 @@
private set
get() = TODO()
private val prop4:String = ""
+ protected var prop5:String = ""
+ var prop6: String
+ get // this cannot be protected, https://youtrack.jetbrains.com/issue/KT-3110
+ protected set
+ protected var prop7: String
+ private set
}
""".trimIndent()
)
@@ -247,21 +253,37 @@
}
assertThat(methodNames).containsNoneIn(
listOf(
- "setX", "setProp3", "setZ", "setProp4", "getProp4"
+ "setX", "setProp1", "setProp3", "setZ", "setProp4", "getProp4", "setProp7"
)
)
- listOf("getX", "getProp1", "getProp2", "getProp3").forEach {
- klass.getMethod(it).let { method ->
- assertThat(method.returnType.typeName).isEqualTo(invocation.types.string)
- assertThat(method.parameters).isEmpty()
+ listOf("getX", "getProp1", "getProp2", "getProp3", "getProp5", "getProp6")
+ .forEach {
+ klass.getMethod(it).let { method ->
+ assertThat(method.returnType.typeName).isEqualTo(invocation.types.string)
+ assertThat(method.parameters).isEmpty()
+ }
}
- }
listOf("setY", "setProp2").forEach {
klass.getMethod(it).let { method ->
assertThat(method.returnType.typeName).isEqualTo(invocation.types.voidOrUnit)
assertThat(method.parameters.first().type.typeName).isEqualTo(
invocation.types.string
)
+ assertThat(method.isPublic()).isTrue()
+ }
+ }
+ listOf("getProp5", "getProp7").forEach {
+ klass.getMethod(it).let { method ->
+ assertThat(method.isProtected()).isTrue()
+ assertThat(method.isPublic()).isFalse()
+ assertThat(method.isPrivate()).isFalse()
+ }
+ }
+ listOf("setProp5", "setProp6").forEach {
+ klass.getMethod(it).let { method ->
+ assertThat(method.isProtected()).isTrue()
+ assertThat(method.isPublic()).isFalse()
+ assertThat(method.isPrivate()).isFalse()
}
}
}
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
index 382e5b9..e5a0592 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
@@ -35,17 +35,50 @@
"""
class Foo {
val intField: Int = 0
+ @JvmField
+ val jvmField: Int = 0
+ protected val protectedField: Int = 0
+ @JvmField
+ protected val protectedJvmField: Int = 0
}
""".trimIndent()
)
runKspTest(sources = listOf(src), succeed = true) { invocation ->
val fooElement = invocation.processingEnv.requireTypeElement("Foo")
- fooElement
- .getField("intField").let { field ->
- assertThat(field.name).isEqualTo("intField")
- assertThat(field.kindName()).isEqualTo("property")
- assertThat(field.enclosingTypeElement).isEqualTo(fooElement)
- }
+ fooElement.getField("intField").let { field ->
+ assertThat(field.name).isEqualTo("intField")
+ assertThat(field.kindName()).isEqualTo("property")
+ assertThat(field.enclosingTypeElement).isEqualTo(fooElement)
+ assertThat(field.isPublic()).isFalse()
+ assertThat(field.isProtected()).isFalse()
+ // from java, it is private
+ assertThat(field.isPrivate()).isTrue()
+ }
+ fooElement.getField("jvmField").let { field ->
+ assertThat(field.name).isEqualTo("jvmField")
+ assertThat(field.kindName()).isEqualTo("property")
+ assertThat(field.enclosingTypeElement).isEqualTo(fooElement)
+ assertThat(field.isPublic()).isTrue()
+ assertThat(field.isProtected()).isFalse()
+ assertThat(field.isPrivate()).isFalse()
+ }
+ fooElement.getField("protectedField").let { field ->
+ assertThat(field.name).isEqualTo("protectedField")
+ assertThat(field.kindName()).isEqualTo("property")
+ assertThat(field.enclosingTypeElement).isEqualTo(fooElement)
+ assertThat(field.isPublic()).isFalse()
+ assertThat(field.isProtected()).isFalse()
+ // from java, it is private
+ assertThat(field.isPrivate()).isTrue()
+ }
+ fooElement.getField("protectedJvmField").let { field ->
+ assertThat(field.name).isEqualTo("protectedJvmField")
+ assertThat(field.kindName()).isEqualTo("property")
+ assertThat(field.enclosingTypeElement).isEqualTo(fooElement)
+ assertThat(field.isPublic()).isFalse()
+ assertThat(field.isProtected()).isTrue()
+ assertThat(field.isPrivate()).isFalse()
+ }
}
}