Repository: evant/kotlin-inject (kotlin)

Issue:
Assisted injection + scoped dependency cache one instance forever
If I have something like this set up:

```kotlin
@CarScope
@Component
abstract class CarComponent {
  abstract val carRegistry: (districtId: String) -> CarRegistry
}

@CarScope
@Inject
class CarRegistry(@Assisted districtId: String)

fun main() {
  val component = CarComponent::class.create()
  val r1 = component.carRegistry("1")
  val r2 = component.carRegistry("2")

  r1 == r2 // -> true
}
```

From what I see in currently generated code for this setup, repeatedly calling `carRegistry` factory with different arguments will return a same instance each time, because the code looks this:

```kotlin
public override val carRegistry: Function1<String, CarRegistry>
  get() = { arg0 ->
    _scoped.get("com.example.CarRegistry") {
      CarRegistry(
        arg = arg0
      )
    }
  }
```

I would expect this to be implemented in such a way so that `LazyMap` key would include assisted args somehow, and so that calling with same args always would produce the same instance.

I'm not sure if this feature of having scoping based on assisted args makes sense and also not quite sure how Dagger would work in this case, but at least having current behavior looks a bit strange, perhaps a warning or an error should be given?

Pull request:
These features don't really play well together because you'd expect the returned type to be always based on the assisted args.

Fixes #262

Patch:
diff --git a/.static/detekt-config.yml b/.static/detekt-config.yml
index ea6271e5..6bcc71ed 100644
--- a/.static/detekt-config.yml
+++ b/.static/detekt-config.yml
@@ -145,6 +145,8 @@ style:
     active: true
   UseRequireNotNull:
     active: true
+  ThrowsCount:
+    active: false
 
 coroutines:
   active: true
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 b7d87570..2d6ccab6 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
@@ -77,6 +77,14 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
         element: AstElement,
         params: List<AstParam>,
     ): Map<String, TypeResultRef> {
+        if (context.scopeComponent != null) {
+            val scopeType = context.scopeComponent.scopeType(options)
+            throw FailedToGenerateException(
+                "Cannot apply scope: @${scopeType!!.simpleName} to type with @Assisted parameters: [${
+                    params.filter { it.isAssisted() }.joinToString()
+                }]"
+            )
+        }
         val paramsWithName = LinkedHashMap<String, TypeResultRef>(context.args.size)
         var assistedFailed = false
         val args = context.args.toMutableList()
@@ -163,6 +171,14 @@ class TypeResultResolver(private val provider: AstProvider, private val options:
                 """.trimIndent(),
                 element
             )
+            if (context.scopeComponent != null) {
+                val scopeType = context.scopeComponent.scopeType(options)
+                throw FailedToGenerateException(
+                    "Cannot apply scope: @${scopeType!!.simpleName} to type with assisted parameters: [${
+                        resolvedImplicitly.joinToString()
+                    }]"
+                )
+            }
         }
 
         return paramsWithName


Test patch:
diff --git a/kotlin-inject-compiler/test/src/test/kotlin/me/tatarka/inject/test/FailureTest.kt b/kotlin-inject-compiler/test/src/test/kotlin/me/tatarka/inject/test/FailureTest.kt
index 0ce157e4..8d203aa8 100644
--- a/kotlin-inject-compiler/test/src/test/kotlin/me/tatarka/inject/test/FailureTest.kt
+++ b/kotlin-inject-compiler/test/src/test/kotlin/me/tatarka/inject/test/FailureTest.kt
@@ -761,4 +761,31 @@ class FailureTest {
             contains("But got:  []")
         }
     }
+
+    @ParameterizedTest
+    @EnumSource(Target::class)
+    fun fails_if_assisted_class_has_scope(target: Target) {
+        val projectCompiler = ProjectCompiler(target, workingDir)
+
+        assertFailure {
+            projectCompiler.source(
+                "MyComponent.kt",
+                """
+                import me.tatarka.inject.annotations.Component
+                import me.tatarka.inject.annotations.Scope
+                import me.tatarka.inject.annotations.Assisted
+                import me.tatarka.inject.annotations.Inject
+                
+                @Scope annotation class MyScope
+                @MyScope @Inject class Foo(@Assisted val arg: String)
+                
+                @MyScope @Component abstract class MyComponent {
+                    abstract val foo: (String) -> Foo
+                }
+                """.trimIndent()
+            ).compile()
+        }.output().all {
+            contains("Cannot apply scope: @MyScope to type with @Assisted parameters: [arg: String]")
+        }
+    }
 }
\ No newline at end of file