Repository: square/anvil (kotlin)

Issue:
Unable to generate contributed subcomponent with factory
I've got another one when generating a `@ContributesSubcomponent` with a `ParentComponent` inside.

```

@ContributesSubcomponent(
  scope = TrainingScope::class,
  parentScope = Singleton::class
)
public interface NavEntryTrainingScopeComponent {
  @ContributesSubcomponent.Factory
  public interface Factory {
    public fun create(): NavEntryTrainingScopeComponent
  }

  @ContributesTo(Singleton::class)
  public interface ParentComponent {
    public fun getNavEntryTrainingScopeComponent(): Factory
  }
}
```

Error:

```
e: com.squareup.anvil.compiler.api.AnvilCompilationException: Back-end (JVM) Internal error: Couldn't find return type for com.freeletics.app.NavEntryTrainingScopeComponent.ParentComponent.getNavEntryTrainingScopeComponent.
File is unknown

        at com.squareup.anvil.compiler.internal.FunctionReferenceKt.returnType(FunctionReference.kt:90)
        at com.squareup.anvil.compiler.codegen.ContributesSubcomponentHandlerGenerator.findParentComponentInterface(ContributesSubcomponentHandlerGenerator.kt:363)
        at com.squareup.anvil.compiler.codegen.ContributesSubcomponentHandlerGenerator.generateParentComponent(ContributesSubcomponentHandlerGenerator.kt:296)
        at com.squareup.anvil.compiler.codegen.ContributesSubcomponentHandlerGenerator.access$generateParentComponent(ContributesSubcomponentHandlerGenerator.kt:89)
        at com.squareup.anvil.compiler.codegen.ContributesSubcomponentHandlerGenerator$generateCode$6$content$1.invoke(ContributesSubcomponentHandlerGenerator.kt:232)
        at com.squareup.anvil.compiler.codegen.ContributesSubcomponentHandlerGenerator$generateCode$6$content$1.invoke(ContributesSubcomponentHandlerGenerator.kt:197)
        at com.squareup.anvil.compiler.internal.KotlinPoetUtilsKt.buildFile(KotlinPoetUtils.kt:374)
        at com.squareup.anvil.compiler.internal.KotlinPoetUtilsKt.buildFile$default(KotlinPoetUtils.kt:363)
        at com.squareup.anvil.compiler.codegen.ContributesSubcomponentHandlerGenerator.generateCode(ContributesSubcomponentHandlerGenerator.kt:197)
        at com.squareup.anvil.compiler.codegen.CodeGenerationExtension.analysisCompleted$generateCode(CodeGenerationExtension.kt:96)
        at com.squareup.anvil.compiler.codegen.CodeGenerationExtension.analysisCompleted(CodeGenerationExtension.kt:136)
        at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM$analyzeFilesWithJavaIntegration$2.invoke(TopDownAnalyzerFacadeForJVM.kt:112)
        at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:122)
        at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:96)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:262)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:53)
        at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:113)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:253)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:100)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:58)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:170)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:52)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:92)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
        at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:98)
        at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1618)
        at jdk.internal.reflect.GeneratedMethodAccessor108.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
        at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
        at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
        at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
        at java.base/java.lang.Thread.run(Thread.java:833)
```

Let me know if I should move this to a separate issue.

Update: this one works when I generate `ParentComponent` as a top level class in the same file, but it looks like Anvil also generates a `ParentComponent` in that case.

_Originally posted by @gabrielittner in https://github.com/square/anvil/issues/477#issuecomment-1025167831_

Pull request:
Fixes #479

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 5ac8d6529..48595f097 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
@@ -41,9 +41,9 @@ 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.collectDescendantsOfType
 import org.jetbrains.kotlin.psi.psiUtil.containingClass
 import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
-import org.jetbrains.kotlin.psi.psiUtil.isTopLevelKtOrJavaMember
 import org.jetbrains.kotlin.psi.psiUtil.parents
 import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
 import org.jetbrains.kotlin.types.DefinitelyNotNullType
