Repository: evant/kotlin-inject (kotlin)

Issue:
Cannot apply scope: @ActivityScope to type with @Assisted parameters
KSP throws this issue
```
e: [ksp] Cannot apply scope: @ActivityScope to type with @Assisted parameters:
 [navigator: com.slack.circuit.runtime.Navigator]
```
This happens only in the new update 0.6.2.

Code:

```
@Inject
class Presenter(
    @Assisted private val navigator: Navigator
) : Presenter<Example> {
    @Composable
    override fun present(): State {}
}
```
```
@Inject
class PresenterFactory(
    private val presenter: (Navigator) -> Presenter,
) : Presenter.Factory {
    override fun create(
        screen: Screen,
        navigator: Navigator,
        context: CircuitContext
    ): Presenter<*>? = when (screen) {
        is HomeScreen -> presenter(navigator)
        else -> null
    }

}

```

Pull request:
Fixes #301

Patch:
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c6f4127d..2560acf0 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
 [versions]
-kotlin-inject = "0.7.0-SNAPSHOT"
+kotlin-inject = "0.6.3-SNAPSHOT"
 kotlin = "1.9.0"
 ksp = "1.9.0-1.0.11"
 kotlinpoet = "1.14.2"
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 955642dd..ed5d056f 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
@@ -100,21 +100,21 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
             }
 
             for (method in typeInfo.providesMethods) {
-                val scopeType = method.scopeType(options)
-                if (scopeType != null && scopeType != typeInfo.elementScope) {
+                val scope = method.scopeType(options)
+                if (scope != null && scope != typeInfo.elementScope) {
                     if (typeInfo.elementScope != null) {
                         provider.error(
-                            "@Provides with scope: $scopeType must match component scope: ${typeInfo.elementScope}",
+                            "@Provides with scope: $scope must match component scope: ${typeInfo.elementScope}",
                             method
                         )
                     } else {
                         provider.error(
-                            "@Provides with scope: $scopeType cannot be provided in an unscoped component",
+                            "@Provides with scope: $scope cannot be provided in an unscoped component",
                             method
                         )
                     }
                 }
-                val scopedComponent = if (scopeType != null) astClass else null
+                val scopedComponent = if (scope != null) astClass else null
                 if (method.hasAnnotation(INTO_MAP.packageName, INTO_MAP.simpleName)) {
                     // Pair<A, B> -> Map<A, B>
                     val returnType = method.returnTypeFor(astClass)
@@ -126,7 +126,7 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
                             resolvedType.arguments[1],
                             key.qualifier
                         )
-                        addContainerType(provider, key, containerKey, method, accessor, scopedComponent)
+                        addContainerType(provider, key, containerKey, method, accessor, scope, scopedComponent)
                     } else {
                         provider.error("@IntoMap must have return type of type Pair", method)
                     }
@@ -135,7 +135,7 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
                     val returnType = method.returnTypeFor(astClass)
                     val key = TypeKey(returnType, method.qualifier(options))
                     val containerKey = ContainerKey.SetKey(returnType, key.qualifier)
-                    addContainerType(provider, key, containerKey, method, accessor, scopedComponent)
+                    addContainerType(provider, key, containerKey, method, accessor, scope, scopedComponent)
                 } else {
                     val returnType = method.returnTypeFor(astClass)
                     val key = TypeKey(returnType, method.qualifier(options))
@@ -147,7 +147,7 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
                             provider.error("@Provides method is not accessible", method)
                         }
                     }
-                    addMethod(key, method, accessor, scopedComponent)
+                    addMethod(key, method, accessor, scope, scopedComponent)
                 }
             }
 
@@ -190,6 +190,7 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
             containerKey: ContainerKey,
             method: AstMember,
             accessor: Accessor,
+            scope: AstType?,
             scopedComponent: AstClass?,
         ) {
             val current = type(containerKey.containerTypeKey(provider))
@@ -199,10 +200,16 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
             }
 
             containerTypes.getOrPut(containerKey) { mutableListOf() }
