Merge "Wrap XAnnotationValue.value list items in XAnnotationValue." into androidx-main
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)
         }
     }