Wrap XAnnotationValue.value list items in XAnnotationValue.

This is useful when needing to report an error on an item in
the list. It also matches how things are modeled in javac.

Test: ./gradlew :room:r-c-p:test
Change-Id: I81f6ff5f6d4f62a2c77e420fb69a6a89c9efc427
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 1787f38..7a4dc1b 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
@@ -47,40 +47,92 @@
      */
     val type: XType
 
-    /**
-     * All values declared in the annotation class.
-     */
+    /** All values declared in the annotation class. */
     val annotationValues: List<XAnnotationValue>
 
-    /**
-     * Returns the value of the given [methodName] as a type reference.
-     */
-    fun getAsType(methodName: String): XType = get(methodName)
+    /** Returns the value of the given [methodName] as a type reference. */
+    fun getAsType(methodName: String): XType = getAnnotationValue(methodName).asType()
 
-    /**
-     * Returns the value of the given [methodName] as a list of type references.
-     */
-    fun getAsTypeList(methodName: String): List<XType> = get(methodName)
+    /** Returns the value of the given [methodName] as a list of type references. */
+    fun getAsTypeList(methodName: String): List<XType> = getAnnotationValue(methodName).asTypeList()
 
-    /**
-     * Returns the value of the given [methodName] as another [XAnnotation].
-     */
-    fun getAsAnnotation(methodName: String): XAnnotation = get(methodName)
+    /** Returns the value of the given [methodName] as another [XAnnotation]. */
+    fun getAsAnnotation(methodName: String): XAnnotation =
+        getAnnotationValue(methodName).asAnnotation()
 
-    /**
-     * Returns the value of the given [methodName] as a list of [XAnnotation].
-     */
-    fun getAsAnnotationList(methodName: String): List<XAnnotation> = get(methodName)
+    /** Returns the value of the given [methodName] as a list of [XAnnotation]. */
+    fun getAsAnnotationList(methodName: String): List<XAnnotation> =
+        getAnnotationValue(methodName).asAnnotationList()
 
-    /**
-     * Returns the value of the given [methodName] as a [XEnumEntry].
-     */
-    fun getAsEnum(methodName: String): XEnumEntry = get(methodName)
+    /** Returns the value of the given [methodName] as a [XEnumEntry]. */
+    fun getAsEnum(methodName: String): XEnumEntry = getAnnotationValue(methodName).asEnum()
 
