Repository: evant/kotlin-inject (kotlin)

Issue:
Support receivers on inject functions
Currently the following generates incorrect code:

```kotlin
typealias foo = String.() -> String
@Inject
fun String.foo(bar: Bar): String = ...

@Component
abstract class MyComponent {
  abstract val foo: foo
}
```

Pull request description:
Fixes #136

Patch:
diff --git a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResult.kt b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResult.kt
index fffbdbe0..a7808118 100644
--- a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResult.kt
+++ b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResult.kt
@@ -1,5 +1,7 @@
 package me.tatarka.inject.compiler
 
+import com.squareup.kotlinpoet.MemberName
+
 /**
  * [TypeResult] wrapper that allows updating it's value.
  */
@@ -95,7 +97,7 @@ sealed class TypeResult {
      * A named function that returns the type.
      */
     class NamedFunction(
-        val name: String,
+        val name: MemberName,
         val args: List<String>,
         val parameters: List<TypeResultRef>,
     ) : TypeResult() {
diff --git a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultGenerator.kt b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultGenerator.kt
index da7cc5ae..87d16749 100644
--- a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultGenerator.kt
+++ b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultGenerator.kt
@@ -172,7 +172,7 @@ private fun TypeResult.NamedFunction.generate(): CodeBlock {
             add(" ->")
         }
 
-        add("%L(", name)
+        add("%M(", name)
         parameters.forEachIndexed { i, param ->
             if (i != 0) {
                 add(",")
diff --git a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultResolver.kt b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultResolver.kt
index c73648df..f5e1773f 100644
--- a/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultResolver.kt
+++ b/kotlin-inject-compiler/core/src/main/kotlin/me/tatarka/inject/compiler/TypeResultResolver.kt
@@ -196,10 +196,15 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
         key: TypeKey,
         args: List<AstType>
     ) = withCycleDetection(key, function) {
-        val namedArgs = args.mapIndexed { i, arg -> arg to "arg$i" }
+        // Drop receiver from args
+        val namedArgs = if (function.receiverParameterType != null) {
+            args.drop(1)
+        } else {
+            args
+        }.mapIndexed { i, arg -> arg to "arg$i" }
         val size = function.parameters.size
         TypeResult.NamedFunction(
-            name = function.asMemberName().toString(),
+            name = function.asMemberName(),
             args = namedArgs.map { it.second },
             parameters = function.parameters.mapIndexed { i, param ->
                 val key = TypeKey(param.type, param.qualifier(options))


Test patch:
diff --git a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/InjectFunctionTest.kt b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/InjectFunctionTest.kt
index 44feec87..22fc1a1a 100644
--- a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/InjectFunctionTest.kt
+++ b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/InjectFunctionTest.kt
@@ -47,6 +47,16 @@ abstract class FunctionAnnotationInjectComponent {
     abstract val baz: baz
 }
 
+typealias receiverFun = String.(arg: NamedFoo) -> String
+
+@Inject
+fun String.receiverFun(dep: Foo, arg: NamedFoo): String = this
+
+@Component
+abstract class ReceiverFunctionInjectionComponent {
+    abstract val receiverFun: receiverFun
+}
+
 class InjectFunctionTest {
 
     @Test
@@ -66,4 +76,11 @@ class InjectFunctionTest {
         assertThat(annotations).extracting { it.annotationClass }
             .containsExactly(FunctionAnnotation::class)
     }
+
+    @Test
+    fun generates_a_component_that_provides_a_function_with_receiver() {
+        val component = ReceiverFunctionInjectionComponent::class.create()
+
+        assertThat(with(component) { "test".receiverFun(NamedFoo("arg")) }).isEqualTo("test")
+    }
 }
\ No newline at end of file