google/dagger

[issue] Dagger doesn't compile or doesn't give a right error message when ViewModelFactory is implemened on Kotlin rather than on Java

Gelassen opened this issue · 4 comments

The issue

I observe some bizarre behavior of dagger when some of my dagger annotated classes are implemented on kotlin rather than on java. In my current case it is just hide a correct error message, in others cases it just doesn't compile, but everything compiles well when return back kotlin code to java code.

I can not give a code sample to reproduce (just prepared a WIP commit and new branch to reproduce the issue, see DI package), but it happens at least three times to report this. Unfortunately I did invest enough investigation time to speed up a development, so I just keep some classes on a Java.

@Module
public abstract class TestViewModelModule {

    @Binds
    public abstract ViewModelFactory bindViewModelFactory(ViewModelFactory viewModelFactory);

    @Binds
    @IntoMap
    @ViewModelKey(MachinesViewModel.class)
    abstract MachinesViewModel bindMachinesViewModel(MachinesViewModel vm);
}

Hi @Gelassen , I tried the branch and it did build. I tried converting TestViewModelModule to Kotlin and it builds as well. Could you share the error message you saw and how I can reproduce this?

Hello @kuanyingchou,
After I have switched from my dev branch to issue branch I get an exception:

java.nio.file.FileAlreadyExistsException: /home/gelassen/Workspace/Personal/manufacturer/manufactory-knowledge-management-system/mobile/app/build/generated/ksp/debug/java/io/github/gelassen/manufactory_knowledge_management/di/ViewModelFactory_Factory.java
	at java.base/sun.nio.fs.UnixCopyFile.copy(UnixCopyFile.java:573)
	at java.base/sun.nio.fs.UnixFileSystemProvider.copy(UnixFileSystemProvider.java:257)
	at java.base/java.nio.file.Files.copy(Files.java:1305)
	at com.google.devtools.ksp.IncrementalUtilKt.copyWithTimestamp(IncrementalUtil.kt:80)
	at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.updateFromShadow(KotlinSymbolProcessingExtension.kt:473)
	at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:371)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:112)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:77)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:256)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:247)
	at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:115)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:247)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.repeatAnalysisIfNeeded(KotlinToJVMBytecodeCompiler.kt:181)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.repeatAnalysisIfNeeded(KotlinToJVMBytecodeCompiler.kt:181)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:87)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:43)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:165)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:50)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:104)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:48)
	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1523)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
	at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)

Running $ ./gradlew clean and compile again solves this issue. Ksp stage is alpha, so it might be related to a lack of full support of ksp in dagger. Please consider to automate run of ./gradlew clean when developer switch from one branch to another. From dagger perspective it might be not possible, but they same company develop both dagger and its dev environment, so it just requires organizational and product decisions, therefore it is possible. [feature-request]

I am able to compile this code because TestViewModelModule is added in AppComponent and it is written on pure Java. If you replace TestViewModelModule by ViewModelModule which is written on kotlin, you will get on compilation:

[ksp] /home/gelassen/Workspace/Personal/manufacturer/manufactory-knowledge-management-system/mobile/app/src/main/java/io/github/gelassen/manufactory_knowledge_management/di/AppComponent.kt:17: [Dagger/MissingBinding] @javax.inject.Named("MACHINE_VIEW_MODEL_FACTORY") io.github.gelassen.manufactory_knowledge_management.di.ViewModelFactory cannot be provided without an @Provides-annotated method.

    @javax.inject.Named("MACHINE_VIEW_MODEL_FACTORY") io.github.gelassen.manufactory_knowledge_management.di.ViewModelFactory is injected at
        [io.github.gelassen.manufactory_knowledge_management.di.InjectorModule_ProvideMachineFragmentInjector.MachineFragmentSubcomponent] io.github.gelassen.manufactory_knowledge_management.ui.MachineFragment.viewModelFactory
    io.github.gelassen.manufactory_knowledge_management.ui.MachineFragment is injected at
        [io.github.gelassen.manufactory_knowledge_management.di.InjectorModule_ProvideMachineFragmentInjector.MachineFragmentSubcomponent] dagger.android.AndroidInjector.inject(T) [io.github.gelassen.manufactory_knowledge_management.di.AppComponent → io.github.gelassen.manufactory_knowledge_management.di.InjectorModule_ProvideMachineFragmentInjector.MachineFragmentSubcomponent]

Steps to reproduce are:

  1. $ git clone https://github.com/Gelassen/manufactory-knowledge-management-system.git
  2. $ git checkout issue/google-issue-ticket
  3. Go to AppComponent and replace TestViewModelModule by ViewModelModule
  4. Compile

Expected behavior:
App is successfully compiled and launched (screen with camera asking you for permissions)

Observed behavior:
App doesn't compile successfully with an error message described above

Hi, @Gelassen , thanks for the steps. I was able to reproduce the error. I think it's because the return type of bindViewModelFactory in ViewModelModule.kt is different from TestViewModelModule.java. After making them the same I was able to compile:

-    abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
+    abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelFactory

As for the branch changing issue I think it's outside Dagger's scope. Maybe a Git hook is better for the job.

@kuanyingchou , thank you for invested time into this and spotted issue in my codebase. Glad to hear the issue is on my side.

Also thank you for Git hook recommendation https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks, so far I don't have necessity to use it and be familiar with it.

The issue can be closed, but if you have a chance to be familiar with this codebase and have deep expertise in dagger could you please to take a look on this issue?

This is out of the scope, but it is just about asking a favor.