-    /**
-     * Returns the value of the given [methodName] as a list of [XEnumEntry].
-     */
-    fun getAsEnumList(methodName: String): List<XEnumEntry> = get(methodName)
+    /** Returns the value of the given [methodName] as a list of [XEnumEntry]. */
+    fun getAsEnumList(methodName: String): List<XEnumEntry> =
+        getAnnotationValue(methodName).asEnumList()
+
+    /** Returns the value of the given [methodName] as a [Boolean]. */
+    fun getAsBoolean(methodName: String): Boolean = getAnnotationValue(methodName).asBoolean()
+
+    /** Returns the value of the given [methodName] as a list of [Boolean]. */
+    fun getAsBooleanList(methodName: String): List<Boolean> =
+        getAnnotationValue(methodName).asBooleanList()
+
+    /** Returns the value of the given [methodName] as a [String]. */
+    fun getAsString(methodName: String): String = getAnnotationValue(methodName).asString()
+
+    /** Returns the value of the given [methodName] as a list of [String]. */
+    fun getAsStringList(methodName: String): List<String> =
+        getAnnotationValue(methodName).asStringList()
+
+    /** Returns the value of the given [methodName] as a [Int]. */
+    fun getAsInt(methodName: String): Int = getAnnotationValue(methodName).asInt()
+
+    /** Returns the value of the given [methodName] as a list of [Int]. */
+    fun getAsIntList(methodName: String): List<Int> = getAnnotationValue(methodName).asIntList()
+
+    /** Returns the value of the given [methodName] as a [Long]. */
+    fun getAsLong(methodName: String): Long = getAnnotationValue(methodName).asLong()
+
+    /** Returns the value of the given [methodName] as a list of [Long]. */
+    fun getAsLongList(methodName: String): List<Long> = getAnnotationValue(methodName).asLongList()
+
+    /** Returns the value of the given [methodName] as a [Short]. */
+    fun getAsShort(methodName: String): Short = getAnnotationValue(methodName).asShort()
+
+    /** Returns the value of the given [methodName] as a list of [Short]. */
+    fun getAsShortList(methodName: String): List<Short> =
+        getAnnotationValue(methodName).asShortList()
+
+    /** Returns the value of the given [methodName] as a [Float]. */
+    fun getAsFloat(methodName: String): Float = getAnnotationValue(methodName).asFloat()
+
+    /** Returns the value of the given [methodName] as a list of [Float]. */
+    fun getAsFloatList(methodName: String): List<Float> =
+        getAnnotationValue(methodName).asFloatList()
+
+    /** Returns the value of the given [methodName] as a [Double]. */
+    fun getAsDouble(methodName: String): Double = getAnnotationValue(methodName).asDouble()
+
+    /** Returns the value of the given [methodName] as a list of [Double]. */
+    fun getAsDoubleList(methodName: String): List<Double> =
+        getAnnotationValue(methodName).asDoubleList()
+
+    /** Returns the value of the given [methodName] as a [Byte]. */
+    fun getAsByte(methodName: String): Byte = getAnnotationValue(methodName).asByte()
+
+    /** Returns the value of the given [methodName] as a list of [Byte]. */
+    fun getAsByteList(methodName: String): List<Byte> = getAnnotationValue(methodName).asByteList()
+
+    /** Returns the value of the given [methodName] as a list of [Byte]. */
+    fun getAsAnnotationValueList(methodName: String): List<XAnnotationValue> =
+        getAnnotationValue(methodName).asAnnotationValueList()
+
+    /**Returns the value of the given [methodName] as a [XAnnotationValue]. */
+    fun getAnnotationValue(methodName: String): XAnnotationValue {
+        return annotationValues.firstOrNull { it.name == methodName }
+            ?: error("No property named $methodName was found in annotation $name")
+    }
 }
 
 /**
@@ -95,15 +147,9 @@
  *
  * For convenience, wrapper functions are provided for these types, eg [XAnnotation.getAsType]
  */
-inline fun <reified T> XAnnotation.get(methodName: String): T {
-    val argument = annotationValues.firstOrNull { it.name == methodName }
-        ?: error("No property named $methodName was found in annotation $name")
-
-    return argument.value as? T ?: error(
-        "Value of $methodName of type ${argument.value?.javaClass} " +
-            "cannot be cast to ${T::class.java}"
-    )
-}
+// TODO: Consider deprecating this method for getAs*() methods
+@Suppress("DEPRECATION")
+inline fun <reified T> XAnnotation.get(methodName: String): T = get(methodName, T::class.java)
 
 /**
  * Returns the value of the given [methodName], throwing an exception if the method is not
@@ -119,16 +165,23 @@
  *
  * For convenience, wrapper functions are provided for these types, eg [XAnnotation.getAsType]
  */
