Dagger: Add readable compiler error when trying to inject generics

Created on 24 Oct 2017  路  8Comments  路  Source: google/dagger

I'm getting an _IllegalArgumentException_ for my Android Kotlin project.

Unfortunately there's no error message or warning from the Dagger annotation processor.

Dagger related stack trace:

Caused by: java.lang.IllegalArgumentException
    at com.google.common.base.Preconditions.checkArgument(Preconditions.java:108)
    at dagger.internal.codegen.InjectBindingRegistry.getOrFindMembersInjectionBinding(InjectBindingRegistry.java:316)
    at dagger.internal.codegen.BindingGraph$Factory$Resolver.lookUpBindings(BindingGraph.java:561)
    at dagger.internal.codegen.BindingGraph$Factory$Resolver.resolve(BindingGraph.java:979)
    at dagger.internal.codegen.BindingGraph$Factory.create(BindingGraph.java:319)
    at dagger.internal.codegen.BindingGraph$Factory.create(BindingGraph.java:235)
    at dagger.internal.codegen.ComponentProcessingStep.process(ComponentProcessingStep.java:137)
    at dagger.internal.codegen.ComponentProcessingStep.process(ComponentProcessingStep.java:47)
    at dagger.shaded.auto.common.BasicAnnotationProcessor.process(BasicAnnotationProcessor.java:329)
    at dagger.shaded.auto.common.BasicAnnotationProcessor.process(BasicAnnotationProcessor.java:182)


Full stack trace:

java.lang.IllegalStateException: failed to analyze: java.lang.IllegalArgumentException
    at org.jetbrains.kotlin.analyzer.AnalysisResult.throwIfError(AnalysisResult.kt:57)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules(KotlinToJVMBytecodeCompiler.kt:138)
    at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:154)
    at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:58)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:103)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.java:51)
    at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:92)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$1$2.invoke(CompileServiceImpl.kt:386)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl$compile$1$2.invoke(CompileServiceImpl.kt:96)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:889)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl$doCompile$$inlined$ifAlive$lambda$2.invoke(CompileServiceImpl.kt:96)
    at org.jetbrains.kotlin.daemon.common.DummyProfiler.withMeasure(PerfUtils.kt:137)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl.checkedCompile(CompileServiceImpl.kt:916)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl.doCompile(CompileServiceImpl.kt:888)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:385)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:346)
    at sun.rmi.transport.Transport$1.run(Transport.java:200)
    at sun.rmi.transport.Transport$1.run(Transport.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException
    at com.google.common.base.Preconditions.checkArgument(Preconditions.java:108)
    at dagger.internal.codegen.InjectBindingRegistry.getOrFindMembersInjectionBinding(InjectBindingRegistry.java:316)
    at dagger.internal.codegen.BindingGraph$Factory$Resolver.lookUpBindings(BindingGraph.java:561)
    at dagger.internal.codegen.BindingGraph$Factory$Resolver.resolve(BindingGraph.java:979)
    at dagger.internal.codegen.BindingGraph$Factory.create(BindingGraph.java:319)
    at dagger.internal.codegen.BindingGraph$Factory.create(BindingGraph.java:235)
    at dagger.internal.codegen.ComponentProcessingStep.process(ComponentProcessingStep.java:137)
    at dagger.internal.codegen.ComponentProcessingStep.process(ComponentProcessingStep.java:47)
    at dagger.shaded.auto.common.BasicAnnotationProcessor.process(BasicAnnotationProcessor.java:329)
    at dagger.shaded.auto.common.BasicAnnotationProcessor.process(BasicAnnotationProcessor.java:182)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
    at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
    at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1068)
    at org.jetbrains.kotlin.kapt3.AnnotationProcessingKt.doAnnotationProcessing(annotationProcessing.kt:73)
    at org.jetbrains.kotlin.kapt3.AnnotationProcessingKt.doAnnotationProcessing$default(annotationProcessing.kt:42)
    at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.runAnnotationProcessing(Kapt3Extension.kt:205)
    at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.analysisCompleted(Kapt3Extension.kt:166)
    at org.jetbrains.kotlin.kapt3.ClasspathBasedKapt3Extension.analysisCompleted(Kapt3Extension.kt:82)
    at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM$analyzeFilesWithJavaIntegration$2.invoke(TopDownAnalyzerFacadeForJVM.kt:96)
    at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:106)
    at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:83)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:377)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:68)
    at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:96)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:368)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules(KotlinToJVMBytecodeCompiler.kt:133)
    ... 30 more

