question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unresolved type error for a class in transitive dependency

See original GitHub issue

Dagger 2.41

Repro project: https://github.com/lwasyl/dagger-transitive-dependencies/tree/2634c3b3fb316fc877fc0e0919e79e36946efc31

If you run ./gradlew presentation:assemble, the build will fail with a message that it can’t resolve ApiModel. If you change Dagger version to 2.40.5 in settings.gradle, the same command will succeed.

I see in the changelog more strict validation for unresolved annotations, but nothing for types. In this repro, there are no scopes or qualifiers, so I don’t know if the change is expected or not

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:3
  • Comments:14

github_iconTop GitHub Comments

2reactions
bcorsocommented, Apr 13, 2022

@lwasyl, @GianfrancoMS just wanted to give an update.

TL;DR

Unfortunately, I don’t think we’ll be able to make the proposed changes, as they are more problematic than I initially thought.

As it turns out, even if we relax Dagger’s validation javac ends up failing conditionally depending on how exactly we implement the generated code (see details below). This makes our generated code fragile to otherwise benign changes to what should be implementation details. It also means that toggling on/off different build flags that change the internal implementation, e.g. fastInit mode, can cause unexpected breakages. Thus, while this may have worked okay for you in the past, it’s fragile and there’s no guarantee that some future change to our generated code won’t accidentally break you.

I do list some workarounds on our side that could likely avoid these issues (see below), but given that it adds complexity to our code base, reduces the readability of our generated code, and likely requires a lot more time/effort we’re going to continue to validate the types for these case. Hopefully all of this makes sense and seems reasonable to you, but let me know if not.

Javac Issues

As an example of how javac can conditionally fail based on our generated code, I’ll use CoreInterface from the sample app you linked.

Before looking at each scenario, it’s important to understand that Dagger generates a factory class for each binding that contains both a static create() method and a static newInstance() method that our generated code uses to create either an instance of the factory (e.g. Foo_Factory) or type (e.g. Foo) respectively.

Scenario 1

In some cases we satisfy the request for a binding using Factory#newInstance(), e.g.

@Override
public CoreInterface getCoreInterface() {
  return CoreInterfaceImpl_Factory.newInstance(
      CoreModule_internalCoreInterfaceFactory.newInstance());
}

This scenario fails javac because the call to CoreModule_internalCoreInterfaceFactory.newInstance() returns InternalCoreInterface<ApiModel> which references ApiModel (which is not on the classpath). Note that this happens even though we don’t explicitly reference ApiModel anywhere in the generated code. Javac will fail with an error like:

error: cannot access ApiModel
    return CoreInterfaceImpl_Factory.newInstance(
       CoreModule_internalCoreInterfaceFactory.newInstance());

  class file for ApiModel not found

Scenario 2

In other cases, we need to use a Provider to satisfy the request using Factory.create(), e.g.

private Provider<CoreInterface> coreInterfaceProvider;

void initialize() {
  coreInterfaceProvider =
      CoreInterfaceImpl_Factory.create(CoreModule_internalCoreInterfaceFactory.create());
}

@Override
public CoreInterface getCoreInterface() {
  return coreInterfaceProvider.get();
}

In this case javac does not fail. It turns out that we get a bit lucky because the CoreModule_internalCoreInterfaceFactory.create() returns the subclass type CoreModule_internalCoreInterfaceFactory rather than its supertype, Provider<InternalCoreInterface<ApiModel>>, which would have caused javac to fail due to the reference to ApiModel.

Workarounds

One workaround to this issue is that we could change Foo_Factory#newInstance() to always return Object rather than Foo to avoid javac from having to know about the type when compiling the generated component. Unfortunately, we would have to do this for every binding since we can’t know apriori which classes will not be on the classpath. This also means that all of the input parameters for #newInstance() and #create() would also have to be Object, which would add a lot more casts to the generated code. We would also need some version skew logic to check whether the generated factory was from a new enough version of Dagger to include these changes.

2reactions
bcorsocommented, Apr 4, 2022

@lwasyl that’s a good point, and we already do the processing for the @Binds within the Gradle module that defines it, so I agree we should be able to skip the check for @Binds when processing the component. I’ll include this in the change.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Maven - Transitive dependencies are not resolved for artifact ...
To successfully resolve transitive dependencies, project B's jar and pom.xml must be accessible in the Maven repository.
Read more >
IDE: "Cannot access class" false positive error with transitive ...
In a certain case, the Kotlin plugin seems to highlight errors of "Cannot access class" in the editor where they do not actually...
Read more >
AS BumbleBee "Unresolved Reference" in test code
Open the JVM unit test in AS, observe that IDE shows an "Unresolved reference" error. This works fine in Chipmunk with AGP 7.2.1....
Read more >
RE: Liferay 7, cannot resolve runtime dependency - Forums
Without doing any bnd.bnd modifications, when you deploy this module to OSGi you'll get the unresolved reference error because there is no other...
Read more >
Unrelated packages Unresolved requirement Import-Package ...
This is due to third-party jar transitive dependencies. If we use compileInclude scope for third-party dependencies for gradle build type module ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found