@@ -454,13 +454,11 @@ public fun PsiElement.requireFqName(
     ?.let { return it }
 
   // If the referenced type is declared within the same scope, it doesn't need to be imported.
-  declarationsWithinScope()
-    // Top-level declarations are already handled by "$packageName.$classReference", so skip those.
-    .filterNot { it.isTopLevelKtOrJavaMember() }
-    .mapNotNull { it.fqName }
-    .filter { it.asString().endsWith(classReference) }
-    .firstNotNullOfOrNull { module.resolveFqNameOrNull(it) }
-    ?.let { return it }
+  parents
+    .filterIsInstance<KtClassOrObject>()
+    .flatMap { it.collectDescendantsOfType<KtClassOrObject>() }
+    .firstOrNull { it.nameAsSafeName.asString() == classReference }
+    ?.let { return it.requireFqName() }
 
   // If this doesn't work, then maybe a class from the Kotlin package is used.
   module.resolveFqNameOrNull(FqName("kotlin.$classReference"))
@@ -494,20 +492,6 @@ public fun PsiElement.requireFqName(
   )
 }
 
-/**
- * Returns a sequence of all named declarations (anything with an fqName) which are declared at a
- * level between the receiver [PsiElement] and the top level of the file (inclusive).
- *
- * In practice, the returned sequence represents all declarations which can be referenced by the
- * receiver without needing an import statement or qualified name.
- */
-@ExperimentalAnvilApi
-public fun PsiElement.declarationsWithinScope(): Sequence<KtNamedDeclaration> {
-  return parents.filterIsInstance<KtNamedDeclaration>()
-    .flatMap { it.children.filterIsInstance<KtNamedDeclaration>() + it }
-    .distinct()
-}
-
 private fun PsiElement.findFqNameInSuperTypes(
   module: ModuleDescriptor,
   classReference: String


Test patch:
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/codegen/ContributesSubcomponentHandlerGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/codegen/ContributesSubcomponentHandlerGeneratorTest.kt
index 4f64fe31c..e59dad1cb 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/codegen/ContributesSubcomponentHandlerGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/codegen/ContributesSubcomponentHandlerGeneratorTest.kt
@@ -1628,6 +1628,87 @@ class ContributesSubcomponentHandlerGeneratorTest {
     }
   }
 
+  @Test fun `an @ContributeSubcomponent class can be generated with a custom factory`() {
+    val codeGenerator = object : CodeGenerator {
+      override fun isApplicable(context: AnvilContext): Boolean = true
+
+      override fun generateCode(
+        codeGenDir: File,
+        module: ModuleDescriptor,
+        projectFiles: Collection<KtFile>
+      ): Collection<GeneratedFile> {
+        return projectFiles
+          .classesAndInnerClass(module)
+          .filter { it.hasAnnotation(mergeComponentFqName, module) }
+          .map { _ ->
+            val generatedPackage = "com.squareup.test"
+
+            @Language("kotlin")
+            val content = """
+                package $generatedPackage
+                
+                import com.squareup.anvil.annotations.ContributesSubcomponent
+                import com.squareup.anvil.annotations.ContributesTo
+                import com.squareup.test.SubcomponentInterface1
+      
+                @ContributesSubcomponent(
+                  scope = Any::class, 
+                  parentScope = Unit::class,
+                )
+                interface SubcomponentInterface2 {
+                  @ContributesSubcomponent.Factory
+                  interface Factory {
+                    fun create(): SubcomponentInterface2
+                  }
+
+                  @ContributesTo(Unit::class)
+                  interface ParentComponent {
+                    fun createFactory(): Factory
+                  }
+                }
+              """
+
+            createGeneratedFile(
+              codeGenDir = codeGenDir,
+              packageName = generatedPackage,
+              fileName = "SubcomponentInterface2",
+              content = content
+            )
+          }
+          .toList()
+      }
+    }
+
+    compile(
+      """
+        package com.squareup.test
+  
+        import com.squareup.anvil.annotations.ContributesSubcomponent
+        import com.squareup.anvil.annotations.MergeComponent
+  
+        @ContributesSubcomponent(
+          scope = Any::class, 
+          parentScope = Unit::class
+        )
+        interface SubcomponentInterface1
+  
+        @MergeComponent(Unit::class)
+        interface ComponentInterface
+      """,
+      codeGenerators = listOf(codeGenerator)
+    ) {
+      val parentComponentInterface1 = subcomponentInterface1
+        .anvilComponent(componentInterface)
+        .parentComponentInterface
+
+      val parentComponentInterface2 = subcomponentInterface2
+        .parentComponentInterface
+
+      assertThat(componentInterface extends parentComponentInterface1).isTrue()
+      assertThat(componentInterface extends parentComponentInterface2).isTrue()
+    }
+  }
+
   @Test fun `a contributed subcomponent can be replaced`() {
     compile(
       """