Skip to content

Commit

Permalink
Fix: Materialization/resolution inconsistency.
Browse files Browse the repository at this point in the history
If the alias is included into a super component, while another dependent alias
 is included into a sub, and the string representation of a list-binding is
 queried, which involves resolving the second alias in the sub - crash!

This CL fixes long existing graph building inconsistency -
 if an alias is included from the super-component, binding materialization
 continues locally after the alias resolution, not in the parent.

(cherry picked from commit 887fd3c)
  • Loading branch information
Fedor Ihnatkevich committed Sep 28, 2023
1 parent 13010c7 commit c140c30
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 164 deletions.
5 changes: 2 additions & 3 deletions core/graph/impl/src/main/kotlin/BindingGraphImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ internal class BindingGraphImpl(
override fun resolveBindingRaw(node: NodeModel): BaseBinding {
return bindings.getBindingFor(node)
?: parent?.resolveBindingRaw(node)
?: throw IllegalStateException("Not reached: missing binding for $node")
?: throw IllegalStateException("Not reached: missing binding for ${node.toString(null)}")
}

// MAYBE: write algorithm in such a way that this is local variable.
Expand Down Expand Up @@ -192,8 +192,7 @@ internal class BindingGraphImpl(
val nonAlias = binding.accept(AliasMaterializeVisitor())
if (nonAlias.owner == this) {
localBindings.getOrPut(nonAlias, ::BindingUsageImpl).accept(dependency.kind)
}
if (binding.owner == this) {

if (!seenBindings.add(nonAlias)) {
continue
}
Expand Down
38 changes: 38 additions & 0 deletions testing/tests/src/test/kotlin/CoreBindingsFailureTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,44 @@ class CoreBindingsFailureTest(
compileRunAndValidate()
}

@Test
fun `alias in super - alias in sub (bug #54)`() {
givenKotlinSource("test.TestCase", """
import com.yandex.yatagan.*
import javax.inject.*
@Scope annotation class SubScope
interface Api1
interface Api2
@SubScope class Api2Impl @Inject constructor() : Api2
@SubScope class Api1Consumer @Inject constructor(api: Api1)
class Api1Impl @Inject constructor(foo: Api2): Api1
@Module interface MyRootModule {
@Binds fun api(i: Api1Impl): Api1 // Alias in super
}
@Module interface MySubModule {
@Binds fun api2(i: Api2Impl): Api2 // Alias in sub
@Binds @IntoList fun objects1(i: Api1Consumer): Any
@Binds @IntoList fun objects2(i: Api1Consumer): Any // add twice
}
@Singleton @Component(modules = [MyRootModule::class])
interface MyRootComponent {
val sub: MySubComponent
}
@SubScope @Component(isRoot = false, modules = [MySubModule::class])
interface MySubComponent {
val objects: List<Any>
}
""".trimIndent())

// Should report missing binding for `Api2`, no crashes.
compileRunAndValidate()
}

@Test
fun `manual framework type usage`() {
givenKotlinSource("test.TestCase", """
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error: Missing binding for test.Api2
NOTE: No known way to infer the binding
Encountered:
in graph for root-component test.MyRootComponent
in graph for component test.MySubComponent
in entry-point getObjects: java.util.List<java.lang.Object>
^-[*1]--------------------------
in [1*] list-binding List { multi-binding-contributor java.lang.Object, } assembled in graph for component test.MySubComponent
^-[*2]------------------------------------
in [2*] alias test.MySubModule::objects2(test.Api1Consumer)
^-[*3]-----------
in [3*] inject-constructor test.Api1Consumer(test.Api1)
^-[*4]---
in [4*] alias test.MyRootModule::api(test.Api1Impl)
^-[*5]-------
in [5*] inject-constructor test.Api1Impl(test.Api2)
^-[*6]---
here: [6*] <missing>
^~~~~~~~~
Loading

0 comments on commit c140c30

Please sign in to comment.