Repository: evant/kotlin-inject (kotlin)

Issue:
Support typealias for @IntoMap
Normally typealiases are treated as separate types, but here it should look through them to see the Pair as it's useful to re-use across provides.

```kotlin
typealias MyEntry = Pair<MyKey, MyValue>

@Component
abstract class MyComponent {
   @Provides @IntoMap
   fun entry1(): MyEntry = ...
   @Provides @IntoMap
   fun entry2(): MyEntry = ...
}
```

Pull request:
Fixes #105

Patch:
diff --git a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt
index 1478837c..27097139 100644
--- a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt
+++ b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt
@@ -56,8 +56,9 @@ class TypeCollector private constructor(private val provider: AstProvider, priva
             if (method.hasAnnotation(INTO_MAP.packageName, INTO_MAP.simpleName)) {
                 // Pair<A, B> -> Map<A, B>
                 val type = method.returnTypeFor(astClass)
-                if (type.packageName == "kotlin" && type.simpleName == "Pair") {
-                    val typeArgs = method.returnTypeFor(astClass).arguments
+                val resolvedType = type.resolvedType()
+                if (resolvedType.packageName == "kotlin" && resolvedType.simpleName == "Pair") {
+                    val typeArgs = resolvedType.arguments
                     val mapType =
                         TypeKey(declaredTypeOf(Map::class, typeArgs[0], typeArgs[1]), method.qualifier(options))
                     addContainerType(mapType, mapOf, method, accessor, scopedComponent)
diff --git a/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt b/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
index 1aa0ea29..f1e565f3 100644
--- a/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
+++ b/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
@@ -611,7 +611,7 @@ private class ModelAstType(
     override fun resolvedType(): AstType {
         val abbreviatedType = kmType?.abbreviatedType
         return if (abbreviatedType != null) {
-            ModelAstType(this, type, abbreviatedType)
+            ModelAstType(this, type, kmType?.resolve())
         } else {
             this
         }
diff --git a/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/Util.kt b/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/Util.kt
index 74848dc5..34983b4c 100644
--- a/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/Util.kt
+++ b/kotlin-inject-compiler/kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/Util.kt
@@ -301,6 +301,18 @@ fun KmType.eqvHashCode(collector: HashCollector = HashCollector()): Int {
     }
 }
 
+/**
+ * Returns the [KmType] without type alias info.
+ */
+fun KmType.resolve(): KmType = KmType(flags).also {
+    it.classifier = classifier
+    it.arguments.clear()
+    it.arguments.addAll(arguments)
+    it.abbreviatedType = null
+    it.outerType = outerType
+    it.flexibleTypeUpperBound = flexibleTypeUpperBound
+}
+
 fun TypeMirror.eqvHashCode(collector: HashCollector = HashCollector()): Int = collectHash(collector) {
     if (this@eqvHashCode is DeclaredType) {
         val element = asElement()


Test patch:
diff --git a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt
index 4f1dc421..935597ae 100644
--- a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt
+++ b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt
@@ -10,28 +10,33 @@ import kotlin.test.Test
 
 data class FooValue(val name: String)
 
-@Component abstract class SetComponent {
+@Component
+abstract class SetComponent {
     abstract val items: Set<FooValue>
 
-    @Provides @IntoSet
+    @Provides
+    @IntoSet
     fun fooValue1() = FooValue("1")
 
     val fooValue2
         @Provides @IntoSet get() = FooValue("2")
 }
 
-@Component abstract class DynamicKeyComponent {
+@Component
+abstract class DynamicKeyComponent {
 
     abstract val items: Map<String, FooValue>
 
-    @Provides @IntoMap
+    @Provides
+    @IntoMap
     fun fooValue1() = "1" to FooValue("1")
 
     val fooValue2
         @Provides @IntoMap get() = "2" to FooValue("2")
 }
 
-@Component abstract class ParentSetComponent {
+@Component
+abstract class ParentSetComponent {
     val Foo.bind: IFoo
         @Provides @IntoSet get() = this
 
@@ -43,13 +48,27 @@ data class FooValue(val name: String)
         @Provides get() = this
 }
 
-@Component abstract class ChildSetComponent(@Component val parent: ParentSetComponent) {
+@Component
+abstract class ChildSetComponent(@Component val parent: ParentSetComponent) {
     val Foo3.bind: IFoo
         @Provides @IntoSet get() = this
 
     abstract val items: Set<IFoo>
 }
 
+typealias Entry = Pair<String, FooValue>
+
+@Component
+abstract class TypeAliasMapComponent {
+    abstract val items: Map<String, FooValue>
+
+    val fooValue1: Entry
+        @Provides @IntoMap get() = "1" to FooValue("1")
+
+    val fooValue2: Entry
+        @Provides @IntoMap get() = "2" to FooValue("2")
+}
+
 class MultibindsTest {
 
     @Test
@@ -79,4 +98,14 @@ class MultibindsTest {
             Foo3()
         )
     }
+
+    @Test
+    fun generates_a_component_that_provides_multiple_items_using_a_type_alias_into_a_map() {
+        val component = TypeAliasMapComponent::class.create()
+
+        assertThat(component.items).containsOnly(
+            "1" to FooValue("1"),
+            "2" to FooValue("2")
+        )
+    }
 }
\ No newline at end of file