Repository: square/anvil (kotlin)

Issue:
Dagger factory - Package name with a sentence case segment results in the wrong import in the generated factory
A dagger module like this
```
        package com.squareup.test
        
        import dagger.Module
        import dagger.Provides
        import com.uppercase.Package.Thing
        
        @Module
        object DaggerModule1 {
          @Provides fun provideThing(): Thing = Thing()
        }
```

generates a factory with the import to the `Package`, assuming that `Thing` is a nested class:

```
package com.squareup.test

import com.uppercase.Package
import dagger.internal.Factory
import dagger.internal.Preconditions
import kotlin.jvm.JvmStatic

object DaggerModule1_ProvideThingFactory : Factory<Package.Thing> {
  override fun get(): Package.Thing = provideThing()

  @JvmStatic
  fun create(): DaggerModule1_ProvideThingFactory = this

  @JvmStatic
  fun provideThing(): Package.Thing = Preconditions.checkNotNull(DaggerModule1.provideThing(),
      "Cannot return null from a non-@Nullable @Provides method")
}
```

This factory does not compile:
```
v: Configuring the compilation environment
e: /var/folders/34/3hpdfjvj7453gz767yc7b6j40000gn/T/Kotlin-Compilation1676785146484626793/build/anvil/com/squareup/test/DaggerModule1_ProvideThingFactory.kt: (5, 22): Packages cannot be imported
e: /var/folders/34/3hpdfjvj7453gz767yc7b6j40000gn/T/Kotlin-Compilation1676785146484626793/build/anvil/com/squareup/test/DaggerModule1_ProvideThingFactory.kt: (10, 60): Unresolved reference: Thing
e: /var/folders/34/3hpdfjvj7453gz767yc7b6j40000gn/T/Kotlin-Compilation1676785146484626793/build/anvil/com/squareup/test/DaggerModule1_ProvideThingFactory.kt: (11, 31): Unresolved reference: Thing
e: /var/folders/34/3hpdfjvj7453gz767yc7b6j40000gn/T/Kotlin-Compilation1676785146484626793/build/anvil/com/squareup/test/DaggerModule1_ProvideThingFactory.kt: (17, 31): Unresolved reference: Thing
```

Unfortunately the unconventional package is a 3rd party library that in general does not follow java conventions that are not explicitly enforced. 

From the exploration, the root cause is the same as in https://github.com/square/kotlinpoet/issues/978 - `ClassName.bestGuess` does not have enough context to make the correct guess and can only rely on conventions .

Pull request:
Fixes #150 

I decided to remove the `ClassName.bestGuess` instead of using it as a fallback because I could not think of a case where this fallback would be required and be reliable enough for it to produce something useful. However, I don't think I understand the full picture and might be missing something important.

