Repository: square/anvil (kotlin)

Issue:
Upper case functions not compiling
Having upper case factory-style function names cause compilation errors.
I created a quick example here:
https://github.com/PaulWoitaschek/anvil/commit/1d443c1724314f5999ed5acdcec8739cb192a875

The stacktrace is as follows:
```console
➜  anvil git:(anvil_name_hickup) ./gradlew sample:library:assemble
Watching the file system is an incubating feature.
Received 15 file system events since last build
Virtual file system retained information about 5291 files, 1326 directories and 38 missing files since last build
Configuration on demand is an incubating feature.

> Configure project :
Versions: [Kotlin:1.3.72, Gradle:6.6.1, Kotlin use IR:false, Kotlin IR supported:false]

> Task :sample:library:compileKotlin FAILED
e: /home/paul/repos/anvil/sample/library/build/anvil/src-gen-compileKotlin/com/squareup/anvil/sample/hello/HelloModule_UserFactory.kt: (10, 49): Unresolved reference: User
e: /home/paul/repos/anvil/sample/library/build/anvil/src-gen-compileKotlin/com/squareup/anvil/sample/hello/HelloModule_UserFactory.kt: (11, 32): Unresolved reference: User
e: /home/paul/repos/anvil/sample/library/build/anvil/src-gen-compileKotlin/com/squareup/anvil/sample/hello/HelloModule_UserFactory.kt: (17, 22): Unresolved reference: User

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sample:library:compileKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork
   > Compilation error. See log for more details

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.6.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 733ms
14 actionable tasks: 1 executed, 13 up-to-date
Received 2 file system events for current build
Virtual file system retains information about 5294 files, 1334 directories and 38 missing files till next build
```

Pull request:
If a classname matches two imports, then try to resolve the type to find the correct one.

Fixes #154