+@Deprecated("Use one of the getAs*() methods instead, e.g. getAsBoolean().")
 fun <T> XAnnotation.get(methodName: String, clazz: Class<T>): T {
-    val argument = annotationValues.firstOrNull { it.name == methodName }
-        ?: error("No property named $methodName was found in annotation $name")
+    val argument = getAnnotationValue(methodName)
 
-    if (!clazz.isInstance(argument.value)) {
-        error("Value of $methodName of type ${argument.value?.javaClass} cannot be cast to $clazz")
+    val value = if (argument.value is List<*>) {
+        // If the argument is for a list, unwrap each item in the list
+        (argument.value as List<*>).map { (it as XAnnotationValue).value }
+    } else {
+        argument.value
+    }
+
+    if (!clazz.isInstance(value)) {
+        error("Value of $methodName of type ${value?.javaClass} cannot be cast to $clazz")
     }
 
     @Suppress("UNCHECKED_CAST")
-    return argument.value as T
+    return value as T
 }
 
 /**
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotationValue.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotationValue.kt
index c3c6d34..ffd039b 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotationValue.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotationValue.kt
@@ -34,7 +34,77 @@
      * - XEnumEntry
      * - XAnnotation
      * - XType
-     * - List of any of the above
+     * - List of [XAnnotationValue]
      */
     val value: Any?
-}
+
+    /** Returns the value as a [XType]. */
+    fun asType(): XType = value as XType
+
+    /** Returns the value as a list of [XType]. */
+    fun asTypeList(): List<XType> = asAnnotationValueList().map { it.asType() }
+
+    /** Returns the value as another [XAnnotation]. */
+    fun asAnnotation(): XAnnotation = value as XAnnotation
+
+    /** Returns the value as a list of [XAnnotation]. */
+    fun asAnnotationList(): List<XAnnotation> = asAnnotationValueList().map { it.asAnnotation() }
+
+    /** Returns the value as a [XEnumEntry]. */
+    fun asEnum(): XEnumEntry = value as XEnumEntry
+
+    /** Returns the value as a list of [XEnumEntry]. */
+    fun asEnumList(): List<XEnumEntry> = asAnnotationValueList().map { it.asEnum() }
+
+    /** Returns the value as a [Boolean]. */
+    fun asBoolean(): Boolean = value as Boolean
+
+    /** Returns the value as a list of [Boolean]. */
+    fun asBooleanList(): List<Boolean> = asAnnotationValueList().map { it.asBoolean() }
+
+    /** Returns the value as a [String]. */
+    fun asString(): String = value as String
+
+    /** Returns the value as a list of [String]. */
+    fun asStringList(): List<String> = asAnnotationValueList().map { it.asString() }
+
+    /** Returns the value as a [Int]. */
+    fun asInt(): Int = value as Int
+
+    /** Returns the value as a list of [Int]. */
+    fun asIntList(): List<Int> = asAnnotationValueList().map { it.asInt() }
+
+    /** Returns the value as a [Long]. */
+    fun asLong(): Long = value as Long
+
+    /** Returns the value as a list of [Long]. */
+    fun asLongList(): List<Long> = asAnnotationValueList().map { it.asLong() }
+
+    /** Returns the value as a [Short]. */
+    fun asShort(): Short = value as Short
+
+    /** Returns the value as a list of [Short]. */
+    fun asShortList(): List<Short> = asAnnotationValueList().map { it.asShort() }
+
+    /** Returns the value as a [Float]. */
+    fun asFloat(): Float = value as Float
+
+    /** Returns the value as a list of [Float]. */
+    fun asFloatList(): List<Float> = asAnnotationValueList().map { it.value as Float }
+
+    /** Returns the value as a [Double]. */
+    fun asDouble(): Double = value as Double
+
+    /** Returns the value as a list of [Double]. */
+    fun asDoubleList(): List<Double> = asAnnotationValueList().map { it.asDouble() }
+
+    /** Returns the value as a [Byte]. */
+    fun asByte(): Byte = value as Byte
+
+    /** Returns the value as a list of [Byte]. */
+    fun asByteList(): List<Byte> = asAnnotationValueList().map { it.asByte() }
+
+    /**Returns the value a list of [XAnnotationValue]. */
+    @Suppress("UNCHECKED_CAST") // Values in a list are always wrapped in XAnnotationValue
+    fun asAnnotationValueList(): List<XAnnotationValue> = value as List<XAnnotationValue>
+}
\ No newline at end of file
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 8767264..7a3d2b2 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
@@ -30,55 +30,60 @@
 
 internal class JavacAnnotationValue(
     val env: JavacProcessingEnv,
-    val element: ExecutableElement,
-    val annotationValue: AnnotationValue
+    val method: ExecutableElement,
+    val annotationValue: AnnotationValue,
+    private val valueProvider: () -> Any? = {
+        UNWRAP_VISITOR.visit(annotationValue, VisitorData(env, method))
+    }
 ) : XAnnotationValue {
     override val name: String
-        get() = element.simpleName.toString()
+        get() = method.simpleName.toString()
 
-    override val value: Any? by lazy { UNWRAP_VISITOR.visit(annotationValue, env) }
+    override val value: Any? by lazy { valueProvider.invoke() }
 }
 
