Repository: evant/kotlin-inject (kotlin)

Issue:
Namespace generated inner classes
If you have two components with the same name with different parent classes, they generate conflicting classes.

```kotlin
class Foo1 {
  @Component abstract class MyComponent()
}
class Foo2 {
  @Component abstract class MyComponent()
}
```

both generates `InjectMyComponent()` which causes a compile error.

Pull request description:
Fixes #107

Patch:
diff --git a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt
index 1413d147..68e3b1ce 100644
--- a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt
+++ b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt
@@ -43,11 +43,12 @@ class InjectGenerator<Output, Provider>(
 
         val constructor = astClass.primaryConstructor
 
-        val injectComponent = generateInjectComponent(astClass, constructor)
+        val injectName = astClass.toInjectName()
+        val injectComponent = generateInjectComponent(astClass, injectName, constructor)
         val createFunction = createGenerator.create(astClass, constructor, injectComponent.typeSpec)
 
         return provider.astFileSpec(
-            FileSpec.builder(astClass.packageName, "Inject${astClass.name}")
+            FileSpec.builder(astClass.packageName, injectName)
                 .apply {
                     createFunction.forEach { addFunction(it) }
                 }, injectComponent
@@ -57,9 +58,10 @@ class InjectGenerator<Output, Provider>(
     @Suppress("ComplexMethod", "LongMethod", "NestedBlockDepth")
     private fun generateInjectComponent(
         astClass: AstClass,
+        injectName: String,
         constructor: AstConstructor?
     ): AstTypeSpec {
-        val context = collectTypes(astClass)
+        val context = collectTypes(astClass, injectName)
         val resolver = TypeResultResolver(this, options)
         val scope = context.collector.scopeClass
         scopeType = scope?.scopeType(options)
@@ -142,7 +144,8 @@ class InjectGenerator<Output, Provider>(
     }
 
     private fun collectTypes(
-        astClass: AstClass
+        astClass: AstClass,
+        injectName: String,
     ): Context {
         val typeCollector = TypeCollector(
             provider = this,
@@ -153,7 +156,7 @@ class InjectGenerator<Output, Provider>(
         val scopeFromParent = elementScopeClass != astClass
         return Context(
             provider = this,
-            className = "Inject${astClass.name}",
+            className = injectName,
             collector = typeCollector,
             scopeInterface = if (scopeFromParent) elementScopeClass else null,
         )
@@ -214,6 +217,9 @@ fun AstMethod.isProvider(): Boolean =
         is AstProperty -> true
     } && receiverParameterType == null && returnType.isNotUnit()
 
+fun AstClass.toInjectName(): String =
+    "Inject${asClassName().simpleNames.joinToString("_")}"
+
 private fun dumpGraph(astClass: AstClass, entries: List<TypeResult.Provider>): String {
     val out = StringBuilder(astClass.name).append("\n")
     for (entry in entries) {
diff --git a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
index 7264f535..3b740b6c 100644
--- a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
+++ b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
@@ -152,7 +152,7 @@ private class KSAstClass(provider: KSAstProvider, override val declaration: KSCl
     KSAstProvider by provider {
 
     override val packageName: String
-        get() = declaration.qualifiedName!!.getQualifier()
+        get() = declaration.simplePackageName()
 
     override val name: String
         get() = declaration.simpleName.asString()
@@ -211,7 +211,7 @@ private class KSAstClass(provider: KSAstProvider, override val declaration: KSCl
         }
 
     override fun asClassName(): ClassName {
-        return ClassName(packageName, name)
+        return declaration.asClassName()
     }
 
     override fun equals(other: Any?): Boolean {
@@ -370,7 +370,7 @@ private class KSAstType(provider: KSAstProvider, val type: KSType) : AstType(),
     override val declaration: KSDeclaration get() = type.declaration
 
     override val packageName: String
-        get() = type.declaration.qualifiedName!!.getQualifier()
+        get() = type.declaration.simplePackageName()
 
     override val simpleName: String
         get() = type.declaration.simpleName.asString()
diff --git a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt
index 896a9de8..1281bd89 100644
--- a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt
+++ b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt
@@ -45,11 +45,19 @@ private fun KSAnnotation.hasName(packageName: String, simpleName: String): Boole
     return declaration.packageName.asString() == packageName
 }
 
+/**
+ * package name except root is "" instead of "<root>"
+ */
+fun KSDeclaration.simplePackageName(): String {
+    val packageName = packageName.asString()
+    return if (packageName == "<root>") "" else packageName
+}
+
 fun KSDeclaration.asClassName(): ClassName {
     val name = qualifiedName!!
-    val packageName = packageName.asString()
+    val packageName = simplePackageName()
     val shortName = name.asString().removePrefix("$packageName.")
-    return ClassName(if (packageName == "<root>") "" else packageName, shortName.split('.'))
+    return ClassName(packageName, shortName.split('.'))
 }
 
 fun KSType.asTypeName(): TypeName {


Test patch:
diff --git a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/InnerClassComponentTest.kt b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/InnerClassComponentTest.kt
new file mode 100644
index 00000000..34f95678
--- /dev/null
+++ b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/InnerClassComponentTest.kt
@@ -0,0 +1,22 @@
+package me.tatarka.inject.test
+
+import me.tatarka.inject.annotations.Component
+import org.junit.Test
+
+class Inner1 {
+    @Component
+    abstract class InnerClassComponent
+}
+
+class Inner2 {
+    @Component
+    abstract class InnerClassComponent
+}
+
+class InnerClassComponentTest {
+    @Test
+    fun generates_components_in_an_inner_class_with_a_unique_name() {
+        val component1 = Inner1.InnerClassComponent::class.create()
+        val component2 = Inner2.InnerClassComponent::class.create()
+    }
+}
\ No newline at end of file