Repository: square/anvil (kotlin)

Issue:
Wildcard imports break Anvil codegen annotation constant resolution
Given the following:

    package org.bubenheimer.anvilbugs.constants
    
    const val MY_CONST: String = "Hi"

and

    package org.bubenheimer.anvilbugs

    import org.bubenheimer.anvilbugs.constants.*
    import javax.inject.Inject
    import javax.inject.Named

    class MyClass {
        @Inject
        @Named(MY_CONST)
        lateinit var myString: String
    }

Anvil 2.4.0 generates the invalid code below, which references the constant from the wrong package (`org.bubenheimer.anvilbugs.MY_CONST` instead of `org.bubenheimer.anvilbugs.constants.MY_CONST`):

    public class MyClass_MembersInjector(
        private val myString: Provider<String>
    ) : MembersInjector<MyClass> {
        public override fun injectMembers(instance: MyClass): Unit {
            MyClass_MembersInjector.injectMyString(instance, myString.get())
        }

        public companion object {
            @JvmStatic
            public fun create(myString: Provider<String>): MyClass_MembersInjector =
                MyClass_MembersInjector(myString)

            @JvmStatic
            @InjectedFieldSignature("org.bubenheimer.anvilbugs.MyClass.myString")
            @Named(value = org.bubenheimer.anvilbugs.MY_CONST)
            public fun injectMyString(instance: MyClass, myString: String): Unit {
                instance.myString = myString
            }
        }
    }

Pull request:
Fixes #592

Patch:
diff --git a/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt b/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt
index 44f56be4c..62ba93fe2 100644
--- a/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt
+++ b/compiler-utils/src/main/java/com/squareup/anvil/compiler/internal/PsiUtils.kt
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry
 import org.jetbrains.kotlin.psi.KtClassLiteralExpression
 import org.jetbrains.kotlin.psi.KtClassOrObject
 import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
+import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.psi.KtFunctionType
 import org.jetbrains.kotlin.psi.KtNameReferenceExpression
 import org.jetbrains.kotlin.psi.KtNamedDeclaration
@@ -36,6 +37,18 @@ public fun KtNamedDeclaration.requireFqName(): FqName = requireNotNull(fqName) {
   "fqName was null for $this, $nameAsSafeName"
 }
 
+@ExperimentalAnvilApi
+public fun PsiElement.ktFile(): KtFile {
+  return if (this is KtPureElement) {
+    containingKtFile
+  } else {
+    parentsWithSelf
+      .filterIsInstance<KtPureElement>()
+      .first()
+      .containingKtFile
+  }
+}
+
 @ExperimentalAnvilApi
 public fun PsiElement.fqNameOrNull(
   module: ModuleDescriptor
@@ -53,10 +66,7 @@ public fun PsiElement.fqNameOrNull(
 public fun PsiElement.requireFqName(
   module: ModuleDescriptor
 ): FqName {
-  val containingKtFile = parentsWithSelf
-    .filterIsInstance<KtPureElement>()
-    .first()
-    .containingKtFile
+  val containingKtFile = ktFile()
 
   fun failTypeHandling(): Nothing = throw AnvilCompilationException(
     "Don't know how to handle Psi element: $text",
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 9060b4be7..5fe0da2b1 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
@@ -6,6 +6,7 @@ import com.squareup.anvil.compiler.internal.classDescriptor
 import com.squareup.anvil.compiler.internal.descendant
 import com.squareup.anvil.compiler.internal.fqNameOrNull
 import com.squareup.anvil.compiler.internal.getContributedPropertyOrNull
+import com.squareup.anvil.compiler.internal.ktFile
 import com.squareup.anvil.compiler.internal.reference.AnnotationArgumentReference.Descriptor
 import com.squareup.anvil.compiler.internal.reference.AnnotationArgumentReference.Psi
 import com.squareup.anvil.compiler.internal.requireFqName
@@ -245,7 +246,11 @@ public sealed class AnnotationArgumentReference {
             ?.firstChild
             ?.fqNameOrNull(module)
             ?.descendant(psiElement.children.last().text)
-          ?: return null
+          ?: return psiElement.ktFile().importDirectives
+            // Check if there's a wildcard import for the constant.
+            .filter { it.isAllUnder }
+            .mapNotNull { it.importPath?.fqName?.descendant(psiElement.text) }
+            .firstNotNullOfOrNull { resolveConstant(it) }
 
         return resolveConstant(fqName)
       }


Test patch:
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 d0f58e09b..8125d6203 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
@@ -147,6 +147,7 @@ class AnnotationReferenceTest {
       import com.squareup.test2.Abc.Companion.CONSTANT_4
       import com.squareup.test2.SomeObject
       import com.squareup.test2.SomeObject.CONSTANT_3
+      import com.squareup.test3.*
       import kotlin.Int.Companion.MAX_VALUE
  
       annotation class BindingKey(val value: Int)
@@ -194,6 +195,9 @@ class AnnotationReferenceTest {
       
       @BindingKey(com.squareup.test2.Abc.CONSTANT_4)
       interface SomeClass14
+
+      @BindingKey(CONSTANT_5)
+      interface SomeClass15
       """,
       """
       package com.squareup.test2
@@ -210,6 +214,11 @@ class AnnotationReferenceTest {
         }
       }
       """,
+      """
+      package com.squareup.test3
+        
+      const val CONSTANT_5 = 5
+      """,
       allWarningsAsErrors = false,
       codeGenerators = listOf(
         simpleCodeGenerator { psiRef ->
@@ -267,6 +276,13 @@ class AnnotationReferenceTest {
                 assertThat(ref.annotations.single().toAnnotationSpec().toString())
                   .isEqualTo("@com.squareup.test.BindingKey(value = 4)")
               }
+              "SomeClass15" -> {
+                val argument = ref.annotations.single().arguments.single()
+                assertThat(argument.value<Int>()).isEqualTo(5)
+
+                assertThat(ref.annotations.single().toAnnotationSpec().toString())
+                  .isEqualTo("@com.squareup.test.BindingKey(value = 5)")
+              }
               else -> throw NotImplementedError(psiRef.shortName)
             }
           }