I've tried to find out the cause of the error looking at Daggers source but couldn't quite find an issue.
Seems like there's an invalid members injection key, even though I don't know what that means:

https://github.com/google/dagger/blob/5b604719cacf0c4aec28f910192afce6ad84a7b3/java/dagger/internal/codegen/InjectBindingRegistry.java#L316

Most helpful comment

Android prevents the use of constructor injection on Activities, Fragments, Services, etc, which is why we have the dagger.android APIs. If you're not on Android, regular construction injection is prefered.

All 8 comments

It probably means that somewhere you have an inject(MyType<?> myType) method, or some type that isn't valid. It's bad that we don't tell you what type it is at least.

Can you look in your code to see if you see something like that? Perhaps it's a kotlin issue, and you have a fun inject(MyType<String> myTypeOfString) and kotlin is translating that to inject(MyType<? extends String> mtos);

Yep thanks a lot! 馃憤馃槃

Actually I've had this code in my app component:

/**
 * Inject this component's dependencies into an [Application].
 */
fun <ApplicationType: Application> inject(application: ApplicationType): ApplicationType

... and also this in my base activity component:

/**
 * Inject this component's dependencies into an [Activity].
 */
fun <ActivityType: Activity> inject(activity: ActivityType): ActivityType

... and also this in my base fragment component:

/**
 * Inject this component's dependencies into a [Fragment].
 */
fun <FragmentType: Fragment> inject(fragment: FragmentType): FragmentType

Naively I thought it would be good to have some sort of base component interface that the individual components for each activity/fragment could extend to encapsulate behavior. That would make it possible to expose an Activity instance in the BaseActivityComponent so that I don't write the same expose function in every "sub-interface" of BaseActivityComponent.
So if I have activities FooActivity and BarActivity which both extend BaseActivity I would like to have the corresponding components defined like this:

@Component(dependencies = arrayOf(...), modules = arrayOf(...))
interface BaseActivityComponent {
    fun <ActivityType: Activity> inject(activity: ActivityType): ActivityType
    fun exposeActivity(): Activity
}

@Component(dependencies = arrayOf(..., [more dependencies]), modules = arrayOf(..., [more modules]))
interface FooActivityComponent : BaseActivityComponent {
    fun inject(activity: FooActivity): FooActivity
    fun exposeFoo(): FooSomething
    ...
}

@Component(dependencies = arrayOf(..., [more dependencies]), modules = arrayOf(..., [more modules]))
interface FooActivityComponent : BaseActivityComponent {
    fun inject(activity: FooActivity): FooActivity
    fun exposeBar(): BarSomething
    ...
}

Is there any meaningful way to implement something like that?
(I'm new to Dagger and dependency injection so I might misunderstand some principles 馃檲)

Anyway, Dagger should have a readable compiler error for the stack trace above.

If I just remove the inject function from BaseActivityComponent it works, but I lose the ability to call the inject method from my BaseActivity where I bundle activity-related boilerplate code. 馃

Currently in my abstract BaseActivity<ComponentType : BaseActivityComponent> there's an abstract fun onCreateComponent(): ComponentType and internally the code in BaseActivity is something like this:

protected val component by lazy { onCreateComponent() }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    component.inject(this)
}

That way I only have to override onCreateComponent() in the other activities.

Take a look at google.github.io/dagger/android. It may have all of what you want.

I see 馃槂
From pure interest: what would be the correct way if I weren't developing on Android?

Android prevents the use of constructor injection on Activities, Fragments, Services, etc, which is why we have the dagger.android APIs. If you're not on Android, regular construction injection is prefered.

I added a better error message in this commit: 71d0ad1578a314158f34330191b8296978e477d1

Should be out in 2.14

Perfect! Thanks a lot! 馃憤馃巻

Was this page helpful?
0 / 5 - 0 ratings