KOIN-D001 false positive on DSL definitions that don't use the bound type's constructor (providerOnly ignored in full-graph validation)
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
Research direction
Start in CompileSafetyValidator.validateFullGraph and compare its allDefinitions handling with the providerOnly filter in CallSiteValidator's Phase 3.1 path. Add regression coverage for lambda and zero-argument create(::makeProbe) definitions; done means both compile without KOIN-D001 while the bound type remains a provider.
Written by the indexing model from the issue text.
Description
Summary
Since 1.1.0 made full-graph validation the sole verifier, a DSL definition whose instance is not built by the bound type's own constructor still has that constructor's parameters treated as dependencies.
The plugin already models this correctly — KoinDSLTransformer sets providerOnly = true on these definitions, and the Phase 3.1 DSL-graph path in CallSiteValidator filters them out with reachableDefs.filter { !(it is Definition.DslDef && it.providerOnly) } (~line 509). But CompileSafetyValidator.validateFullGraph does not apply that filter, so every constructor parameter of the bound type is reported as a missing dependency.
Because 1.1.0 removed per-module validation, the A3 path is now the only one that runs whenever a compilation has an entry point, and Phase 3.1 is skipped once A3 has populated assembledGraphTypes. So the code path that respects the flag appears to be unreachable for any compilation that has an entry point.
Environment
- Koin: 4.2.2
- Koin Compiler Plugin: 1.1.0
- Kotlin: 2.4.0 (a listed supported version)
- Gradle 9.6.1, JDK 21, Kotlin Multiplatform project, failure observed on the JVM target
koinCompiler { }otherwise at defaults (compileSafetydefaulttrue)
Reproduction
No external dependencies needed. In any compilation that contains a Koin entry point (startKoin / koinApplication / @KoinApplication) — in our case a koinApplication<TestApp> { } in a test source set — declare:
class Probe(val a: String, val b: Int)
private fun makeProbe(): Probe = Probe("x", 1)
private val probeModule = module {
single { Probe("x", 1) } // lambda builds it
single { create(::makeProbe) } // zero-arg factory function
}
Actual
e: [Koin][KOIN-D001] Missing dependency: kotlin.String
required by: dsl:Probe (parameter 'a')
in module: probeModule
e: [Koin][KOIN-D001] Missing dependency: kotlin.Int
required by: dsl:Probe (parameter 'b')
in module: probeModule
Expected
No diagnostic. Neither definition resolves a or b from the container — the first constructs Probe inside its own lambda, and the second delegates to a factory function that takes no parameters. Koin never invokes Probe's constructor itself in either case, and both resolve correctly at runtime.
create(::fn) does not work around it
This is the difference from #36. That issue was about definitions not being recognised as providers (single<Vertx> { vertx } reporting Vertx itself missing), and create(::provideVertx) was the accepted answer — it's also what the docs recommend for building an instance from something other than its own constructor.
Here the definition is recognised as a provider (the bound type itself is never reported missing); it is additionally treated as a consumer of its own constructor. create(::makeProbe) with a zero-arg function produces the identical error, so requirements appear to be read from the bound type rather than from the factory function's parameter list. Verified on 1.1.0 with both forms.
Real-world impact
Our case is a test fixture overriding a builder with an in-memory one:
private val inMemoryDatabaseModule = module {
single<RoomDatabase.Builder<LedgerDatabase>> { Room.inMemoryDatabaseBuilder<LedgerDatabase>() }
}
→ Missing dependency: kotlin.reflect.KClass … (parameter 'klass') and kotlin.Function0 … (parameter 'factory') — RoomDatabase.Builder's own constructor parameters, which no application should ever provide. The tests pass at runtime; only the compile fails.
Any framework or third-party type with a public constructor that you bind via a hand-written lambda hits this: builders, ObjectMapper, Vertx, an SDK client, a test double. Interfaces are unaffected (no constructor to walk), which is why it can look sporadic.
The only available escape is koinCompiler { compileSafety = false }, and since that setting is per-Gradle-project it also disables validation for the module's main source set — the same complaint raised in #58. That is a heavy price for a test-only fixture.
Possible fix
Apply the existing providerOnly filter in CompileSafetyValidator.validateFullGraph (or when building allDefinitions) so it matches the Phase 3.1 path. A providerOnly definition should still be registered as a provider of its bound type — it just shouldn't contribute requirements.
Related
- #36 — same family (DSL definitions not built by their own constructor), closed in 1.0.2; the
create(::fn)remedy accepted there does not cover this case. - #58 — different root cause, but the same end-user situation: false
KOIN-D001in a test source set, withcompileSafety = falsebeing too blunt a workaround. - #74 — unrelated (
@InjectedParam/parametersOf).
Thanks for 1.1.0 — the entry-point-only model fixed a real cross-module false positive for us, and the improved KOIN-D001 messages (owning module + file:line) are what made this one diagnosable.
- Dominant language
- Kotlin
- Stars
- 82
- Forks
- 15
- Avg merge
- 1m
- Merged PRs (30d)
- 1
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from InsertKoinIO/koin-compiler-plugin
-
type:improvement
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 4/5 3-5 days Newbie friendliness 52/100
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
InsertKoinIO/koin-compiler-plugin#110 · 1 reaction ·
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
-
Difficulty 4/5 3-5 days Newbie friendliness 52/100
InsertKoinIO/koin-compiler-plugin#107 · 2 reactions ·
All issues in InsertKoinIO/koin-compiler-plugin
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Good First Issue
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
ankidroid/Anki-Android#21942 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
libre-tube/LibreTube#8781 · 1 comment ·
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
MetrolistGroup/Metrolist#4396 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100