Patch:
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 a47741b07..ecb7038df 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
@@ -62,13 +62,15 @@ internal fun ClassDescriptor.asClassName(): ClassName =
   )
 
 internal fun FqName.asClassName(module: ModuleDescriptor): ClassName {
-  try {
+  val segments = pathSegments().map { it.asString() }
+
+  // If the first sentence case is not the last segment of the path it becomes ambiguous,
+  // for example, com.Foo.Bar could be a inner class Bar or an unconventional package com.Foo.
+  val canGuessClassName = segments.indexOfFirst { it[0].isUpperCase() } == segments.size - 1
+  if (canGuessClassName) {
     return ClassName.bestGuess(asString())
-  } catch (ignored: IllegalArgumentException) {
-    // Probably lowercase class. Try to resolve the class below.
   }
 
-  val segments = pathSegments().map { it.asString() }
   for (index in (segments.size - 1) downTo 1) {
     val packageSegments = segments.subList(0, index)
     val classSegments = segments.subList(index, segments.size)


Test patch:
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 4312a3a8c..1bea16671 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
@@ -2,6 +2,9 @@ package com.squareup.anvil.compiler.dagger
 
 import com.google.common.truth.Truth.assertThat
 import com.squareup.anvil.compiler.componentInterfaceAnvilModule
+import com.squareup.anvil.compiler.dagger.UppercasePackage.OuterClass.InnerClass
+import com.squareup.anvil.compiler.dagger.UppercasePackage.TestClassInUppercasePackage
+import com.squareup.anvil.compiler.dagger.UppercasePackage.lowerCaseClassInUppercasePackage
 import com.squareup.anvil.compiler.daggerModule1
 import com.squareup.anvil.compiler.innerModule
 import com.squareup.anvil.compiler.isStatic
@@ -2231,7 +2234,7 @@ public final class DaggerComponentInterface implements ComponentInterface {
   @Test
   fun `a factory class is generated for a method returning a class with a named import`() {
     compile(
-      """
+        """
         package com.squareup.test
         
         import dagger.Module
@@ -2254,7 +2257,7 @@ public final class DaggerComponentInterface implements ComponentInterface {
   @Test
   fun `a factory class is generated ignoring the named import original path`() {
     compile(
-      """
+        """
         package com.squareup.test
         
         import dagger.Module
@@ -2309,6 +2312,93 @@ public final class DaggerComponentInterface implements ComponentInterface {
     }
   }
 
+  @Test
+  fun `a factory class is generated for a capital case package name`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import com.squareup.anvil.compiler.dagger.UppercasePackage.TestClassInUppercasePackage
+        
+        @Module
+        object DaggerModule1 {
+          @Provides fun provideThing(): TestClassInUppercasePackage = TestClassInUppercasePackage()
+        }
+        """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideThing")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val thingProvider = staticMethods.single { it.name == "provideThing" }
+      assertThat(thingProvider.invoke(null)).isInstanceOf(TestClassInUppercasePackage::class.java)
+    }
+  }
+
+  @Test
+  fun `a factory class is generated for a capital case package name and lower class name`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import com.squareup.anvil.compiler.dagger.UppercasePackage.lowerCaseClassInUppercasePackage
+        
+        @Module
+        object DaggerModule1 {
+          @Provides fun provideThing(): lowerCaseClassInUppercasePackage {
+            return lowerCaseClassInUppercasePackage()
+          }
+        }
+        """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideThing")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val thingProvider = staticMethods.single { it.name == "provideThing" }
+      assertThat(thingProvider.invoke(null))
+          .isInstanceOf(lowerCaseClassInUppercasePackage::class.java)
+    }
+  }
+
+  @Test
+  fun `a factory class is generated for a capital case package name and inner class`() {
+    compile(
+        """
+        package com.squareup.test
+        
+        import dagger.Module
+        import dagger.Provides
+        import com.squareup.anvil.compiler.dagger.UppercasePackage.OuterClass.InnerClass
+        
+        @Module
+        object DaggerModule1 {
+          @Provides fun provideThing(): InnerClass = InnerClass()
+        }
+        """
+    ) {
+      val factoryClass = daggerModule1.moduleFactoryClass("provideThing")
+
+      val constructor = factoryClass.declaredConstructors.single()
+      assertThat(constructor.parameterTypes.toList()).isEmpty()
+
+      val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }
+
+      val thingProvider = staticMethods.single { it.name == "provideThing" }
+      assertThat(thingProvider.invoke(null)).isInstanceOf(InnerClass::class.java)
+    }
+  }
+
   private fun compile(
     source: String,
     block: Result.() -> Unit = { }
diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/UppercasePackage/TestClassInUppercasePackage.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/UppercasePackage/TestClassInUppercasePackage.kt
new file mode 100644
index 000000000..598d9ad95
--- /dev/null
+++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/UppercasePackage/TestClassInUppercasePackage.kt
@@ -0,0 +1,13 @@
+@file:Suppress("PackageName")
+
+package com.squareup.anvil.compiler.dagger.UppercasePackage
+
+// These classes are used in unit tests. Don't rename or move them!
+class TestClassInUppercasePackage
+
+@Suppress("ClassName")
+class lowerCaseClassInUppercasePackage
+
+class OuterClass {
+  class InnerClass
+}