-                .add(method(method, accessor, scopedComponent))
+                .add(method(method, accessor, scope, scopedComponent))
         }
 
-        private fun addMethod(key: TypeKey, method: AstMember, accessor: Accessor, scopedComponent: AstClass?) {
+        private fun addMethod(
+            key: TypeKey,
+            method: AstMember,
+            accessor: Accessor,
+            scope: AstType?,
+            scopedComponent: AstClass?,
+        ) {
             val oldValue = types[key]
             if (oldValue != null) {
                 duplicate(key, newValue = method, oldValue = oldValue.method)
@@ -218,7 +225,7 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
                 }
             }
 
-            types[key] = method(method, accessor, scopedComponent)
+            types[key] = method(method, accessor, scope, scopedComponent)
         }
 
         private fun addProviderMethod(key: TypeKey, method: AstMember, accessor: Accessor) {
@@ -228,9 +235,10 @@ class TypeCollector(private val provider: AstProvider, private val options: Opti
             }
         }
 
-        private fun method(method: AstMember, accessor: Accessor, scopedComponent: AstClass?) = Method(
+        private fun method(method: AstMember, accessor: Accessor, scope: AstType?, scopedComponent: AstClass?) = Method(
             method = method,
             accessor = accessor,
+            scope = scope,
             scopedComponent = scopedComponent
         )
 
@@ -347,6 +355,7 @@ class ProviderMethod(
 class Method(
     val method: AstMember,
     val accessor: Accessor = Accessor.Empty,
+    val scope: AstType? = null,
     val scopedComponent: AstClass? = null,
 )
 
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 7f7f76a0..1a6f1321 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
@@ -66,6 +66,7 @@ sealed class TypeResult {
      */
     class Constructor(
         val type: AstType,
+        val scope: AstType?,
         val parameters: Map<String, TypeResultRef>,
         val supportsNamedArguments: Boolean
     ) : TypeResult() {
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 5dbff2b8..76e41048 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
@@ -61,12 +61,13 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
     private fun resolveParams(
         context: Context,
         element: AstElement,
+        scope: AstType?,
         params: List<AstParam>,
     ): Map<String, TypeResultRef> {
         return if (params.any { it.isAssisted() }) {
-            resolveParamsNew(context, element, params)
+            resolveParamsNew(context, element, scope, params)
         } else {
-            resolveParamsLegacy(context, element, params)
+            resolveParamsLegacy(context, element, scope, params)
         }
     }
 
@@ -75,12 +76,12 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
     private fun resolveParamsNew(
         context: Context,
         element: AstElement,
+        scope: AstType?,
         params: List<AstParam>,
     ): Map<String, TypeResultRef> {
-        if (context.scopeComponent != null) {
-            val scopeType = context.scopeComponent.scopeType(options)
+        if (scope != null) {
             throw FailedToGenerateException(
-                "Cannot apply scope: @${scopeType!!.simpleName} to type with @Assisted parameters: [${
+                "Cannot apply scope: @${scope.simpleName} to type with @Assisted parameters: [${
                     params.filter { it.isAssisted() }.joinToString()
                 }]"
             )
@@ -137,6 +138,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
     private fun resolveParamsLegacy(
         context: Context,
         element: AstElement,
+        scope: AstType?,
         params: List<AstParam>,
     ): Map<String, TypeResultRef> {
         val size = params.size
@@ -171,10 +173,9 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
                 """.trimIndent(),
                 element
             )
-            if (context.scopeComponent != null) {
-                val scopeType = context.scopeComponent.scopeType(options)
+            if (scope != null) {
                 throw FailedToGenerateException(
-                    "Cannot apply scope: @${scopeType!!.simpleName} to type with assisted parameters: [${
+                    "Cannot apply scope: @${scope.simpleName} to type with @Assisted parameters: [${
                         resolvedImplicitly.joinToString()
                     }]"
                 )
@@ -199,6 +200,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
                 context = withTypes(types),
                 accessor = method.accessor,
                 method = method.method,
+                scope = null,
                 key = key,
             )
         }
@@ -255,6 +257,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
                 context = this,
                 accessor = creator.accessor,
                 method = creator.method,
+                scope = creator.scope,
                 key = key,
             )
         }
@@ -270,7 +273,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
                 creator = containerKey.creator,
                 args = args,
                 mapArg = { key, arg, types ->
-                    Provides(withTypes(types), arg.accessor, arg.method, key)
+                    Provides(withTypes(types), arg.accessor, arg.method, arg.scope, key)
                 }
             )
         }
@@ -284,7 +287,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
                 args = args,
                 mapArg = { key, arg, types ->
                     Function(withTypes(types), args = innerType.arguments.dropLast(1)) { context ->
-                        TypeResultRef(key, Provides(context, arg.accessor, arg.method, key))
+                        TypeResultRef(key, Provides(context, arg.accessor, arg.method, arg.scope, key))
                     }
                 }
             )
@@ -299,7 +302,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
                 args = args,
                 mapArg = { key, arg, types ->
                     Lazy(key) {
-                        TypeResultRef(key, Provides(withTypes(types), arg.accessor, arg.method, key))
+                        TypeResultRef(key, Provides(withTypes(types), arg.accessor, arg.method, arg.scope, key))
                     }
                 }
             )
@@ -316,7 +319,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
             creator = containerKey.creator,
             args = args,
             mapArg = { key, arg, types ->
-                Provides(withTypes(types), arg.accessor, arg.method, key)
+                Provides(withTypes(types), arg.accessor, arg.method, arg.scope, key)
             }
         )
     }
@@ -376,6 +379,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
             Constructor(
                 context = this,
                 constructor = injectCtor,
+                scope = scope,
                 key = key,
             )
         }
@@ -406,6 +410,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
         context: Context,
         accessor: Accessor,
         method: AstMember,
+        scope: AstType?,
         key: TypeKey,
     ) = withCycleDetection(key, method) {
         TypeResult.Provides(
@@ -418,7 +423,7 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
             },
             isProperty = method is AstProperty,
             parameters = (method as? AstFunction)?.let {
-                resolveParams(context, method, it.parameters)
+                resolveParams(context, method, scope, it.parameters)
             } ?: emptyMap(),
         )
     }
@@ -435,14 +440,19 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
         result = resolve(context.withoutScoped(key.type, scopedComponent), element, key)
     )
 
-    private fun Constructor(context: Context, constructor: AstConstructor, key: TypeKey) =
-        withCycleDetection(key, constructor) {
-            TypeResult.Constructor(
-                type = constructor.type,
-                parameters = resolveParams(context, constructor, constructor.parameters),
-                supportsNamedArguments = constructor.supportsNamedArguments
-            )
-        }
+    private fun Constructor(
+        context: Context,
+        constructor: AstConstructor,
+        scope: AstType?,
+        key: TypeKey
+    ) = withCycleDetection(key, constructor) {
+        TypeResult.Constructor(
+            type = constructor.type,
+            scope = scope,
+            parameters = resolveParams(context, constructor, scope, constructor.parameters),
+            supportsNamedArguments = constructor.supportsNamedArguments
+        )
+    }
 
     private fun Container(
         creator: String,
@@ -492,7 +502,12 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
         TypeResult.NamedFunction(
             name = function.toMemberName(),
             args = namedArgs.map { it.second },
-            parameters = resolveParams(context.withArgs(namedArgs), function, function.parameters),
+            parameters = resolveParams(
+                context = context.withArgs(namedArgs),
+                element = function,
+                scope = null,
+                params = function.parameters
+            ),
         )
     }
 


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 f4cef6d8..8d5a56fe 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
@@ -35,6 +35,7 @@ typealias receiverFun = String.(arg: NamedFoo) -> String
 fun String.receiverFun(dep: Foo, @Assisted arg: NamedFoo): String = this
 
 @Component
+@CustomScope
 abstract class ReceiverFunctionInjectionComponent {
     abstract val receiverFun: receiverFun
 }