Ignore default values when converting annotation to AnnotationSpec.
Test: tested with XAnnotationTest.defaultValues
Bug: 277078486
Change-Id: I41d40c8d90ae0aeea2216b53f97bc61dfd6d9850
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
index 7b781ec..fa7b4d7 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
@@ -40,9 +40,14 @@
internal val JAVA_NONE_TYPE_NAME: JClassName =
JClassName.get("androidx.room.compiler.processing.error", "NotAType")
-fun XAnnotation.toAnnotationSpec(): AnnotationSpec {
+@JvmOverloads
+fun XAnnotation.toAnnotationSpec(includeDefaultValues: Boolean = true): AnnotationSpec {
val builder = AnnotationSpec.builder(className)
- annotationValues.forEach { builder.addAnnotationValue(it) }
+ if (includeDefaultValues) {
+ annotationValues.forEach { builder.addAnnotationValue(it) }
+ } else {
+ declaredAnnotationValues.forEach { builder.addAnnotationValue(it) }
+ }
return builder.build()
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt
index d40c108..a0e190e 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotation.kt
@@ -68,6 +68,9 @@
val className: ClassName
get() = typeElement.asClassName().java
+ /** All value in the annotation class that are explicitly declared. */
+ val declaredAnnotationValues: List<XAnnotationValue>
+
/** All values declared in the annotation class. */
val annotationValues: List<XAnnotationValue>
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
index 2e37934..814848a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
@@ -25,7 +25,6 @@
import androidx.room.compiler.processing.XFieldElement
import androidx.room.compiler.processing.XFiler
import androidx.room.compiler.processing.XMessager
-import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XProcessingEnv
import androidx.room.compiler.processing.XRoundEnv
import androidx.room.compiler.processing.XType
@@ -37,6 +36,7 @@
import androidx.room.compiler.processing.javac.JavacExecutableElement
import androidx.room.compiler.processing.javac.JavacExecutableType
import androidx.room.compiler.processing.javac.JavacFiler
+import androidx.room.compiler.processing.javac.JavacMethodElement
import androidx.room.compiler.processing.javac.JavacProcessingEnv
import androidx.room.compiler.processing.javac.JavacProcessingEnvMessager
import androidx.room.compiler.processing.javac.JavacRoundEnv
@@ -148,7 +148,7 @@
@JvmStatic
fun AnnotationValue.toXProcessing(method: ExecutableElement, env: XProcessingEnv):
XAnnotationValue = JavacAnnotationValue(
- env as JavacProcessingEnv, method.toXProcessing(env) as XMethodElement, this
+ env as JavacProcessingEnv, method.toXProcessing(env) as JavacMethodElement, this
)
@JvmStatic
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt
index 2e532d3..05e031a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotation.kt
@@ -41,6 +41,14 @@
JavacDeclaredType(env, mirror.annotationType, XNullability.NONNULL)
}
+ override val declaredAnnotationValues: List<XAnnotationValue> by lazy {
+ // getElementValues returns values of this annotation's element, only those elements with
+ // values explicitly present in the annotation are included, not those that are implicitly
+ // assuming their default values.
+ val explicitValues = mirror.getElementValues().keys.map { it.simpleName.toString() }
+ annotationValues.filter { explicitValues.contains(it.name) }
+ }
+
override val annotationValues: List<XAnnotationValue> by lazy {
AnnotationMirrors.getAnnotationValuesWithDefaults(mirror)
.map { (executableElement, annotationValue) ->
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt
index f779503..9e58a58 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacAnnotationValue.kt
@@ -19,10 +19,8 @@
import androidx.room.compiler.processing.InternalXAnnotationValue
import androidx.room.compiler.processing.XArrayType
import androidx.room.compiler.processing.XEnumTypeElement
-import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.XType
-import androidx.room.compiler.processing.compat.XConverters.toJavac
import com.google.auto.common.MoreTypes
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.AnnotationValue
@@ -33,7 +31,7 @@
internal class JavacAnnotationValue(
val env: JavacProcessingEnv,
- private val method: XMethodElement,
+ private val method: JavacMethodElement,
val annotationValue: AnnotationValue,
override val valueType: XType = method.returnType,
private val valueProvider: () -> Any? = {
@@ -41,12 +39,12 @@
}
) : InternalXAnnotationValue() {
override val name: String
- get() = method.toJavac().simpleName.toString()
+ get() = method.element.simpleName.toString()
override val value: Any? by lazy { valueProvider.invoke() }
}
-private data class VisitorData(val env: JavacProcessingEnv, val method: XMethodElement)
+private data class VisitorData(val env: JavacProcessingEnv, val method: JavacMethodElement)
private val UNWRAP_VISITOR = object : AbstractAnnotationValueVisitor8<Any?, VisitorData>() {
override fun visitBoolean(b: Boolean, data: VisitorData) = b
@@ -57,7 +55,6 @@
override fun visitInt(i: Int, data: VisitorData) = i
override fun visitLong(i: Long, data: VisitorData) = i
override fun visitShort(s: Short, data: VisitorData) = s
-
override fun visitString(s: String?, data: VisitorData) =
if (s == "<error>") {
throw TypeNotPresentException(s, null)
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt
index ac2d325..ac34870 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacKmAnnotation.kt
@@ -43,14 +43,21 @@
override val type: XType
get() = typeElement.type
+ // KmAnnotation doesn't include arguments with default values
+ override val declaredAnnotationValues: List<XAnnotationValue> by lazy {
+ annotationValues
+ }
+
override val annotationValues: List<XAnnotationValue> by lazy {
val methods = typeElement.getDeclaredMethods()
- methods.map { method ->
- JavacKmAnnotationValue(
- method = method,
- kmAnnotationArgumentContainer =
- kmAnnotation.getArguments(env).getValue(method.jvmName)
- )
- }
+ // KmAnnotation doesn't include arguments with default values
+ methods
+ .mapNotNull { method ->
+ JavacKmAnnotationValue(
+ method = method,
+ kmAnnotationArgumentContainer =
+ kmAnnotation.getArguments(env)[method.jvmName] ?: return@mapNotNull null
+ )
+ }
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
index dbfb091..9eaf170 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
@@ -22,6 +22,7 @@
import androidx.room.compiler.processing.XType
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.Origin
internal class KspAnnotation(
val env: KspProcessingEnv,
@@ -42,6 +43,12 @@
env.wrap(ksType, allowPrimitives = true)
}
+ override val declaredAnnotationValues: List<XAnnotationValue> by lazy {
+ annotationValues.filterNot {
+ (it as KspAnnotationValue).valueArgument.origin == Origin.SYNTHETIC
+ }
+ }
+
override val annotationValues: List<XAnnotationValue> by lazy {
// In KSP the annotation members may be represented by constructor parameters in kotlin
// source or by abstract methods in java source so we check both.
@@ -74,4 +81,4 @@
annotation = ksAnnotated
)
}
-}
\ No newline at end of file
+}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index 976ab4f..d1dc611 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -641,6 +641,11 @@
).isEqualTo(
listOf(LinkedHashMap::class.asKTypeName())
)
+ } else {
+ assertThat(annotation.toAnnotationSpec(includeDefaultValues = false))
+ .isEqualTo(JAnnotationSpec.get(annotation.toJavac()))
+ assertThat(annotation.toAnnotationSpec())
+ .isNotEqualTo(JAnnotationSpec.get(annotation.toJavac()))
}
val enumValueEntry = annotation.getAsEnum("enumVal")
@@ -648,7 +653,6 @@
val javaEnumType = invocation.processingEnv.requireTypeElement(JavaEnum::class)
assertThat(enumValueEntry.enclosingElement)
.isEqualTo(javaEnumType)
-
val enumList = annotation.getAsEnumList("enumArrayVal")
assertThat(enumList[0].name).isEqualTo("VAL1")
assertThat(enumList[1].name).isEqualTo("VAL2")
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 0ca863f..cc895fe 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
@@ -17,6 +17,7 @@
package androidx.room.compiler.processing
import androidx.room.compiler.codegen.JArrayTypeName
+import androidx.room.compiler.processing.compat.XConverters.toJavac
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.asJClassName
@@ -43,6 +44,7 @@
import com.squareup.kotlinpoet.SHORT
import com.squareup.kotlinpoet.SHORT_ARRAY
import com.squareup.kotlinpoet.STAR
+import com.squareup.kotlinpoet.javapoet.JAnnotationSpec
import com.squareup.kotlinpoet.javapoet.JClassName
import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName
import com.squareup.kotlinpoet.javapoet.JTypeName
@@ -982,6 +984,7 @@
@Target({ElementType.TYPE, ElementType.TYPE_USE})
@interface MyAnnotation {
String stringParam();
+ String stringParam2() default "2";
String[] stringArrayParam();
String[] stringVarArgsParam(); // There's no varargs in java so use array
}
@@ -1007,6 +1010,7 @@
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class MyAnnotation(
val stringParam: String,
+ val stringParam2: String = "2",
val stringArrayParam: Array<String>,
vararg val stringVarArgsParam: String,
)
@@ -1066,7 +1070,8 @@
val annotation = getAnnotation(invocation)
// Compare the AnnotationSpec string ignoring whitespace
- assertThat(annotation.toAnnotationSpec().toString().removeWhiteSpace())
+ if (sourceKind == SourceKind.KOTLIN && !invocation.isKsp && isTypeAnnotation) {
+ assertThat(annotation.toAnnotationSpec().toString().removeWhiteSpace())
.isEqualTo("""
@test.MyAnnotation(
stringParam = "1",
@@ -1074,7 +1079,19 @@
stringVarArgsParam = {"9", "11", "13"}
)
""".removeWhiteSpace())
-
+ } else {
+ assertThat(annotation.toAnnotationSpec().toString().removeWhiteSpace())
+ .isEqualTo("""
+ @test.MyAnnotation(
+ stringParam = "1",
+ stringParam2 = "2",
+ stringArrayParam = {"3", "5", "7"},
+ stringVarArgsParam = {"9", "11", "13"}
+ )
+ """.removeWhiteSpace())
+ val stringParam2 = annotation.getAnnotationValue("stringParam2")
+ checkSingleValue(stringParam2, "2")
+ }
val stringParam = annotation.getAnnotationValue("stringParam")
checkSingleValue(stringParam, "1")
@@ -1346,6 +1363,101 @@
}
@Test
+ fun testDefaultValues() {
+ runTest(
+ javaSource = Source.java(
+ "test.MyClass",
+ """
+ package test;
+ import java.lang.annotation.ElementType;
+ import java.lang.annotation.Target;
+ @Target({ElementType.TYPE, ElementType.TYPE_USE})
+ @interface MyAnnotation {
+ String stringParam() default "1";
+ String stringParam2() default "1";
+ String[] stringArrayParam() default {"3", "5", "7"};
+ }
+ interface MyInterface {}
+ @MyAnnotation(stringParam = "1") class MyClass implements @MyAnnotation MyInterface {}
+ """.trimIndent()
+ ) as Source.JavaSource,
+ kotlinSource = Source.kotlin(
+ "test.MyClass.kt",
+ """
+ package test
+ @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
+ annotation class MyAnnotation(
+ val stringParam: String = "1",
+ val stringParam2: String = "1",
+ val stringArrayParam: Array<String> = ["3", "5", "7"]
+ )
+ interface MyInterface
+ @MyAnnotation(stringParam = "1") class MyClass : @MyAnnotation MyInterface
+ """.trimIndent()
+ ) as Source.KotlinSource
+ ) { invocation ->
+ val annotation = getAnnotation(invocation)
+ if (sourceKind == SourceKind.JAVA && invocation.isKsp && !isPreCompiled) {
+ // TODO(https://github.com/google/ksp/issues/1392) Remove the condition
+ // when bugs are fixed in ksp/kapt.
+ assertThat(annotation.getAnnotationValue("stringParam").value).isEqualTo("1")
+ assertThat(annotation.getAnnotationValue("stringParam2").value).isEqualTo("1")
+ assertThat(
+ annotation.getAnnotationValue("stringArrayParam")
+ .asAnnotationValueList().firstOrNull()?.value).isNull()
+ } else if (sourceKind == SourceKind.KOTLIN && !invocation.isKsp && isTypeAnnotation) {
+ // TODO(b/285040492) Remove the condition when bugs are fixed in ksp/kapt.
+ assertThat(annotation.toAnnotationSpec().toString().removeWhiteSpace())
+ .isEqualTo("""
+ @test.MyAnnotation
+ """.removeWhiteSpace())
+
+ assertThat(
+ annotation.toAnnotationSpec(
+ includeDefaultValues = false).toString().removeWhiteSpace())
+ .isEqualTo("""
+ @test.MyAnnotation
+ """.removeWhiteSpace())
+ } else {
+ // Compare the AnnotationSpec string ignoring whitespace
+ assertThat(annotation.toAnnotationSpec().toString().removeWhiteSpace())
+ .isEqualTo("""
+ @test.MyAnnotation(
+ stringParam = "1",
+ stringParam2 = "1",
+ stringArrayParam = {"3", "5", "7"}
+ )
+ """.removeWhiteSpace())
+ if (!isTypeAnnotation) {
+ assertThat(
+ annotation.toAnnotationSpec(
+ includeDefaultValues = false).toString().removeWhiteSpace())
+ .isEqualTo("""
+ @test.MyAnnotation(
+ stringParam = "1"
+ )
+ """.removeWhiteSpace())
+ } else {
+ assertThat(
+ annotation.toAnnotationSpec(
+ includeDefaultValues = false).toString().removeWhiteSpace())
+ .isEqualTo("""
+ @test.MyAnnotation
+ """.removeWhiteSpace())
+ }
+ if (!invocation.isKsp) {
+ // Check that "XAnnotation#toAnnotationSpec()" matches JavaPoet's
+ // "AnnotationSpec.get(AnnotationMirror)"
+ assertThat(annotation.toAnnotationSpec(includeDefaultValues = false))
+ .isEqualTo(JAnnotationSpec.get(annotation.toJavac()))
+ assertThat(annotation.toAnnotationSpec())
+ .isNotEqualTo(JAnnotationSpec.get(annotation.toJavac()))
+ }
+ }
+ }
+ }
+
+ @Test
fun testAnnotationValue() {
runTest(
javaSource = Source.java(