Repository: evant/kotlin-inject (kotlin)

Issue:
Support suspend
These should all work

```kotlin
@Component abstract class MyComponent {
   abstract val foo: suspend () -> Foo

   @Provides fun provideFoo(): suspend () -> Foo = { Foo() }

   abstract suspend fun bar(): Bar

   @Provides suspend fun providesSuspendBar(): Bar = Bar()
}

@Inject class Baz(foo: suspend () -> Foo, bar: suspend () -> Bar)
```

Pull request:
Fixes #76

Patch:
diff --git a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt
index a645b75e..fb8f0706 100644
--- a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt
+++ b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt
@@ -109,6 +109,8 @@ abstract class AstConstructor(private val parent: AstClass) : AstElement() {
 abstract class AstFunction : AstMethod() {
     abstract val parameters: List<AstParam>
 
+    abstract val isSuspend: Boolean
+
     abstract override fun equals(other: Any?): Boolean
 
     abstract override fun hashCode(): Int
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 6e290d22..2719f456 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
@@ -126,6 +126,11 @@ class InjectGenerator(provider: AstProvider, private val options: Options) :
                                         FunSpec.builder(method.name)
                                             .returns(returnType.asTypeName())
                                             .addModifiers(KModifier.OVERRIDE)
+                                            .apply {
+                                                if (method.isSuspend) {
+                                                    addModifiers(KModifier.SUSPEND)
+                                                }
+                                            }
                                             .addCode(codeBlock.build())
                                             .build()
                                     )
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 ae10b3ec..72872bb0 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
@@ -339,6 +339,9 @@ private class ModelAstFunction(
     override val isPrivate: Boolean
         get() = kmFunction.isPrivate()
 
+    override val isSuspend: Boolean
+        get() = kmFunction.isSuspend()
+
     override val returnType: AstType
         get() = ModelAstType(
             this,
@@ -364,11 +367,18 @@ private class ModelAstFunction(
 
     override val parameters: List<AstParam>
         get() {
-            val params = if (kmFunction.receiverParameterType != null) {
-                // drop the extension function receiver if present
-                element.parameters.drop(1)
-            } else {
-                element.parameters
+            val params = when {
+                kmFunction.receiverParameterType != null -> {
+                    // drop the extension function receiver if present
+                    element.parameters.drop(1)
+                }
+                kmFunction.isSuspend() -> {
+                    // drop last continuation parameter
+                    element.parameters.dropLast(1)
+                }
+                else -> {
+                    element.parameters
+                }
             }
             val kmParams: List<KmValueParameter> = kmFunction.valueParameters
             return params.mapIndexed { index, param ->
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 a57e4c1e..08ca487f 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
@@ -316,6 +316,8 @@ inline fun KmFunction.isAbstract() = Flag.Common.IS_ABSTRACT(flags)
 
 inline fun KmFunction.isPrivate() = Flag.Common.IS_PRIVATE(flags)
 
+inline fun KmFunction.isSuspend() = Flag.Function.IS_SUSPEND(flags)
+
 inline fun KmProperty.isAbstract() = Flag.Common.IS_ABSTRACT(flags)
 
 inline fun KmProperty.isPrivate() = Flag.Common.IS_PRIVATE(flags)
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 3ab5ec3d..f8b27c86 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
@@ -25,20 +25,7 @@ import com.google.devtools.ksp.isAbstract
 import com.google.devtools.ksp.isPrivate
 import com.google.devtools.ksp.processing.KSPLogger
 import com.google.devtools.ksp.processing.Resolver
-import com.google.devtools.ksp.symbol.AnnotationUseSiteTarget
-import com.google.devtools.ksp.symbol.ClassKind
-import com.google.devtools.ksp.symbol.FileLocation
-import com.google.devtools.ksp.symbol.KSAnnotated
-import com.google.devtools.ksp.symbol.KSAnnotation
-import com.google.devtools.ksp.symbol.KSClassDeclaration
-import com.google.devtools.ksp.symbol.KSDeclaration
-import com.google.devtools.ksp.symbol.KSFunctionDeclaration
-import com.google.devtools.ksp.symbol.KSPropertyDeclaration
-import com.google.devtools.ksp.symbol.KSType
-import com.google.devtools.ksp.symbol.KSTypeAlias
-import com.google.devtools.ksp.symbol.KSTypeReference
-import com.google.devtools.ksp.symbol.KSValueParameter
-import com.google.devtools.ksp.symbol.Variance
+import com.google.devtools.ksp.symbol.*
 import org.jetbrains.kotlin.analyzer.AnalysisResult
 import kotlin.reflect.KClass
 
@@ -256,6 +243,9 @@ private class KSAstFunction(provider: KSAstProvider, override val declaration: K
     override val isPrivate: Boolean
         get() = declaration.isPrivate()
 
+    override val isSuspend: Boolean
+        get() = declaration.modifiers.contains(Modifier.SUSPEND)
+
     override val receiverParameterType: AstType?
         get() = declaration.extensionReceiver?.let { KSAstType(this, it) }
 


Test patch:
diff --git a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/SuspendTest.kt b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/SuspendTest.kt
index 0ce38136..3212d56a 100644
--- a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/SuspendTest.kt
+++ b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/SuspendTest.kt
@@ -19,6 +19,12 @@ import kotlin.test.Test
         @Provides get() = { Foo() }
 }
 
+@Component abstract class SuspendProviderComponent {
+    abstract suspend fun suspendFoo(): IFoo
+
+    @Provides suspend fun suspendProvides(): IFoo = Foo()
+}
+
 class SuspendTest {
     @Test
     fun generates_a_component_that_provides_a_suspend_function(): Unit = runBlocking {
@@ -26,5 +32,12 @@ class SuspendTest {
 
         assertThat(component.suspendFoo()).isInstanceOf(Foo::class)
     }
+
+    @Test
+    fun generates_a_component_that_has_a_suspending_provides(): Unit = runBlocking {
+        val component = SuspendProviderComponent::class.create()
+
+        assertThat(component.suspendFoo()).isInstanceOf(Foo::class)
+    }
 }