Repository: square/anvil
Issue: AnnotationArgumentReference.Psi does not parse string templates correctly

The string template parsing [Here](https://github.com/square/anvil/blob/855e55baeb8afdd81b22277f8477e86490ec5e2d/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/reference/AnnotationArgumentReference.kt#L267) does not currently grab the entire string. When given a blob like

```kotlin
const val SOME_STR: String = "World!"

@AnnotatedWith(value = "Hello, $SOME_STR")
object SomeObj {}
```

`annotationArgRef.value<Any>()` returns the string `"Hello, "`. Seeing as the compiler enforces that all template entries are constantly resolvable the entire value should be resolvable, which would bring this in line with the other available expression types.

Changed files:
compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/reference/AnnotationArgumentReference.kt
compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/AnnotationReferenceTest.kt

diff --git a/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/reference/AnnotationArgumentReference.kt b/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/reference/AnnotationArgumentReference.kt
index a0b86a5..9b69a64 100644
--- a/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/reference/AnnotationArgumentReference.kt
+++ b/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/reference/AnnotationArgumentReference.kt
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.com.intellij.psi.PsiElement
 import org.jetbrains.kotlin.lexer.KtTokens
 import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
 import org.jetbrains.kotlin.psi.KtClass
 import org.jetbrains.kotlin.psi.KtClassLiteralExpression
 import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
@@ -26,12 +27,13 @@ import org.jetbrains.kotlin.psi.KtNamedFunction
 import org.jetbrains.kotlin.psi.KtObjectDeclaration
 import org.jetbrains.kotlin.psi.KtPrefixExpression
 import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.psi.KtSimpleNameStringTemplateEntry
 import org.jetbrains.kotlin.psi.KtStringTemplateEntry
 import org.jetbrains.kotlin.psi.KtStringTemplateExpression
 import org.jetbrains.kotlin.psi.KtValueArgument
 import org.jetbrains.kotlin.psi.KtValueArgumentName
 import org.jetbrains.kotlin.psi.psiUtil.containingClass
-import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
+import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
 import org.jetbrains.kotlin.resolve.constants.ArrayValue
 import org.jetbrains.kotlin.resolve.constants.BooleanValue
 import org.jetbrains.kotlin.resolve.constants.ByteValue
@@ -263,11 +265,19 @@ public sealed class AnnotationArgumentReference {
           is KtCollectionLiteralExpression ->
             psiElement.children.map { parsePsiElement(it) }.convertToArrayIfNeeded()
           is KtConstantExpression, is KtPrefixExpression -> parsePrimitiveType(psiElement.text)
-          is KtStringTemplateExpression ->
-            psiElement
-              .getChildOfType<KtStringTemplateEntry>()
-              ?.text
-              ?: fail()
+          is KtStringTemplateExpression -> {
+            val parts = psiElement.getChildrenOfType<KtStringTemplateEntry>()
+
+            parts.map { templateEntry ->
+              if (templateEntry is KtSimpleNameStringTemplateEntry ||
+                templateEntry is KtBlockStringTemplateEntry
+              ) {
+                parsePsiElement(templateEntry.expression as PsiElement)
+              } else {
+                templateEntry.text
+              }
+            }.joinToString("").ifEmpty { fail() }
+          }
           is KtNameReferenceExpression -> {
             // Those are likely enum values or another primitive constant.
             resolveConstant(psiElement)?.let { return it }
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/AnnotationReferenceTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/AnnotationReferenceTest.kt
index 8125d62..ecc881f 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/AnnotationReferenceTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/AnnotationReferenceTest.kt
@@ -337,4 +337,128 @@ class AnnotationReferenceTest {
       assertThat(exitCode).isEqualTo(OK)
     }
   }
+
+  @Test fun `a string template annotation argument can be parsed`() {
+    compile(
+      """
+      package com.squareup.test
+
+      import kotlin.reflect.KClass
+ 
+      annotation class BindingKey(val name: String)
+
+      const val WORLD: String = "World!"
+
+      @BindingKey("Hello, ${'$'}WORLD")
+      interface SomeClass1
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          if (psiRef.shortName in listOf("BindingKey"))
+            return@simpleCodeGenerator null
+
+          listOf(psiRef, psiRef.toDescriptorReference()).forEach { ref ->
+            val arguments = ref.annotations.single().arguments
+            assertThat(arguments[0].value<String>()).isEqualTo("Hello, World!")
+
+            assertThat(ref.annotations.single().toAnnotationSpec().toString()).isEqualTo(
+              "@com.squareup.test.BindingKey(name = \\"Hello, World!\\")"
+            )
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
+
+  @Test fun `a string template annotation argument with no literals can be parsed`() {
+    compile(
+      """
+      package com.squareup.test
+
+      import kotlin.reflect.KClass
+ 
+      annotation class BindingKey(val name: String)
+
+      const val WORLD: String = "World!"
+
+      @BindingKey("${'$'}WORLD")
+      interface SomeClass1
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          if (psiRef.shortName in listOf("BindingKey"))
+            return@simpleCodeGenerator null
+
+          listOf(psiRef, psiRef.toDescriptorReference()).forEach { ref ->
+            val arguments = ref.annotations.single().arguments
+            assertThat(arguments[0].value<String>()).isEqualTo("World!")
+
+            assertThat(ref.annotations.single().toAnnotationSpec().toString()).isEqualTo(
+              "@com.squareup.test.BindingKey(name = \\"World!\\")"
+            )
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
+
+  @Test fun `a string template annotation argument with dot-qualified constants can be parsed`() {
+    compile(
+      """
+      package com.squareup.test
+
+      import com.squareup.test2.Abc
+      import com.squareup.test2.SomeObject
+      import kotlin.reflect.KClass
+ 
+      annotation class BindingKey(val name: String)
+
+      @BindingKey("${'$'}{SomeObject.HELLO_NESTED} ${'$'}{Abc.WORLD_NESTED}")
+      interface SomeClass1
+      """,
+      """
+      package com.squareup.test2
+      
+      object SomeObject {
+        const val HELLO_NESTED = "Hello nested,"
+      }
+      
+      class Abc {
+        companion object {
+          const val WORLD_NESTED = "World nested!"
+        }
+      }
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          if (psiRef.shortName in listOf("BindingKey", "SomeObject", "Abc", "Companion"))
+            return@simpleCodeGenerator null
+
+          listOf(psiRef, psiRef.toDescriptorReference()).forEach { ref ->
+            val arguments = ref.annotations.single().arguments
+            assertThat(arguments[0].value<String>()).isEqualTo("Hello nested, World nested!")
+
+            assertThat(ref.annotations.single().toAnnotationSpec().toString()).isEqualTo(
+              "@com.squareup.test.BindingKey(name = \\"Hello nested, World nested!\\")"
+            )
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
 }