diff --git a/java/dagger/internal/codegen/binding/BindingGraphFactory.java b/java/dagger/internal/codegen/binding/BindingGraphFactory.java index 37d7c499e23..da7b1a3f8e1 100644 --- a/java/dagger/internal/codegen/binding/BindingGraphFactory.java +++ b/java/dagger/internal/codegen/binding/BindingGraphFactory.java @@ -51,6 +51,7 @@ import dagger.internal.codegen.base.OptionalType; import dagger.internal.codegen.compileroption.CompilerOptions; import dagger.internal.codegen.javapoet.TypeNames; +import dagger.internal.codegen.model.BindingKind; import dagger.internal.codegen.model.ComponentPath; import dagger.internal.codegen.model.DaggerTypeElement; import dagger.internal.codegen.model.DependencyRequest; @@ -812,11 +813,17 @@ void resolve(Key key) { /* Resolve in the parent in case there are multibinding contributions or conflicts in some * component between this one and the previously-resolved one. */ parentResolver.get().resolve(key); - if (!new LocalDependencyChecker().dependsOnLocalBindings(key) - && getLocalExplicitBindings(key).isEmpty()) { + ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get(); + // TODO(b/305748522): Allow caching for assisted injection bindings. + boolean isAssistedInjectionBinding = + previouslyResolvedBindings.bindings().stream() + .anyMatch(binding -> binding.kind() == BindingKind.ASSISTED_INJECTION); + if (!isAssistedInjectionBinding + && !new LocalDependencyChecker().dependsOnLocalBindings(key) + && getLocalExplicitBindings(key).isEmpty()) { /* Cache the inherited parent component's bindings in case resolving at the parent found * bindings in some component between this one and the previously-resolved one. */ - resolvedContributionBindings.put(key, getPreviouslyResolvedBindings(key).get()); + resolvedContributionBindings.put(key, previouslyResolvedBindings); return; } } diff --git a/javatests/dagger/internal/codegen/AssistedFactoryTest.java b/javatests/dagger/internal/codegen/AssistedFactoryTest.java index 18de701a459..6f38f8f7d4d 100644 --- a/javatests/dagger/internal/codegen/AssistedFactoryTest.java +++ b/javatests/dagger/internal/codegen/AssistedFactoryTest.java @@ -326,4 +326,110 @@ public void testParameterizedAssistParam() throws Exception { subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); }); } + + // This is a regression test for b/305748522 + // The important thing for this test is that we have two assisted factories for the same assisted + // injection class and that they are requested in different components. + @Test + public void testMultipleAssistedFactoryInDifferentComponents() throws Exception { + Source component = + CompilerTests.javaSource( + "test.MyComponent", + "package test;", + "", + "import dagger.Component;", + "", + "@Component", + "interface MyComponent {", + " MyComponentAssistedFactory myComponentAssistedFactory();", + " MySubcomponent mySubcomponent();", + "}"); + Source subcomponent = + CompilerTests.javaSource( + "test.MySubcomponent", + "package test;", + "", + "import dagger.Subcomponent;", + "", + "@Subcomponent", + "interface MySubcomponent {", + " MySubcomponentAssistedFactory mySubcomponentAssistedFactory();", + "}"); + Source assistedClass = + CompilerTests.javaSource( + "test.MyAssistedClass", + "package test;", + "", + "import dagger.assisted.Assisted;", + "import dagger.assisted.AssistedInject;", + "", + "final class MyAssistedClass {", + " private final Foo foo;", + " private final Bar bar;", + "", + " @AssistedInject", + " MyAssistedClass(@Assisted Foo foo, Baz baz, @Assisted Bar bar) {", + " this.foo = foo;", + " this.bar = bar;", + " }", + "}"); + Source componentAssistedFactory = + CompilerTests.javaSource( + "test.MyComponentAssistedFactory", + "package test;", + "", + "import dagger.assisted.AssistedFactory;", + "", + "@AssistedFactory", + "interface MyComponentAssistedFactory {", + " MyAssistedClass create(Bar bar, Foo foo);", + "}"); + Source subcomponentAssistedFactory = + CompilerTests.javaSource( + "test.MySubcomponentAssistedFactory", + "package test;", + "", + "import dagger.assisted.AssistedFactory;", + "", + "@AssistedFactory", + "interface MySubcomponentAssistedFactory {", + " MyAssistedClass create(Bar bar, Foo foo);", + "}"); + Source foo = + CompilerTests.javaSource( + "test.Foo", + "package test;", + "final class Foo {}"); + Source bar = + CompilerTests.javaSource( + "test.Bar", + "package test;", + "final class Bar {}"); + Source baz = + CompilerTests.javaSource( + "test.Baz", + "package test;", + "", + "import javax.inject.Inject;", + "", + "final class Baz {", + " @Inject Baz() {}", + "}"); + + CompilerTests.daggerCompiler( + component, + subcomponent, + assistedClass, + componentAssistedFactory, + subcomponentAssistedFactory, + foo, + bar, + baz) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(0); + subject.generatedSource(goldenFileRule.goldenSource("test/DaggerMyComponent")); + }); + } } diff --git a/javatests/dagger/internal/codegen/goldens/AssistedFactoryTest_testMultipleAssistedFactoryInDifferentComponents_DEFAULT_MODE_test.DaggerMyComponent b/javatests/dagger/internal/codegen/goldens/AssistedFactoryTest_testMultipleAssistedFactoryInDifferentComponents_DEFAULT_MODE_test.DaggerMyComponent new file mode 100644 index 00000000000..4e5de7ad7d1 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/AssistedFactoryTest_testMultipleAssistedFactoryInDifferentComponents_DEFAULT_MODE_test.DaggerMyComponent @@ -0,0 +1,96 @@ +package test; + +import dagger.internal.DaggerGenerated; +import javax.annotation.processing.Generated; +import javax.inject.Provider; + +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava" +}) +final class DaggerMyComponent { + private DaggerMyComponent() { + } + + public static Builder builder() { + return new Builder(); + } + + public static MyComponent create() { + return new Builder().build(); + } + + static final class Builder { + private Builder() { + } + + public MyComponent build() { + return new MyComponentImpl(); + } + } + + private static final class MySubcomponentImpl implements MySubcomponent { + private final MyComponentImpl myComponentImpl; + + private final MySubcomponentImpl mySubcomponentImpl = this; + + private MyAssistedClass_Factory myAssistedClassProvider; + + private Provider mySubcomponentAssistedFactoryProvider; + + private MySubcomponentImpl(MyComponentImpl myComponentImpl) { + this.myComponentImpl = myComponentImpl; + + initialize(); + + } + + @SuppressWarnings("unchecked") + private void initialize() { + this.myAssistedClassProvider = MyAssistedClass_Factory.create(Baz_Factory.create()); + this.mySubcomponentAssistedFactoryProvider = MySubcomponentAssistedFactory_Impl.create(myAssistedClassProvider); + } + + @Override + public MySubcomponentAssistedFactory mySubcomponentAssistedFactory() { + return mySubcomponentAssistedFactoryProvider.get(); + } + } + + private static final class MyComponentImpl implements MyComponent { + private final MyComponentImpl myComponentImpl = this; + + private MyAssistedClass_Factory myAssistedClassProvider; + + private Provider myComponentAssistedFactoryProvider; + + private MyComponentImpl() { + + initialize(); + + } + + @SuppressWarnings("unchecked") + private void initialize() { + this.myAssistedClassProvider = MyAssistedClass_Factory.create(Baz_Factory.create()); + this.myComponentAssistedFactoryProvider = MyComponentAssistedFactory_Impl.create(myAssistedClassProvider); + } + + @Override + public MyComponentAssistedFactory myComponentAssistedFactory() { + return myComponentAssistedFactoryProvider.get(); + } + + @Override + public MySubcomponent mySubcomponent() { + return new MySubcomponentImpl(myComponentImpl); + } + } +} diff --git a/javatests/dagger/internal/codegen/goldens/AssistedFactoryTest_testMultipleAssistedFactoryInDifferentComponents_FAST_INIT_MODE_test.DaggerMyComponent b/javatests/dagger/internal/codegen/goldens/AssistedFactoryTest_testMultipleAssistedFactoryInDifferentComponents_FAST_INIT_MODE_test.DaggerMyComponent new file mode 100644 index 00000000000..315ebc43cc8 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/AssistedFactoryTest_testMultipleAssistedFactoryInDifferentComponents_FAST_INIT_MODE_test.DaggerMyComponent @@ -0,0 +1,149 @@ +package test; + +import dagger.internal.DaggerGenerated; +import dagger.internal.SingleCheck; +import javax.annotation.processing.Generated; +import javax.inject.Provider; + +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava" +}) +final class DaggerMyComponent { + private DaggerMyComponent() { + } + + public static Builder builder() { + return new Builder(); + } + + public static MyComponent create() { + return new Builder().build(); + } + + static final class Builder { + private Builder() { + } + + public MyComponent build() { + return new MyComponentImpl(); + } + } + + private static final class MySubcomponentImpl implements MySubcomponent { + private final MyComponentImpl myComponentImpl; + + private final MySubcomponentImpl mySubcomponentImpl = this; + + private Provider mySubcomponentAssistedFactoryProvider; + + private MySubcomponentImpl(MyComponentImpl myComponentImpl) { + this.myComponentImpl = myComponentImpl; + + initialize(); + + } + + @SuppressWarnings("unchecked") + private void initialize() { + this.mySubcomponentAssistedFactoryProvider = SingleCheck.provider(new SwitchingProvider(myComponentImpl, mySubcomponentImpl, 0)); + } + + @Override + public MySubcomponentAssistedFactory mySubcomponentAssistedFactory() { + return mySubcomponentAssistedFactoryProvider.get(); + } + + private static final class SwitchingProvider implements Provider { + private final MyComponentImpl myComponentImpl; + + private final MySubcomponentImpl mySubcomponentImpl; + + private final int id; + + SwitchingProvider(MyComponentImpl myComponentImpl, MySubcomponentImpl mySubcomponentImpl, + int id) { + this.myComponentImpl = myComponentImpl; + this.mySubcomponentImpl = mySubcomponentImpl; + this.id = id; + } + + @SuppressWarnings("unchecked") + @Override + public T get() { + switch (id) { + case 0: // test.MySubcomponentAssistedFactory + return (T) new MySubcomponentAssistedFactory() { + @Override + public MyAssistedClass create(Bar bar, Foo foo) { + return new MyAssistedClass(foo, new Baz(), bar); + } + }; + + default: throw new AssertionError(id); + } + } + } + } + + private static final class MyComponentImpl implements MyComponent { + private final MyComponentImpl myComponentImpl = this; + + private Provider myComponentAssistedFactoryProvider; + + private MyComponentImpl() { + + initialize(); + + } + + @SuppressWarnings("unchecked") + private void initialize() { + this.myComponentAssistedFactoryProvider = SingleCheck.provider(new SwitchingProvider(myComponentImpl, 0)); + } + + @Override + public MyComponentAssistedFactory myComponentAssistedFactory() { + return myComponentAssistedFactoryProvider.get(); + } + + @Override + public MySubcomponent mySubcomponent() { + return new MySubcomponentImpl(myComponentImpl); + } + + private static final class SwitchingProvider implements Provider { + private final MyComponentImpl myComponentImpl; + + private final int id; + + SwitchingProvider(MyComponentImpl myComponentImpl, int id) { + this.myComponentImpl = myComponentImpl; + this.id = id; + } + + @SuppressWarnings("unchecked") + @Override + public T get() { + switch (id) { + case 0: // test.MyComponentAssistedFactory + return (T) new MyComponentAssistedFactory() { + @Override + public MyAssistedClass create(Bar bar, Foo foo) { + return new MyAssistedClass(foo, new Baz(), bar); + } + }; + + default: throw new AssertionError(id); + } + } + } + } +}