Patch:
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 48e790aa7..84d4ffa49 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
@@ -239,18 +239,31 @@ internal fun PsiElement.requireFqName(
   // First look in the imports for the reference name. If the class is imported, then we know the
   // fully qualified name.
   importPaths
-      .filter { it.alias == null }
-      .firstOrNull {
-        it.fqName.shortName().asString() == classReference
+      .filter { it.alias == null && it.fqName.shortName().asString() == classReference }
+      .also { matchingImportPaths ->
+        when {
+          matchingImportPaths.size == 1 ->
+            return matchingImportPaths[0].fqName
+          matchingImportPaths.size > 1 ->
+            return matchingImportPaths.first { importPath ->
+              module.resolveClassByFqName(importPath.fqName, FROM_BACKEND) != null
+            }.fqName
+        }
       }
-      ?.let { return it.fqName }
 
   importPaths
-      .filter { it.alias == null }
-      .firstOrNull {
-        it.fqName.shortName().asString() == classReferenceOuter
+      .filter { it.alias == null && it.fqName.shortName().asString() == classReferenceOuter }
+      .also { matchingImportPaths ->
+        when {
+          matchingImportPaths.size == 1 ->
+            return FqName("${matchingImportPaths[0].fqName.parent()}.$classReference")
+          matchingImportPaths.size > 1 ->
+            return matchingImportPaths.first { importPath ->
+              val fqName = FqName("${importPath.fqName.parent()}.$classReference")
+              module.resolveClassByFqName(fqName, FROM_BACKEND) != null
+            }.fqName
+        }
       }
-      ?.let { return FqName("${it.fqName.parent()}.$classReference") }
 
   // If there is no import, then try to resolve the class with the same package as this file.
   module.findClassOrTypeAlias(containingKtFile.packageFqName, classReference)
@@ -289,9 +302,9 @@ internal fun PsiElement.requireFqName(
 
   // Check if it's a named import.
   containingKtFile.importDirectives
-    .firstOrNull { classReference == it.importPath?.importedName?.asString() }
-    ?.importedFqName
-    ?.let { return it }
+      .firstOrNull { classReference == it.importPath?.importedName?.asString() }
+      ?.importedFqName
+      ?.let { return it }
 
   // Everything else isn't supported.
   throw AnvilCompilationException(
@@ -390,7 +403,7 @@ fun KtUserType.isTypeParameter(): Boolean {
 
 fun KtUserType.findExtendsBound(): List<FqName> {
   return parents.filterIsInstance<KtClassOrObject>()
-    .first()
-    .typeParameters
-    .mapNotNull { it.fqName }
+      .first()
+      .typeParameters
+      .mapNotNull { it.fqName }
 }


Test patch:
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt b/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
index 4c36a0763..946a966df 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt
@@ -18,7 +18,7 @@ import java.util.Locale.US
 import kotlin.reflect.KClass
 
 internal fun compile(
-  source: String,
+  vararg sources: String,
   enableDaggerAnnotationProcessor: Boolean = false,
   generateDaggerFactories: Boolean = false,
   block: Result.() -> Unit = { }
@@ -50,10 +50,19 @@ internal fun compile(
             )
         )
 
-        val name = "${workingDir.absolutePath}/sources/src/main/java/com/squareup/test/Source.kt"
-        check(File(name).parentFile.mkdirs())
+        this.sources = sources.map { content ->
+          val packageDir = content.lines()
+              .first { it.trim().startsWith("package ") }
+              .substringAfter("package ")
+              .replace('.', '/')
 
-        sources = listOf(SourceFile.kotlin(name, contents = source, trimIndent = true))
+          val name = "${workingDir.absolutePath}/sources/src/main/java/$packageDir/Source.kt"
+          with(File(name).parentFile) {
+            check(exists() || mkdirs())
+          }
+
+          SourceFile.kotlin(name, contents = content, trimIndent = true)
+        }
       }
       .compile()
       .also(block)
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt
index 0ea5248c9..850db76c8 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt
@@ -89,10 +89,10 @@ class ComponentDetectorCheckTest {
   }
 
   private fun compile(
-    source: String,
+    vararg sources: String,
     block: Result.() -> Unit = { }
   ): Result = com.squareup.anvil.compiler.compile(
-      source = source,
+      sources = *sources,
       generateDaggerFactories = true,
       block = block
   )
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 eeca7ab8b..17f7334cf 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
@@ -1304,10 +1304,10 @@ public final class InjectClass_Factory<T extends CharSequence> implements Factor
   }
 
   private fun compile(
-    source: String,
+    vararg sources: String,
     block: Result.() -> Unit = { }
   ): Result = com.squareup.anvil.compiler.compile(
-      source = source,
+      sources = *sources,
       enableDaggerAnnotationProcessor = useDagger,
       generateDaggerFactories = !useDagger,
       block = block
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt
index 580a8c8f3..8c89b9eaa 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt
@@ -685,10 +685,10 @@ public final class InjectClass_MembersInjector<T, U, V> implements MembersInject
   }
 
   private fun compile(
-    source: String,
+    vararg sources: String,
     block: Result.() -> Unit = { }
   ): Result = com.squareup.anvil.compiler.compile(
-      source = source,
+      sources = *sources,
       enableDaggerAnnotationProcessor = useDagger,
       generateDaggerFactories = !useDagger,
       block = block
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt
index 1bea16671..6dd4d6db6 100644
--- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt
@@ -2399,11 +2399,52 @@ public final class DaggerComponentInterface implements ComponentInterface {
     }
   }
 
+  @Test
+  fun `a factory class is generated for an uppercase factory function`() {
+    compile(
+        """
+        package com.squareup.test.a
+        
+        import com.squareup.test.b.User
+        
+        fun User(): User = User(42)
+        """,
+        """
+        package com.squareup.test.b
+        
+        data class User(val age: Int)          
+        """,
+        """
+        package com.squareup.test
+        
+        import com.squareup.test.a.User
+        import com.squareup.test.b.User
+        import dagger.Module
+        import dagger.Provides
+        
+        @Module
+        object DaggerModule1 {
+          @Provides fun user(): User = User()
+        }
+        """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("user")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val userProvider = staticMethods.single { it.name == "user" }
+      assertThat(userProvider.invoke(null)).isNotNull()
+    }
+  }
+
   private fun compile(
-    source: String,
+    vararg sources: String,
     block: Result.() -> Unit = { }
   ): Result = com.squareup.anvil.compiler.compile(
-      source,
+      sources = *sources,
       enableDaggerAnnotationProcessor = useDagger,
       generateDaggerFactories = !useDagger,
       block = block