-private val UNWRAP_VISITOR = object : AbstractAnnotationValueVisitor8<Any?, JavacProcessingEnv>() {
-    override fun visitBoolean(b: Boolean, env: JavacProcessingEnv) = b
-    override fun visitByte(b: Byte, env: JavacProcessingEnv) = b
-    override fun visitChar(c: Char, env: JavacProcessingEnv) = c
-    override fun visitDouble(d: Double, env: JavacProcessingEnv) = d
-    override fun visitFloat(f: Float, env: JavacProcessingEnv) = f
-    override fun visitInt(i: Int, env: JavacProcessingEnv) = i
-    override fun visitLong(i: Long, env: JavacProcessingEnv) = i
-    override fun visitShort(s: Short, env: JavacProcessingEnv) = s
+private data class VisitorData(val env: JavacProcessingEnv, val method: ExecutableElement)
 
-    override fun visitString(s: String?, env: JavacProcessingEnv) =
+private val UNWRAP_VISITOR = object : AbstractAnnotationValueVisitor8<Any?, VisitorData>() {
+    override fun visitBoolean(b: Boolean, data: VisitorData) = b
+    override fun visitByte(b: Byte, data: VisitorData) = b
+    override fun visitChar(c: Char, data: VisitorData) = c
+    override fun visitDouble(d: Double, data: VisitorData) = d
+    override fun visitFloat(f: Float, data: VisitorData) = f
+    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)
         } else {
             s
         }
 
