Repository: square/anvil (kotlin)

Issue:
Factory generation: incorrect import for nested classes
Class 1:

```
package com.example.myapplication

import com.example.myapplication.foo.MyClassFoo
import javax.inject.Inject

class MyClass @Inject constructor(d: Dependencies) : MyClassFoo(d)
```

Class 2:
```
package com.example.myapplication.foo

import javax.inject.Inject

open class MyClassFoo(d: Dependencies) {
  class Dependencies @Inject constructor()
}
```

Generated `MyClass_Factory` references `Dependencies` directly, without outer class name, which resuls in compilation error. Both files are otherwise valid Kotlin.

Pull request:
Add missing import for inject constructor factories when an injected type is an inner class of the class itself or are parent type.

Fixes #79.

Patch:
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/InterfaceMerger.kt b/compiler/src/main/java/com/squareup/anvil/compiler/InterfaceMerger.kt
index 187c7c7f1..02a42cf4e 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/InterfaceMerger.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/InterfaceMerger.kt
@@ -3,15 +3,12 @@ package com.squareup.anvil.compiler
 import org.jetbrains.kotlin.descriptors.ClassDescriptor
 import org.jetbrains.kotlin.descriptors.EffectiveVisibility.Public
 import org.jetbrains.kotlin.descriptors.effectiveVisibility
