Repository: square/anvil (kotlin)

Issue:
Anvil cannot resolve types whose names are wrapped in backticks (like from KopyKat)
I ran into this while trying out [KopyKat](https://github.com/kopykat-kt/kopykat).

If KopyKat's added to a module which is also using Anvil's `BindingModuleGenerator`, you get this:

```
e: com.squareup.anvil.compiler.api.AnvilCompilationException: Back-end (JVM) Internal error: Couldn't resolve FqName `SomeDataClass$DslMarker` for Psi element: `SomeDataClass$DslMarker`
File being compiled: [...].SomeDataClass$Mutable.kt

        at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:288)
        at com.squareup.anvil.compiler.internal.PsiUtilsKt.requireFqName(PsiUtils.kt:139)
        at com.squareup.anvil.compiler.internal.reference.AnnotationReferenceKt.toAnnotationReference(AnnotationReference.kt:227)
        at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi$annotations$2.invoke(ClassReference.kt:146)
        at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi$annotations$2.invoke(ClassReference.kt:145)
        at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:81)
        at com.squareup.anvil.compiler.internal.reference.ClassReference$Psi.getAnnotations(ClassReference.kt:145)
        at com.squareup.anvil.compiler.internal.reference.AnnotatedReference$DefaultImpls.isAnnotatedWith(AnnotatedReference.kt:14)
        at com.squareup.anvil.compiler.internal.reference.ClassReference.isAnnotatedWith(ClassReference.kt:51)
        at com.squareup.anvil.compiler.codegen.BindingModuleGenerator.generateCode(BindingModuleGenerator.kt:74)
        at com.squareup.anvil.compiler.codegen.CodeGenerationExtension.analysisCompleted$generateCode(CodeGenerationExtension.kt:104)
        at com.squareup.anvil.compiler.codegen.CodeGenerationExtension.analysisCompleted(CodeGenerationExtension.kt:139)
        [...]
```

The annotation class it's trying to resolve is declared like this:

```kotlin
@DslMarker
public annotation class `SomeDataClass$DslMarker`
```

The problem is that the PSI resolve function is trying to resolve the FqName with backticks included, like: ``com.example.`SomeDataClass$DslMarker` ``. But in order to actually resolve the name inside a `ModuleDescriptor`, we need to strip out the backticks.

Pull request:
fixes #641

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 44978513b..000d12ee3 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
@@ -85,18 +85,20 @@ public fun PsiElement.requireFqName(
     }
     is KtNameReferenceExpression -> getReferencedName()
     is KtUserType -> {
-      val isGenericType = children.any { it is KtTypeArgumentList }
-      if (isGenericType) {
-        // For an expression like Lazy<Abc> the qualifier will be null. If the qualifier exists,
-        // then it may refer to the package and the referencedName refers to the class name, e.g.
-        // a KtUserType "abc.def.GenericType<String>" has three children: a qualifier "abc.def",
-        // the referencedName "GenericType" and the KtTypeArgumentList.
-        val qualifierText = qualifier?.text
-        val className = referencedName
-
-        if (qualifierText != null) {
-
-          // The generic might be fully qualified. Try to resolve it and return early.
+      // For a simple expression like `String` or `Lazy<Abc>` the qualifier will be null.
+      // If the qualifier exists, then it may refer to the package and the referencedName refers to
+      // the class name, e.g. a KtUserType "abc.def.GenericType<String>" has three children: a
+      // qualifier "abc.def", the referencedName "GenericType" and the KtTypeArgumentList.
+      val qualifierText = qualifier?.text
+      // Prefer `referencedName` instead of just `text` even if this is just a simple name without a
+      // qualifier, because `referencedName` will automatically remove any wrapping backticks, and
+      // backticks must be removed before resolving with an FqName via ModuleDescriptor.
+      val className = referencedName
+
+      when {
+        qualifierText != null -> {
+
+          // The KtUserType might be fully qualified. Try to resolve it and return early.
           FqName("$qualifierText.$className")
             .takeIf { it.canResolveFqName(module) }
             ?.let { return it }
@@ -104,21 +106,13 @@ public fun PsiElement.requireFqName(
           // If the name isn't fully qualified, then it's something like "Outer.Inner".
           // We can't use `text` here because that includes the type parameter(s).
           "$qualifierText.$className"
-        } else {
-          className ?: failTypeHandling()
         }
-      } else {
-        val text = text
-
-        // Sometimes a KtUserType is a fully qualified name. Give it a try and return early.
-        if (text.contains(".") && text[0].isLowerCase()) {
-          FqName(text).takeIf { it.canResolveFqName(module) }
-            ?.let { return it }
-        }
-
-        // We can't use referencedName here. For inner classes like "Outer.Inner" it would only
-        // return "Inner", whereas text returns "Outer.Inner", what we expect.
-        text
+        className != null -> className
+        // If there are type arguments, it's a generic type.  In this case, the `text` would
+        // include type parameters and can't be resolved as a name.
+        children.any { it is KtTypeArgumentList } -> failTypeHandling()
+        // If className is somehow null, and it's not a generic type, try resolving the text.
+        else -> text
       }
     }
     is KtTypeReference -> {


Test patch:
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt
index 9fa72b807..53f9b9197 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/codegen/BindingModuleGeneratorTest.kt
@@ -956,6 +956,36 @@ class BindingModuleGeneratorTest(
     }
   }
 
+  @Test fun `an unrelated annotation wrapped in backticks does not break type resolution`() {
+    compile(
+      """
+      package com.squareup.test
+      
+      import com.squareup.anvil.annotations.ContributesTo
+      $import
+      
+      @DslMarker
+      annotation class `Fancy${'$'}DslMarker`
+
+      @ContributesTo(Any::class)
+      @dagger.Module
+      abstract class DaggerModule1
+      
+      @`Fancy${'$'}DslMarker`
+      $annotation(Any::class)
+      interface ComponentInterface
+      """
+    ) {
+      val modules = if (annotationClass == MergeModules::class) {
+        componentInterface.daggerModule.includes.toList()
+      } else {
+        componentInterface.anyDaggerComponent.modules
+      }
+      assertThat(modules)
+        .containsExactly(componentInterface.anvilModule.kotlin, daggerModule1.kotlin)
+    }
+  }
+
   private val Class<*>.anyDaggerComponent: AnyDaggerComponent
     get() = anyDaggerComponent(annotationClass)
 }
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 ecc881f2e..d2af58b40 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
@@ -461,4 +461,38 @@ class AnnotationReferenceTest {
       assertThat(exitCode).isEqualTo(OK)
     }
   }
+
+  @Test fun `an annotation may have a dollar sign and be wrapped in backticks`() {
+    compile(
+      """
+      package com.squareup.test
+      
+      @DslMarker
+      annotation class `Fancy${'$'}DslMarker`
+      
+      @`Fancy${'$'}DslMarker`
+      interface SomeClass1
+      """,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          if (psiRef.shortName == "Fancy\$DslMarker") {
+            psiRef.clazz.nameAsSafeName.asString().also(::println)
+            return@simpleCodeGenerator null
+          }
+
+          listOf(psiRef, psiRef.toDescriptorReference()).forEach { ref ->
+
+            val annotation = ref.annotations.single()
+
+            assertThat(annotation.fqName.asString())
+              .isEqualTo("com.squareup.test.Fancy\$DslMarker")
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
 }
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/PropertyReferenceTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/PropertyReferenceTest.kt
index f97d37f6a..77b34c28f 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/PropertyReferenceTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/PropertyReferenceTest.kt
@@ -175,6 +175,145 @@ class PropertyReferenceTest {
     }
   }
 
+  @Test fun `a stdlib type reference wrapped in backticks resolves to the standard type`() {
+    compile(
+      """
+      package com.squareup.test
+
+      class Subject {
+        val name: `String` = ""
+      }
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          val descriptorRef = psiRef.toDescriptorReference()
+
+          listOf(psiRef, descriptorRef).forEach { ref ->
+
+            assertThat(ref.properties.single().type().asTypeName().toString())
+              .isEqualTo("kotlin.String")
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
+
+  @Test fun `a property for a nested class with a name wrapped in backticks can be resolved`() {
+    compile(
+      """
+      package com.squareup.test
+
+      class Subject {
+        lateinit var fancy: `Nested${'$'}Fancy` 
+        
+        class `Nested${'$'}Fancy`
+      }
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          val descriptorRef = psiRef.toDescriptorReference()
+
+          if (psiRef.shortName == "Nested\$Fancy") return@simpleCodeGenerator null
+
+          listOf(psiRef, descriptorRef).forEach { ref ->
+
+            val propertyType = ref.properties.single().type()
+
+            assertThat(propertyType.asTypeName().toString())
+              .isEqualTo("com.squareup.test.Subject.`Nested\$Fancy`")
+
+            assertThat(propertyType.asClassReference().fqName.asString())
+              .isEqualTo("com.squareup.test.Subject.Nested\$Fancy")
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
+
+  @Test fun `a property for a partially qualified nested class with a name wrapped in backticks can be resolved`() {
+    compile(
+      """
+      package com.squareup.test
+
+      class Subject {
+        lateinit var fancy: Subject.`Nested${'$'}Fancy` 
+        
+        class `Nested${'$'}Fancy`
+      }
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          val descriptorRef = psiRef.toDescriptorReference()
+
+          if (psiRef.shortName == "Nested\$Fancy") return@simpleCodeGenerator null
+
+          listOf(psiRef, descriptorRef).forEach { ref ->
+
+            val propertyType = ref.properties.single().type()
+
+            assertThat(propertyType.asTypeName().toString())
+              .isEqualTo("com.squareup.test.Subject.`Nested\$Fancy`")
+
+            assertThat(propertyType.asClassReference().fqName.asString())
+              .isEqualTo("com.squareup.test.Subject.Nested\$Fancy")
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
+
+  @Test fun `a property with fully qualified reference to a nested class with a name wrapped in backticks can be resolved`() {
+    compile(
+      """
+      package com.squareup.test
+
+      class Subject {
+        lateinit var fancy: com.squareup.test.Subject.`Nested${'$'}Fancy` 
+        
+        class `Nested${'$'}Fancy`
+      }
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          val descriptorRef = psiRef.toDescriptorReference()
+
+          if (psiRef.shortName == "Nested\$Fancy") return@simpleCodeGenerator null
+
+          listOf(psiRef, descriptorRef).forEach { ref ->
+
+            val propertyType = ref.properties.single().type()
+
+            assertThat(propertyType.asTypeName().toString())
+              .isEqualTo("com.squareup.test.Subject.`Nested\$Fancy`")
+
+            assertThat(propertyType.asClassReference().fqName.asString())
+              .isEqualTo("com.squareup.test.Subject.Nested\$Fancy")
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
+
   @Test fun `mixed constructor and member properties are all properties`() {
     compile(
       """
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/TypeReferenceTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/TypeReferenceTest.kt
index 13fc8b9a7..77503b756 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/TypeReferenceTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/internal/reference/TypeReferenceTest.kt
@@ -53,6 +53,46 @@ class TypeReferenceTest {
     }
   }
 
+  @Test fun `type names may be wrapped in backticks`() {
+    compile(
+      """
+      package com.squareup.test
+
+      abstract class `Fancy${'$'}Class`
+
+      class Subject : `Fancy${'$'}Class`()
+      """,
+      allWarningsAsErrors = false,
+      codeGenerators = listOf(
+        simpleCodeGenerator { psiRef ->
+          val descriptorRef = psiRef.toDescriptorReference()
+
+          when (psiRef.shortName) {
+            "Fancy\$Class" -> return@simpleCodeGenerator null
+
+            "Subject" -> {
+              listOf(psiRef, descriptorRef).forEach { ref ->
+                val superType = ref.directSuperTypeReferences().single()
+
+                assertThat(superType.asTypeName().toString())
+                  .isEqualTo("com.squareup.test.`Fancy\$Class`")
+
+                assertThat(superType.asClassReference().fqName.asString())
+                  .isEqualTo("com.squareup.test.Fancy\$Class")
+              }
+            }
+
+            else -> throw NotImplementedError(psiRef.shortName)
+          }
+
+          null
+        }
+      )
+    ) {
+      assertThat(exitCode).isEqualTo(OK)
+    }
+  }
+
   @Test fun `resolving the generic type name on a concrete type returns the concrete type`() {
     compile(
       """