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(