Repository: square/anvil
Issue: factory generation fails when injecting a nested class with a generic parameter

Kotlin 1.4.32 or 1.5.0-M2
Anvil 2.2.1

given this code:

``` Kotlin
package com.example

class Other {
  class Inner<T>
}
      
class InjectClass @Inject constructor(
  val inner: Other.Inner<String>
)
```
Compilation fails because it assumes any generic `KtUserType` which is qualified is _fully_ qualified.  So it attempts to resolve `Other.Inner`, but doesn't do so safely.

Exception:
```
com.squareup.anvil.compiler.AnvilCompilationException: Back-end (JVM) Internal error: Couldn't resolve fqName.
File being compiled: (10,14) in [...]

	at com.squareup.anvil.compiler.codegen.KotlinPoetKt$requireTypeName$2.invoke(KotlinPoet.kt:140)
	at com.squareup.anvil.compiler.codegen.KotlinPoetKt.requireTypeName(KotlinPoet.kt:210)
	at com.squareup.anvil.compiler.codegen.KotlinPoetKt.mapToParameter(KotlinPoet.kt:282)
	at com.squareup.anvil.compiler.codegen.dagger.InjectConstructorFactoryGenerator.generateFactoryClass(InjectConstructorFactoryGenerator.kt:61)
	at com.squareup.anvil.compiler.codegen.dagger.InjectConstructorFactoryGenerator.generateCodePrivate(InjectConstructorFactoryGenerator.kt:47)
	at com.squareup.anvil.compiler.codegen.PrivateCodeGenerator.generateCode(PrivateCodeGenerator.kt:19)
	at com.squareup.anvil.compiler.codegen.CodeGenerationExtension$analysisCompleted$2.invoke(CodeGenerationExtension.kt:57)
	at com.squareup.anvil.compiler.codegen.CodeGenerationExtension.analysisCompleted(CodeGenerationExtension.kt:62)
[...]
```

Changed files:
compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt
compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt

diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt
index 15c7dc5..55825c5 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt
@@ -232,17 +232,25 @@ internal fun PsiElement.requireFqName(
       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 refers to package and the referencedName refers to the class name, e.g.
+        // 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 packageName = qualifier?.text
+        val qualifierText = qualifier?.text
         val className = referencedName
 
-        if (packageName != null) {
-          return FqName("$packageName.$className")
-        }
+        if (qualifierText != null) {
 
-        className ?: failTypeHandling()
+          // The generic might be fully qualified. Try to resolve it and return early.
+          module
+            .resolveClassByFqName(FqName("$qualifierText.$className"), FROM_BACKEND)
+            ?.let { return it.fqNameSafe }
+
+          // 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
 
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt
index 20deea4..df40e0b 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt
@@ -742,6 +742,104 @@ public final class InjectClass_Factory implements Factory<InjectClass> {
     }
   }
 
+  @Test
+  fun `a factory class is generated for a class injecting an inner class with a generic type parameter`() {
+    /*
+package com.squareup.test
+
+import com.squareup.test.Other.Inner
+import dagger.`internal`.Factory
+import javax.inject.Provider
+import kotlin.String
+import kotlin.Suppress
+import kotlin.jvm.JvmStatic
+
+public class InjectClass_Factory(
+  private val param0: Provider<Inner<String>>
+) : Factory<InjectClass> {
+  public override fun `get`(): InjectClass = newInstance(param0.get())
+
+  public companion object {
+    @JvmStatic
+    public fun create(param0: Provider<Inner<String>>): InjectClass_Factory =
+        InjectClass_Factory(param0)
+
+    @JvmStatic
+    public fun newInstance(param0: Inner<String>): InjectClass = InjectClass(param0)
+  }
+}
+     */
+
+    compile(
+      """
+      package com.squareup.test
+      
+      import javax.inject.Inject
+      
+      class Other {
+        class Inner<T>
+      }
+      
+      class InjectClass @Inject constructor(
+        val inner: Other.Inner<String>
+      )
+      """
+    ) {
+      val constructor = injectClass.factoryClass().declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+    }
+  }
+
+  @Test
+  fun `a factory class is generated for a class injecting a deeply nested inner class with a generic type parameter`() {
+    /*
+package com.squareup.test
+
+import com.squareup.test.Other.Middle.Inner
+import dagger.`internal`.Factory
+import javax.inject.Provider
+import kotlin.String
+import kotlin.Suppress
+import kotlin.jvm.JvmStatic
+
+public class InjectClass_Factory(
+  private val param0: Provider<Inner<String>>
+) : Factory<InjectClass> {
+  public override fun `get`(): InjectClass = newInstance(param0.get())
+
+  public companion object {
+    @JvmStatic
+    public fun create(param0: Provider<Inner<String>>): InjectClass_Factory =
+        InjectClass_Factory(param0)
+
+    @JvmStatic
+    public fun newInstance(param0: Inner<String>): InjectClass = InjectClass(param0)
+  }
+}
+     */
+
+    compile(
+      """
+      package com.squareup.test
+      
+      import javax.inject.Inject
+      
+      class Other {
+        class Middle {
+          class Inner<T>
+        }
+      }
+      
+      class InjectClass @Inject constructor(
+        val inner: Other.Middle.Inner<String>
+      )
+      """
+    ) {
+      val constructor = injectClass.factoryClass().declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+    }
+  }
+
   @Test
   fun `a factory class is generated for an inner class starting with a lowercase character`() {
     /*