-    override fun visitType(t: TypeMirror, env: JavacProcessingEnv): JavacType {
+    override fun visitType(t: TypeMirror, data: VisitorData): JavacType {
         if (t.kind == TypeKind.ERROR) {
             throw TypeNotPresentException(t.toString(), null)
         }
-        return env.wrap(t, kotlinType = null, XNullability.NONNULL)
+        return data.env.wrap(t, kotlinType = null, XNullability.NONNULL)
     }
 
-    override fun visitEnumConstant(c: VariableElement, env: JavacProcessingEnv): JavacEnumEntry {
+    override fun visitEnumConstant(c: VariableElement, data: VisitorData): JavacEnumEntry {
         val type = c.asType()
         if (type.kind == TypeKind.ERROR) {
             throw TypeNotPresentException(type.toString(), null)
         }
         val enumTypeElement = MoreTypes.asTypeElement(type)
         return JavacEnumEntry(
-            env = env,
+            env = data.env,
             entryElement = c,
-            enumTypeElement = JavacTypeElement.create(env, enumTypeElement) as XEnumTypeElement
+            enumTypeElement = JavacTypeElement.create(data.env, enumTypeElement) as XEnumTypeElement
         )
     }
 
-    override fun visitAnnotation(a: AnnotationMirror, env: JavacProcessingEnv) =
-        JavacAnnotation(env, a)
+    override fun visitAnnotation(a: AnnotationMirror, data: VisitorData) =
+        JavacAnnotation(data.env, a)
 
-    override fun visitArray(vals: MutableList<out AnnotationValue>, env: JavacProcessingEnv) =
-        vals.map { it.accept(this, env) }
+    override fun visitArray(vals: MutableList<out AnnotationValue>, data: VisitorData) =
+        vals.map { JavacAnnotationValue(data.env, data.method, it) { it.accept(this, data) } }
 }
\ No newline at end of file
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 122b1b7..bb707eb 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
@@ -20,10 +20,7 @@
 import androidx.room.compiler.processing.XAnnotationBox
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XAnnotationValue
-import androidx.room.compiler.processing.isArray
-import com.google.devtools.ksp.getConstructors
 import com.google.devtools.ksp.symbol.KSAnnotation
-import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSType
 
 internal class KspAnnotation(
@@ -44,18 +41,7 @@
     }
 
     override val annotationValues: List<XAnnotationValue> by lazy {
-        ksAnnotated.arguments.map { arg ->
-            KspAnnotationValue(
-                env, arg,
-                isListType = {
-                    (ksType.declaration as KSClassDeclaration).getConstructors()
-                        .singleOrNull()
-                        ?.parameters
-                        ?.firstOrNull { it.name == arg.name }
-                        ?.let { env.wrap(it.type).isArray() } == true
-                }
-            )
-        }
+        ksAnnotated.arguments.map { KspAnnotationValue(env, this, it) }
     }
 
     override fun <T : Annotation> asAnnotationBox(annotationClass: Class<T>): XAnnotationBox<T> {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt
index 7fd29c2..9959811 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationValue.kt
@@ -17,7 +17,9 @@
 package androidx.room.compiler.processing.ksp
 
 import androidx.room.compiler.processing.XAnnotationValue
+import androidx.room.compiler.processing.isArray
 import com.google.devtools.ksp.getClassDeclarationByName
+import com.google.devtools.ksp.getConstructors
 import com.google.devtools.ksp.symbol.ClassKind
 import com.google.devtools.ksp.symbol.KSAnnotation
 import com.google.devtools.ksp.symbol.KSClassDeclaration
@@ -26,64 +28,80 @@
 
 internal class KspAnnotationValue(
     val env: KspProcessingEnv,
+    private val owner: KspAnnotation,
     val valueArgument: KSValueArgument,
-    val isListType: () -> Boolean,
+    private val valueProvider: () -> Any? = { owner.unwrap(valueArgument) },
 ) : XAnnotationValue {
 
     override val name: String
         get() = valueArgument.name?.asString()
             ?: error("Value argument $this does not have a name.")
 
-    override val value: Any? by lazy { valueArgument.unwrap() }
+    override val value: Any? by lazy { valueProvider.invoke() }
+}
 
-    private fun KSValueArgument.unwrap(): Any? {
-        fun unwrap(value: Any?): Any? {
-            return when (value) {
-                is KSType -> {
-                    val declaration = value.declaration
-                    // Wrap enum entries in enum specific type elements
-                    if (declaration is KSClassDeclaration &&
-                        declaration.classKind == ClassKind.ENUM_ENTRY
-                    ) {
-                        KspEnumEntry.create(env, declaration)
-                    } else {
-                        // And otherwise represent class types as generic XType
-                        env.wrap(value, allowPrimitives = true)
-                    }
+internal fun KspAnnotation.unwrap(valueArgument: KSValueArgument): Any? {
+    fun unwrap(value: Any?): Any? {
+        return when (value) {
+            is KSType -> {
+                val declaration = value.declaration
+                // Wrap enum entries in enum specific type elements
+                if (declaration is KSClassDeclaration &&
+                    declaration.classKind == ClassKind.ENUM_ENTRY
+                ) {
+                    KspEnumEntry.create(env, declaration)
+                } else {
+                    // And otherwise represent class types as generic XType
+                    env.wrap(value, allowPrimitives = true)
                 }
-                is KSAnnotation -> KspAnnotation(env, value)
-                // The List implementation further wraps each value as a AnnotationValue.
-                // We don't use arrays because we don't have a reified type to instantiate the array
-                // with, and using "Any" prevents the array from being cast to the correct
-                // type later on.
-                is List<*> -> value.map { unwrap(it) }
-                // TODO: https://github.com/google/ksp/issues/429
-                // If the enum value is from compiled code KSP gives us the actual value an not
-                // the KSType, so we wrap it as KspEnumEntry for consistency.
-                is Enum<*> -> {
-                    val declaration =
-                        env.resolver.getClassDeclarationByName(value::class.java.canonicalName)
-                            ?: error("Cannot find KSClassDeclaration for Enum '$value'.")
-                    val valueDeclaration = declaration.declarations
-                        .filterIsInstance<KSClassDeclaration>()
-                        .filter { it.classKind == ClassKind.ENUM_ENTRY }
-                        .firstOrNull() { it.simpleName.getShortName() == value.name }
-                        ?: error("Cannot find ENUM_ENTRY '$value' in '$declaration'.")
-                    KspEnumEntry.create(env, valueDeclaration)
-                }
-                else -> value
             }
-        }
-        return unwrap(value).let { result ->
-            // TODO: 5/24/21 KSP does not wrap a single item in a list, even though the
-            // return type should be Class<?>[] (only in sources).
-            // https://github.com/google/ksp/issues/172
-            // https://github.com/google/ksp/issues/214
-            if (result !is List<*> && isListType()) {
-                listOf(result)
-            } else {
-                result
+            is KSAnnotation -> KspAnnotation(env, value)
+            // The List implementation further wraps each value as a AnnotationValue.
+            // We don't use arrays because we don't have a reified type to instantiate the array
+            // with, and using "Any" prevents the array from being cast to the correct
+            // type later on.
+            is List<*> -> value.map { unwrap(it) }
+            // TODO: https://github.com/google/ksp/issues/429
+            // If the enum value is from compiled code KSP gives us the actual value an not
+            // the KSType, so we wrap it as KspEnumEntry for consistency.
+            is Enum<*> -> {
+                val declaration =
+                    env.resolver.getClassDeclarationByName(value::class.java.canonicalName)
+                        ?: error("Cannot find KSClassDeclaration for Enum '$value'.")
+                val valueDeclaration = declaration.declarations
+                    .filterIsInstance<KSClassDeclaration>()
+                    .filter { it.classKind == ClassKind.ENUM_ENTRY }
+                    .firstOrNull() { it.simpleName.getShortName() == value.name }
+                    ?: error("Cannot find ENUM_ENTRY '$value' in '$declaration'.")
+                KspEnumEntry.create(env, valueDeclaration)
             }
+            else -> value
         }
     }
+    return unwrap(valueArgument.value).let { result ->
+        when {
+            result is List<*> -> {
+                // For lists, wrap each item in a KSPAnnotationValue. This models things similar to
+                // javac, and allows us to report errors on each individual item rather than just
+                // the list itself.
+                result.map { KspAnnotationValue(env, this, valueArgument) { it } }
+            }
+            isArrayType(valueArgument) -> {
+                // TODO: 5/24/21 KSP does not wrap a single item in a list, even though the
+                // return type should be Class<?>[] (only in sources).
+                // https://github.com/google/ksp/issues/172
+                // https://github.com/google/ksp/issues/214
+                listOf(KspAnnotationValue(env, this, valueArgument) { result })
+            }
+            else -> result
+        }
+    }
+}
+
+private fun KspAnnotation.isArrayType(arg: KSValueArgument): Boolean {
+    return (ksType.declaration as KSClassDeclaration).getConstructors()
+        .singleOrNull()
+        ?.parameters
+        ?.firstOrNull { it.name == arg.name }
+        ?.let { env.wrap(it.type).isArray() } == true
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
index c03ee6a..d5d6147 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspMessager.kt
@@ -51,6 +51,11 @@
             // pick first node with a location, if possible
             it.location != NonExistLocation
         } ?: nodes.firstOrNull() // fallback to the first non-null argument
+
+        // TODO: 10/8/21 Consider checking if the KspAnnotationValue is for an item in a value's
+        //  list and adding that information to the error message if so (currently, we have to
+        //  report an error on the list itself, so the information about the particular item is
+        //  lost). https://github.com/google/ksp/issues/656
         if (ksNode == null || ksNode.location == NonExistLocation) {
             internalPrintMessage(kind, "$msg - ${element.fallbackLocationText}", ksNode)
         } else {
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 6fd9043..de97611 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
@@ -163,10 +163,10 @@
             val element = invocation.processingEnv.requireTypeElement("foo.bar.Baz")
             val annotation = element.requireAnnotation<TestSuppressWarnings>()
 
-            val argument = annotation.annotationValues.single()
+            val argument = annotation.getAnnotationValue("value")
             assertThat(argument.name).isEqualTo("value")
             assertThat(
-                argument.value
+                argument.asStringList()
             ).isEqualTo(
                 listOf("warning1", "warning 2")
             )
@@ -192,10 +192,10 @@
             val annotation =
                 element.requireAnnotation(ClassName.get(TestSuppressWarnings::class.java))
 
-            val argument = annotation.annotationValues.single()
+            val argument = annotation.getAnnotationValue("value")
             assertThat(argument.name).isEqualTo("value")
             assertThat(
-                argument.value
+                argument.asStringList()
             ).isEqualTo(
                 listOf("warning1", "warning 2")
             )
@@ -857,10 +857,12 @@
             val annotation =
                 element.requireAnnotation(ClassName.get(JavaAnnotationWithDefaults::class.java))
 
-            // Even though not necessary for calling from Kotlin, use the version that passes
-            // in a Class to test it.
-            assertThat(annotation.get("stringVal", String::class.java)).isEqualTo("test")
-            assertThat(annotation.get("intVal", Int::class.javaObjectType)).isEqualTo(3)
+            assertThat(annotation.get<String>("stringVal")).isEqualTo("test")
+            assertThat(annotation.get<Int>("intVal")).isEqualTo(3)
+
+            // Also test reading theses values through getAs*() methods
+            assertThat(annotation.getAsString("stringVal")).isEqualTo("test")
+            assertThat(annotation.getAsInt("intVal")).isEqualTo(3)
         }
     }