-import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.resolve.DescriptorUtils
-import org.jetbrains.kotlin.resolve.DescriptorUtils.getFqNameSafe
 import org.jetbrains.kotlin.resolve.constants.ArrayValue
 import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
 import org.jetbrains.kotlin.resolve.descriptorUtil.module
 import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
 import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.typeUtil.supertypes
 
 /**
  * Finds all contributed component interfaces and adds them as super types to Dagger components
@@ -98,6 +95,7 @@ internal class InterfaceMerger(
 
     if (excludedClasses.isNotEmpty()) {
       val intersect = supertypes.getAllSuperTypes()
+          .toList()
           .intersect(excludedClasses)
 
       if (intersect.isNotEmpty()) {
@@ -123,12 +121,4 @@ internal class InterfaceMerger(
     supertypes += contributedClasses
     super.addSyntheticSupertypes(thisDescriptor, supertypes)
   }
-
-  private fun List<KotlinType>.getAllSuperTypes(): List<FqName> =
-    generateSequence(this) { kotlinTypes ->
-      if (kotlinTypes.isEmpty()) null else kotlinTypes.flatMap { it.supertypes() }
-    }
-        .flatMap { it.asSequence() }
-        .map { getFqNameSafe(it.classDescriptorForType()) }
-        .toList()
 }
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt b/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt
index daa05694a..695ba6af8 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
 import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
 import org.jetbrains.kotlin.resolve.descriptorUtil.module
 import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.typeUtil.supertypes
 import org.jetbrains.org.objectweb.asm.Type
 import javax.inject.Inject
 import javax.inject.Provider
@@ -177,3 +178,10 @@ internal fun KtClassOrObject.generateClassName(
             .shortName()
             .asString()
       }
+
+internal fun List<KotlinType>.getAllSuperTypes(): Sequence<FqName> =
+  generateSequence(this) { kotlinTypes ->
+    kotlinTypes.ifEmpty { null }?.flatMap { it.supertypes() }
+  }
+      .flatMap { it.asSequence() }
+      .map { DescriptorUtils.getFqNameSafe(it.classDescriptorForType()) }
diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt
index f98bb076b..c9a488cf5 100644
--- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt
+++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/KotlinPoet.kt
@@ -41,7 +41,10 @@ internal fun FqName.asTypeName(): TypeName {
   }
 }
 
-internal fun KtCallableDeclaration.requireTypeName(module: ModuleDescriptor): TypeName {
+internal fun KtCallableDeclaration.requireTypeName(
+  module: ModuleDescriptor,
+  fqName: FqName? = null
+): TypeName {
   fun fail(): Nothing = throw AnvilCompilationException(
       message = "Couldn't resolve type of function: ${requireFqName()}",
       element = this
@@ -55,8 +58,12 @@ internal fun KtCallableDeclaration.requireTypeName(module: ModuleDescriptor): Ty
 
   if (typeReference.isGenericType()) return typeVariableName()
 
-  val fqName = typeReference.fqNameOrNull(module) ?: return typeVariableName()
-  return ClassName.bestGuess(fqName.asString())
+  val fqNameForGuess = fqName
+      ?: typeReference.fqNameOrNull(module)
+      ?: return typeVariableName()
+
+  return ClassName.bestGuess(fqNameForGuess.asString())
+      .let { if (typeReference.isNullable()) it.copy(nullable = true) else it }
 }
 
 internal data class Parameter(
@@ -83,8 +90,10 @@ internal fun List<KtCallableDeclaration>.mapToParameter(module: ModuleDescriptor
     val isWrappedInLazy = typeFqName == daggerLazyFqName
 
     val typeName = when {
-      parameter.typeReference?.isNullable() ?: false -> parameter.requireTypeName(module)
-      isWrappedInProvider || isWrappedInLazy -> {
+      parameter.typeReference?.isNullable() ?: false ->
+        parameter.requireTypeName(module, typeFqName).copy(nullable = true)
+
+      isWrappedInProvider || isWrappedInLazy ->
         TypeVariableName(
             name = typeElement!!.children
                 .filterIsInstance<KtTypeArgumentList>()
@@ -97,8 +106,8 @@ internal fun List<KtCallableDeclaration>.mapToParameter(module: ModuleDescriptor
                 .single()
                 .text
         )
-      }
-      else -> parameter.requireTypeName(module)
+
+      else -> parameter.requireTypeName(module, typeFqName)
     }.withJvmSuppressWildcardsIfNeeded(parameter)
 
     Parameter(
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 60dfc32c6..4aaf5ffc4 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
@@ -1,6 +1,7 @@
 package com.squareup.anvil.compiler.codegen
 
 import com.squareup.anvil.compiler.AnvilCompilationException
+import com.squareup.anvil.compiler.getAllSuperTypes
 import com.squareup.anvil.compiler.jvmSuppressWildcardsFqName
 import org.jetbrains.kotlin.com.intellij.psi.PsiElement
 import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.psi.KtTypeReference
 import org.jetbrains.kotlin.psi.KtUserType
 import org.jetbrains.kotlin.psi.KtValueArgument
 import org.jetbrains.kotlin.psi.KtValueArgumentName
+import org.jetbrains.kotlin.psi.psiUtil.parents
 import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
 import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
 
@@ -184,8 +186,30 @@ internal fun PsiElement.requireFqName(
     // If a fully qualified name is used, then we're done and don't need to do anything further.
     is KtDotQualifiedExpression -> return FqName(text)
     is KtNameReferenceExpression -> getReferencedName()
-    is KtUserType -> referencedName ?: failTypeHandling()
-    is KtTypeReference -> text
+    is KtUserType -> {
+      val isGenericType = children.any { it is KtTypeArgumentList }
+      if (isGenericType) {
+        referencedName ?: failTypeHandling()
+      } else {
+        // 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
+      }
+    }
+    is KtTypeReference -> {
+      val children = children
+      if (children.size == 1) {
+        try {
+          // Could be a KtNullableType or KtUserType.
+          return children[0].requireFqName(module)
+        } catch (e: AnvilCompilationException) {
+          // Fallback to the text representation.
+          text
+        }
+      } else {
+        text
+      }
+    }
     is KtNullableType -> return innerType?.requireFqName(module) ?: failTypeHandling()
     else -> failTypeHandling()
   }
@@ -216,6 +240,9 @@ internal fun PsiElement.requireFqName(
   module.resolveClassByFqName(FqName("kotlin.collections.$classReference"), FROM_BACKEND)
       ?.let { return it.fqNameSafe }
 
+  findFqNameInSuperTypes(module, classReference)
+      ?.let { return it }
+
   // Everything else isn't supported.
   throw AnvilCompilationException(
       "Couldn't resolve FqName $classReference for Psi element: $text",
@@ -223,6 +250,31 @@ internal fun PsiElement.requireFqName(
   )
 }
 
+private fun PsiElement.findFqNameInSuperTypes(
+  module: ModuleDescriptor,
+  classReference: String
+): FqName? {
+  fun tryToResolveClassFqName(outerClass: FqName): FqName? =
+    module
+        .resolveClassByFqName(FqName("$outerClass.$classReference"), FROM_BACKEND)
+        ?.fqNameSafe
+
+  val clazz = parents.filterIsInstance<KtClassOrObject>().first()
+  tryToResolveClassFqName(clazz.requireFqName())?.let { return it }
+
+  // At this point we can't work with Psi APIs anymore. We need to resolve the super types and try
+  // to find inner class in them.
+  val descriptor = module.resolveClassByFqName(clazz.requireFqName(), FROM_BACKEND)
+      ?: throw AnvilCompilationException(
+          message = "Couldn't resolve class descriptor for ${clazz.requireFqName()}",
+          element = clazz
+      )
+
+  return listOf(descriptor.defaultType).getAllSuperTypes()
+      .mapNotNull { tryToResolveClassFqName(it) }
+      .firstOrNull()
+}
+
 internal fun KtClassOrObject.functions(
   includeCompanionObjects: Boolean
 ): List<KtNamedFunction> {


Test patch:
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 b4e51cedb..8d249462f 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
@@ -676,6 +676,201 @@ public final class InjectClass_Factory implements Factory<InjectClass> {
     }
   }
 
+  @Test fun `inner classes are imported correctly`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Dependencies_Factory implements Factory<InjectClass.Dependencies> {
+  @Override
+  public InjectClass.Dependencies get() {
+    return newInstance();
+  }
+
+  public static InjectClass_Dependencies_Factory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static InjectClass.Dependencies newInstance() {
+    return new InjectClass.Dependencies();
+  }
+
+  private static final class InstanceHolder {
+    private static final InjectClass_Dependencies_Factory INSTANCE = new InjectClass_Dependencies_Factory();
+  }
+}
+     */
+
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  private final Provider<InjectClass.Dependencies> dependenciesProvider;
+
+  public InjectClass_Factory(Provider<InjectClass.Dependencies> dependenciesProvider) {
+    this.dependenciesProvider = dependenciesProvider;
+  }
+
+  @Override
+  public InjectClass get() {
+    return newInstance(dependenciesProvider.get());
+  }
+
+  public static InjectClass_Factory create(
+      Provider<InjectClass.Dependencies> dependenciesProvider) {
+    return new InjectClass_Factory(dependenciesProvider);
+  }
+
+  public static InjectClass newInstance(InjectClass.Dependencies dependencies) {
+    return new InjectClass(dependencies);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        class InjectClass @Inject constructor(dependencies: Dependencies) {
+          class Dependencies @Inject constructor()     
+        }
+        """
+    ) {
+      val constructor = injectClass.factoryClass().declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+
+      val constructorDependencies = classLoader
+          .loadClass("com.squareup.test.InjectClass\$Dependencies")
+          .factoryClass()
+          .declaredConstructors
+          .single()
+      assertThat(constructorDependencies.parameterTypes.toList()).isEmpty()
+    }
+  }
+
+  @Test fun `inner classes are imported correctly from super type`() {
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class ParentOne_Dependencies_Factory implements Factory<ParentOne.Dependencies> {
+  @Override
+  public ParentOne.Dependencies get() {
+    return newInstance();
+  }
+
+  public static ParentOne_Dependencies_Factory create() {
+    return InstanceHolder.INSTANCE;
+  }
+
+  public static ParentOne.Dependencies newInstance() {
+    return new ParentOne.Dependencies();
+  }
+
+  private static final class InstanceHolder {
+    private static final ParentOne_Dependencies_Factory INSTANCE = new ParentOne_Dependencies_Factory();
+  }
+}
+     */
+
+    /*
+package com.squareup.test;
+
+import dagger.internal.Factory;
+import javax.annotation.Generated;
+import javax.inject.Provider;
+
+@Generated(
+    value = "dagger.internal.codegen.ComponentProcessor",
+    comments = "https://dagger.dev"
+)
+@SuppressWarnings({
+    "unchecked",
+    "rawtypes"
+})
+public final class InjectClass_Factory implements Factory<InjectClass> {
+  private final Provider<ParentOne.Dependencies> dProvider;
+
+  public InjectClass_Factory(Provider<ParentOne.Dependencies> dProvider) {
+    this.dProvider = dProvider;
+  }
+
+  @Override
+  public InjectClass get() {
+    return newInstance(dProvider.get());
+  }
+
+  public static InjectClass_Factory create(Provider<ParentOne.Dependencies> dProvider) {
+    return new InjectClass_Factory(dProvider);
+  }
+
+  public static InjectClass newInstance(ParentOne.Dependencies d) {
+    return new InjectClass(d);
+  }
+}
+     */
+
+    compile(
+        """
+        package com.squareup.test
+        
+        import javax.inject.Inject
+        
+        open class ParentOne(d: Dependencies) {
+          class Dependencies @Inject constructor()
+        }
+        
+        open class ParentTwo(d: Dependencies) : ParentOne(d)
+        
+        class InjectClass @Inject constructor(d: Dependencies) : ParentTwo(d)
+        """
+    ) {
+      val constructor = injectClass.factoryClass().declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).containsExactly(Provider::class.java)
+
+      val constructorDependencies = classLoader
+          .loadClass("com.squareup.test.ParentOne\$Dependencies")
+          .factoryClass()
+          .declaredConstructors
+          .single()
+      assertThat(constructorDependencies.parameterTypes.toList()).isEmpty()
+    }
+  }
+
   private fun compile(
     source: String,
     block: Result.() -> Unit = { }