square / anvil

A Kotlin compiler plugin to make dependency injection with Dagger 2 easier.
Apache License 2.0
1.31k stars 80 forks source link

Unresolved Reference in IDE For Bindings Added Via @ContributesTo #976

Closed ZianeA closed 5 months ago

ZianeA commented 5 months ago

When using @ContributesTo to expose bindings from a component, Android Studio shows "Unresolved reference" when accessing the bindings. However, the app compiles and runs without any issues.

@MergeComponent(AppScope::class)
interface AppComponent

@ContributesTo(AppScope::class)
interface NameProviderBinding {
    val nameProvider: NameProvider
}

(application as AnvilApplication).appComponent.nameProvider.name
Screenshot 2024-04-20 at 20 56 12

Versions: dagger = "2.51.1" anvil = "2.4.9" AGP = "8.3.2"

RBusarow commented 5 months ago

This is expected behavior, and a limitation of the IDE.

The interface merging process affects the .class files and bytecode, but doesn't alter any source code. The IDE only uses source code when resolving references, so from its perspective that property does not exist in the AppComponent declaration.

If you need to reference a property from a merged component in this fashion, you'll probably want to cast it for the sake of the IDE:

val component = (application as AnvilApplication).appComponent
(component as NameProviderBinding).nameProvider

But of course the whole point of Dagger is that you can use constructor injection instead of this pattern.

ZianeA commented 5 months ago

Thank you for clarifying!

But of course the whole point of Dagger is that you can use constructor injection instead of this pattern.

Unfortunately, constructor injection is not possible for Android components like Activities or Fragments.

I tried member injection but its the same issue:

@ContributesTo(AppScope::class)
interface ActivityBindings {
    fun inject(activity: MainActivity)
}
@Inject
lateinit var nameProvider: NameProvider

override fun onCreate(savedInstanceState: Bundle?) {
    (application as AnvilApplication).appComponent.inject(this)
}
ZianeA commented 5 months ago

From what I understand, Anvil uses backend IR plugin to add supertypes. Since interface merging is a change visible to the user (accessing functions and properties), could this be handled at the FIR level?