google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.43k stars 2.01k forks source link

[2.30] NoSuchElementException in KotlinMetadata.java #2190

Closed tadfisher closed 3 years ago

tadfisher commented 3 years ago

Since updating to 2.30, compiling is failing during the kapt step. Here's the bottom of the stacktrace. Unfortunately I can't tell from this error where the problem occurs.

Caused by: java.util.NoSuchElementException
    at dagger.internal.codegen.extension.DaggerCollectors$ToOptionalState.getElement(DaggerCollectors.java:145)
    at dagger.internal.codegen.extension.DaggerCollectors.lambda$static$1(DaggerCollectors.java:61)
    at dagger.internal.codegen.kotlin.KotlinMetadata.findProperty(KotlinMetadata.java:260)
    at dagger.internal.codegen.kotlin.KotlinMetadata.lambda$mapFieldToAnnotationMethod$8(KotlinMetadata.java:226)
    at dagger.internal.codegen.kotlin.KotlinMetadata.mapFieldToAnnotationMethod(KotlinMetadata.java:222)
    at dagger.internal.codegen.kotlin.KotlinMetadata.lambda$new$4(KotlinMetadata.java:122)
    at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:167)
    at dagger.internal.codegen.kotlin.KotlinMetadata.isMissingSyntheticAnnotationMethod(KotlinMetadata.java:321)
    at dagger.internal.codegen.kotlin.KotlinMetadataUtil.isMissingSyntheticPropertyForAnnotations(KotlinMetadataUtil.java:77)
    at dagger.internal.codegen.validation.DependencyRequestValidator.checkQualifiers(DependencyRequestValidator.java:77)
    at dagger.internal.codegen.validation.DependencyRequestValidator.validateDependencyRequest(DependencyRequestValidator.java:68)
    at dagger.internal.codegen.validation.InjectValidator.validateDependencyRequest(InjectValidator.java:256)
    at dagger.internal.codegen.validation.InjectValidator.validateField(InjectValidator.java:212)
    at dagger.internal.codegen.validation.InjectValidator.validateMembersInjectionType(InjectValidator.java:268)
    at dagger.internal.codegen.validation.InjectBindingRegistryImpl.tryRegisterMembersInjectedType(InjectBindingRegistryImpl.java:281)
    at dagger.internal.codegen.validation.InjectBindingRegistryImpl.tryRegisterMembersInjectedType(InjectBindingRegistryImpl.java:265)
    at dagger.internal.codegen.InjectProcessingStep$1.visitVariableAsField(InjectProcessingStep.java:54)
    at dagger.internal.codegen.InjectProcessingStep$1.visitVariableAsField(InjectProcessingStep.java:44)
    at jdk.compiler/com.sun.tools.javac.code.Symbol$VarSymbol.accept(Symbol.java:1576)
    at dagger.internal.codegen.InjectProcessingStep.process(InjectProcessingStep.java:76)
    at dagger.internal.codegen.validation.TypeCheckingProcessingStep.lambda$process$0(TypeCheckingProcessingStep.java:51)
    at com.google.common.collect.RegularImmutableMap.forEach(RegularImmutableMap.java:185)
    at dagger.internal.codegen.validation.TypeCheckingProcessingStep.process(TypeCheckingProcessingStep.java:48)
    at dagger.internal.codegen.validation.TypeCheckingProcessingStep.process(TypeCheckingProcessingStep.java:34)
    at dagger.shaded.auto.common.BasicAnnotationProcessor$ProcessingStepAsStep.process(BasicAnnotationProcessor.java:495)
    at dagger.shaded.auto.common.BasicAnnotationProcessor.process(BasicAnnotationProcessor.java:228)
    at dagger.shaded.auto.common.BasicAnnotationProcessor.process(BasicAnnotationProcessor.java:208)
    at org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor.process(incrementalProcessors.kt)
    at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.process(annotationProcessing.kt:161)
    at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:980)
    ... 42 more
Hospes commented 3 years ago

The same for me

danysantiago commented 3 years ago

Thanks for reporting this.

What Kotlin version are you on? Do you also happen to have KAPT's correctErrorType turned ON?

The likely cause of this issue is a 'weird' (possibly a delegate) property in a member injected class for which its type is generated. Something like this:

class MyMemberInjectedClass {
  @Inject lateinit var foo: Foo
  val delegateProperty: GeneratedType by ...
}

For these cases we have a workaround to a KAPT bug that seems to not be working for all cases: https://github.com/google/dagger/blob/master/java/dagger/internal/codegen/kotlin/KotlinMetadata.java#L257

We might need a bit more time to hunt this issue down, I don't think we'll have a patch release until next week, sorry.

tadfisher commented 3 years ago

We are on Kotlin 1.4.10. We do have correctErrorTypes enabled, along with the following options:

correctErrorTypes = true
mapDiagnosticLocations = true
useBuildCache = true
kapt.include.compile.classpath=false

I will try with correctErrorTypes off.

danysantiago commented 3 years ago

This is a bit of a stretch too but you can find out the class with the 'faulty' property if you attach a debugger, (we really need to publish instructions on how to do this), but basically you run a very big Gradle command:

./gradlew clean app:kaptDebugKotlin --no-daemon -Dorg.gradle.debug=true -Dkotlin.compiler.execution.strategy="in-process" -Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=n"

Then in the IDE add a new run configuration of the 'Remote' category and when the command above start click the 'attach debug' icon. There are more instructions (with screenshots) in this comment.

If you put a non-suspending breakpoint in findProperty() that logs the type element (the class) being processed it can help us track down the Kotlin code that might be causing this.

tadfisher commented 3 years ago

Same stack trace with correctErrorTypes = false. I'll try running the debugger.

kozaxinan commented 3 years ago

Hi, ~I wasn't able to debug but~ I found which class causes the problem for us. With debug mode, I also verified problem is coming from an Activity class.

In our activity, we inject ViewModelFactory

@Inject internal lateinit var factory: ViewModelFactory<AbcViewModel>

Factory is used in a by viewModel delegation but removing delegation didn't have any effect.

Without ViewModelFactory<> injection, dagger doesn't throw NoSuchElementException.

ViewModelFactory class

class ViewModelFactory<out T : ViewModel> @Inject constructor(
  private val provider: Provider<T>
) : ViewModelProvider.Factory {

  @Suppress("UNCHECKED_CAST")
  override fun <T : ViewModel> create(modelClass: Class<T>): T = try {
    provider.get() as T
  } catch (cause: ClassCastException) {
    throw IllegalStateException(
      "Class ${modelClass.name} is not supported by this factory",
      cause
    )
  }
}
danysantiago commented 3 years ago

By any chance do you use Kotlin's Android extensions (apply plugin: 'kotlin-android-extensions')?

kozaxinan commented 3 years ago

Yes, our module has kotlin-android-extension. I noticed that in that module, we didnt setup Kotlin extensions just for parcelizer. After adding androidExtensions { features = ['parcelize'] }, build finished without NoSuchElementException.

GuilhE commented 3 years ago

Yes, our module has kotlin-android-extension. I noticed that in that module, we didnt setup Kotlin extensions just for parcelizer. After adding androidExtensions { features = ['parcelize'] }, build finished without NoSuchElementException.

I'll complement this answer with androidExtensions .kts version:

androidExtensions {
    features = mutableSetOf("parcelize")
}

Just a small not also: Warning: The 'kotlin-android-extensions' Gradle plugin is deprecated. Please use this migration guide (https://goo.gle/kotlin-android-extensions-deprecation) to start working with View Binding (https://developer.android.com/topic/libraries/view-binding) and the 'kotlin-parcelize' plugin.

sczerwinski commented 3 years ago

I'm using kotlin-android-extension plugin for getting views from layouts. So in my case, setting androidExtensions { features = ['parcelize'] } only "helped" for :app:kaptDebugKotlin, but now I'm getting errors in :app:compileDebugKotlin: Unresolved reference: synthetic.

When I set androidExtensions { features = ['views'] } instead, java.util.NoSuchElementException is back.

The error was not there in Dagger 2.29.1, it's just in 2.30.

I tried debugging, but no luck getting any additional information so far.

EDIT: Just saw @GuilhE's answer. Will try that out.

EDIT2: Replacing kotlin-android-extension with View Binding works.