Skip to content

Fix 2325 - CoreResolution with Child Scopes / ViewModelScope#2340

Merged
arnaudgiuliani merged 5 commits into4.2.0from
fix_2325
Jan 14, 2026
Merged

Fix 2325 - CoreResolution with Child Scopes / ViewModelScope#2340
arnaudgiuliani merged 5 commits into4.2.0from
fix_2325

Conversation

@arnaudgiuliani
Copy link
Copy Markdown
Member

Fix #2325 - Scope Resolution + ViewModelScope Scope Leak Fix

Summary

Fixes scope resolution leak where ViewModels at root scope could incorrectly access viewModelScope { } dependencies. CoreResolverV2 is now stricter. Also impacting previously other scope access.

Breaking Change (Experimental API)

  // ❌ No longer works (was leaking)
  module {
      viewModel { MyVM(get()) }
      viewModelScope { scoped { Session() } }
  }

  // ✅ Required pattern
  viewModelScope {
      viewModel { MyVM(get()) }
      scoped { Session() }
  }

Changes

  • CoreResolverV2: Refactored into clear methods (resolveDirectDefinition, resolveFromScopeArchetype, resolveFromScopeSource, resolveFromLinkedScopes)
  • @KoinViewModelScopeApi: New opt-in annotation (WARNING level) on viewModelScope() DSL and viewModelScopeFactory()
  • Error messages: Include scope info for debugging
  • Documentation: Updated KDoc with correct usage patterns

Files Changed

  • CoreResolverV2.kt - Stricter resolution logic
  • KoinAnnotations.kt - New @KoinViewModelScopeApi
  • KoinOption.kt - Annotated viewModelScopeFactory()
  • ViewModelScopeArchetypeDSL.kt - Annotated + improved KDoc
  • ViewModelScopeArchetypeTest.kt - Updated to correct pattern

Impact

  • No impact if viewModelScopeFactory() not used
  • No impact if only using manual scope (KoinScopeComponent)
  • Breaking only for constructor injection with scoped deps declared outside viewModelScope { }

@arnaudgiuliani arnaudgiuliani added this to the 4.2.0 milestone Jan 14, 2026
@arnaudgiuliani arnaudgiuliani changed the title Fix 2325 Fix 2325 - CoreResolution with Child Scopes / ViewModelScope Jan 14, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a scope resolution leak where ViewModels declared at root scope could incorrectly access dependencies defined in viewModelScope { }. The fix introduces stricter scope resolution logic and adds proper annotations and documentation to guide developers toward the correct usage pattern.

Changes:

  • Refactored CoreResolverV2 into clearly separated resolution methods for better maintainability
  • Added @KoinViewModelScopeApi opt-in annotation with WARNING level to enforce correct usage patterns
  • Improved error messages to include scope information for easier debugging

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
projects/gradle.properties Version bump to 4.2.0-beta3-rc6
projects/core/koin-core/src/commonTest/kotlin/org/koin/core/ScopeTest.kt Added @BeforeTest cleanup to prevent test pollution
projects/core/koin-core/src/commonTest/kotlin/org/koin/core/ScopeInfoTest.kt Fixed parameter resolution to use explicit injected parameter context
projects/core/koin-core/src/commonTest/kotlin/org/koin/core/ScopeExampleTest.kt Added comprehensive test case for issue #2325 demonstrating scope resolution
projects/core/koin-core/src/commonMain/kotlin/org/koin/core/resolution/CoreResolverV2.kt Refactored resolver into separate methods and improved error messages with scope info
projects/core/koin-core/src/commonMain/kotlin/org/koin/core/option/KoinOption.kt Annotated viewModelScopeFactory with @KoinViewModelScopeApi and improved documentation
projects/core/koin-core/src/commonMain/kotlin/org/koin/core/annotation/KoinAnnotations.kt Added new @KoinViewModelScopeApi annotation for guarding experimental ViewModel scope API
projects/core/koin-core-viewmodel/src/commonMain/kotlin/org/koin/viewmodel/scope/ViewModelScopeArchetypeDSL.kt Enhanced documentation with usage examples and added opt-in annotation
projects/core/koin-annotations/api/koin-annotations.api Added public API definition for annotations module
projects/android/koin-android/src/test/java/org/koin/test/android/scope/ViewModelScopeArchetypeTest.kt Updated test to follow correct pattern with ViewModel declared inside viewModelScope block
examples/gradle/versions.gradle Updated Kotlin and Koin versions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +62 to 63
@OptIn(KoinExperimentalAPI::class)
class ViewModelScopeArchetypeTest {
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing @OptIn(KoinViewModelScopeApi::class) annotation. The viewModelScope function is annotated with @KoinViewModelScopeApi, which requires opt-in. The class-level @OptIn(KoinExperimentalAPI::class) doesn't cover this requirement.

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +37
@Test
fun test_issue_2325() {
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test cleanup. The test uses startKoin but doesn't have an @AfterTest to call stopKoin(). This could cause state pollution between tests. Add an @AfterTest method to clean up Koin state, similar to other test classes like ObjectScopeTest and ScopeTest.

Copilot uses AI. Check for mistakes.
Comment on lines +93 to +94
val foundScope = resolvedScope ?: return null
if (factory == null) return null
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The null check for factory is redundant. If factory is null, then resolvedScope will also be null (since resolvedScope is only assigned when definition is not null). The check on line 93 already handles this case by returning null when foundScope is null.

Copilot uses AI. Check for mistakes.
koin.createScope("id", named("name"))

val ucOnScoped = koin.get<UseCaseOnScopedRepo>()
assertEquals("single",ucOnScoped.scopedRepo.singleApi.client.name)
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after comma in assertEquals. Should be assertEquals("single", ucOnScoped.scopedRepo.singleApi.client.name) for consistency with Kotlin style conventions.

Copilot uses AI. Check for mistakes.

val ucOnScoped = koin.get<UseCaseOnScopedRepo>()
assertEquals("single",ucOnScoped.scopedRepo.singleApi.client.name)
assertEquals("scoped",ucOnScoped.scopedRepo.scopedApi.client.name)
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after comma in assertEquals. Should be assertEquals("scoped", ucOnScoped.scopedRepo.scopedApi.client.name) for consistency with Kotlin style conventions.

Copilot uses AI. Check for mistakes.
assertEquals("scoped",ucOnScoped.scopedRepo.scopedApi.client.name)

val ucOnSingle = koin.get<UseCaseOnSingleRepo>()
assertEquals("single",ucOnSingle.singleRepo.singleApi.client.name)
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after comma in assertEquals. Should be assertEquals("single", ucOnSingle.singleRepo.singleApi.client.name) for consistency with Kotlin style conventions.

Copilot uses AI. Check for mistakes.
@arnaudgiuliani arnaudgiuliani merged commit 02f8b78 into 4.2.0 Jan 14, 2026
9 of 10 checks passed
@arnaudgiuliani arnaudgiuliani deleted the fix_2325 branch January 14, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants