diff --git a/dagger-compiler/main/java/dagger/internal/codegen/binding/BindingFactory.java b/dagger-compiler/main/java/dagger/internal/codegen/binding/BindingFactory.java index adfb9d960b5..1ab8345a4cc 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/binding/BindingFactory.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/binding/BindingFactory.java @@ -53,6 +53,7 @@ import dagger.internal.codegen.xprocessing.XTypeNames; import java.util.Optional; import javax.inject.Inject; +import javax.inject.Provider; /** A factory for {@link Binding} objects. */ public final class BindingFactory { @@ -60,17 +61,21 @@ public final class BindingFactory { private final DependencyRequestFactory dependencyRequestFactory; private final InjectionSiteFactory injectionSiteFactory; private final InjectionAnnotations injectionAnnotations; + // We need a provider to avoid circular dependencies. + private final Provider injectBindingRegistryProvider; @Inject BindingFactory( KeyFactory keyFactory, DependencyRequestFactory dependencyRequestFactory, InjectionSiteFactory injectionSiteFactory, - InjectionAnnotations injectionAnnotations) { + InjectionAnnotations injectionAnnotations, + Provider injectBindingRegistryProvider) { this.keyFactory = keyFactory; this.dependencyRequestFactory = dependencyRequestFactory; this.injectionSiteFactory = injectionSiteFactory; this.injectionAnnotations = injectionAnnotations; + this.injectBindingRegistryProvider = injectBindingRegistryProvider; } /** @@ -162,6 +167,47 @@ public AssistedInjectionBinding assistedInjectionBinding( .build(); } + /** + * Returns an {@link BindingKind#INJECTION} binding returned by a parameterless {@code @Binds} + * method. + * + *

Although these are {@code @Binds} methods, they are represented as {@link InjectionBinding}s + * rather than {@link DelegateBinding}s. This is because a parameterless {@code @Binds} method + * binds the return type to its {@code @Inject} constructor. If this were a {@link + * DelegateBinding}, both the delegate and the underlying {@code @Inject} binding would have the + * same key, leading to duplicate binding errors and cyclical dependency issues in both binding + * graph resolution and code generation. Representing it as an {@link InjectionBinding} allows us + * to augment the implicit injection binding with metadata from the {@code @Binds} method (e.g., + * {@code contributingModule}) without creating duplicate keys. + * + * @param bindsMethod the parameterless {@code @Binds}-annotated method + * @param module the installed module that declares or inherits the method + */ + public Optional explicitInjectionBinding( + XMethodElement bindsMethod, XTypeElement module) { + checkArgument(bindsMethod.hasAnnotation(XTypeNames.BINDS)); + checkArgument(bindsMethod.getParameters().isEmpty()); + // Normally, we would use the input method as the binding element, but as in this case it is an + // Binds method that breaks assumptions for an InjectionBinding. Instead, we use the @Inject + // constructor as the binding element and expose the @Binds method via + // InjectionBinding#declaringElement(). + // We call InjectBindingRegistry#getOrFindInjectionBinding() rather than calling + // BindingFactory#injectionBinding() directly because the former ensures that the binding is + // properly validated before returning the binding. + Key key = keyFactory.forDelegateDeclaration(bindsMethod, module); // Key from @Binds + return injectBindingRegistryProvider + .get() + .getOrFindInjectionBinding(key) + .map( + binding -> + ((InjectionBinding) binding) + .toBuilder() + .key(key) + .contributingModule(module) // Mark as coming from module + .declaringElement(bindsMethod) + .build()); + } + public AssistedFactoryBinding assistedFactoryBinding( XTypeElement factory, Optional resolvedFactoryType) { diff --git a/dagger-compiler/main/java/dagger/internal/codegen/binding/DeclarationFormatter.java b/dagger-compiler/main/java/dagger/internal/codegen/binding/DeclarationFormatter.java index 4a4685c8a9f..729116d50a4 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/binding/DeclarationFormatter.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/binding/DeclarationFormatter.java @@ -67,6 +67,15 @@ public String format(Declaration declaration) { return formatSubcomponentDeclaration((SubcomponentDeclaration) declaration); } + if (declaration instanceof InjectionBinding) { + InjectionBinding injectionBinding = (InjectionBinding) declaration; + if (injectionBinding.declaringElement().isPresent()) { + return methodSignatureFormatter.format( + injectionBinding.declaringElement().get(), + injectionBinding.contributingModule().map(XTypeElement::getType)); + } + } + if (declaration.bindingElement().isPresent()) { XElement bindingElement = declaration.bindingElement().get(); if (isMethodParameter(bindingElement)) { diff --git a/dagger-compiler/main/java/dagger/internal/codegen/binding/DelegateDeclaration.java b/dagger-compiler/main/java/dagger/internal/codegen/binding/DelegateDeclaration.java index d456c4f4cab..ac0c3a30d1a 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/binding/DelegateDeclaration.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/binding/DelegateDeclaration.java @@ -70,6 +70,7 @@ public static final class Factory { public DelegateDeclaration create(XMethodElement method, XTypeElement contributingModule) { checkArgument(DelegateBinding.hasDelegateAnnotation(method)); + checkArgument(method.getParameters().size() == 1); XMethodType resolvedMethod = method.asMemberOf(contributingModule.getType()); DependencyRequest delegateRequest = dependencyRequestFactory.forRequiredResolvedVariable( diff --git a/dagger-compiler/main/java/dagger/internal/codegen/binding/InjectionBinding.java b/dagger-compiler/main/java/dagger/internal/codegen/binding/InjectionBinding.java index 657b392b3ce..1097d8c34aa 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/binding/InjectionBinding.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/binding/InjectionBinding.java @@ -18,6 +18,7 @@ import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; +import androidx.room3.compiler.processing.XMethodElement; import com.google.auto.value.AutoValue; import com.google.auto.value.extension.memoized.Memoized; import com.google.common.collect.ImmutableSet; @@ -30,7 +31,15 @@ import dagger.internal.codegen.xprocessing.Nullability; import java.util.Optional; -/** A binding for a {@link BindingKind#INJECTION}. */ +/** + * A binding for a {@link BindingKind#INJECTION}. + * + *

This also represents parameterless {@code @Binds} methods, which are used to explicitly bind + * an {@link javax.inject.Inject}-annotated constructor. By treating these as {@link + * InjectionBinding}s rather than {@code DelegateBinding}s, we avoid issues with duplicate bindings + * and cyclical dependencies, as both the {@code @Binds} method and the {@code @Inject} constructor + * would otherwise have the same key. + */ @CheckReturnValue @AutoValue public abstract class InjectionBinding extends ContributionBinding { @@ -72,9 +81,17 @@ public ImmutableSet dependencies() { .build(); } + @Override + public boolean requiresModuleInstance() { + return false; + } + @Override public abstract Builder toBuilder(); + /** The element that declares this binding, used for parameterless {@code @Binds} methods. */ + public abstract Optional declaringElement(); + @Memoized @Override public abstract int hashCode(); @@ -93,5 +110,7 @@ abstract static class Builder extends ContributionBinding.Builder constructorDependencies); abstract Builder injectionSites(ImmutableSortedSet injectionSites); + + abstract Builder declaringElement(XMethodElement declaringElement); } } diff --git a/dagger-compiler/main/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java b/dagger-compiler/main/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java index 46c1d66f369..e16cc0f00da 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java @@ -93,7 +93,7 @@ public String format(XExecutableElement method, Optional container) { private String format( XExecutableElement method, Optional container, boolean includeReturnType) { - return container.isPresent() + return container.isPresent() && !getSimpleName(method).contentEquals("") ? format( method, method.asMemberOf(container.get()), diff --git a/dagger-compiler/main/java/dagger/internal/codegen/binding/ModuleDescriptor.java b/dagger-compiler/main/java/dagger/internal/codegen/binding/ModuleDescriptor.java index f058f42e9a2..c6df4d92442 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/binding/ModuleDescriptor.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/binding/ModuleDescriptor.java @@ -153,8 +153,19 @@ public ModuleDescriptor createUncached(XTypeElement moduleElement) { bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement)); } if (DelegateBinding.hasDelegateAnnotation(moduleMethod)) { - delegates.add( - bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement)); + if (moduleMethod.hasAnnotation(XTypeNames.BINDS) + && moduleMethod.getParameters().isEmpty()) { + // Parameterless @Binds methods are treated as explicit InjectionBindings + // to avoid duplicate binding errors and cyclical dependencies that arise + // if they were modeled as DelegateBindings on the same key as the @Inject + // constructor. + bindingFactory + .explicitInjectionBinding(moduleMethod, moduleElement) + .ifPresent(bindings::add); + } else { + delegates.add( + bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement)); + } } if (moduleMethod.hasAnnotation(XTypeNames.MULTIBINDS)) { multibindingDeclarations.add( diff --git a/dagger-compiler/main/java/dagger/internal/codegen/model/BindingKind.java b/dagger-compiler/main/java/dagger/internal/codegen/model/BindingKind.java index 95dc9599494..e19199e9614 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/model/BindingKind.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/model/BindingKind.java @@ -98,8 +98,9 @@ public enum BindingKind { OPTIONAL, /** - * A binding for {@link dagger.Binds}-annotated method that that delegates from requests for one - * key to another. + * A binding for a {@link dagger.Binds} or {@code dagger.Alias}-annotated method that delegates + * from requests for one key to another. Note that parameterless {@code @Binds} methods are + * represented as {@link #INJECTION} bindings to avoid issues with duplicate bindings. */ // TODO(dpb,ronshapiro): This name is confusing and could use work. Not all usages of @Binds // bindings are simple delegations and we should have a name that better reflects that diff --git a/dagger-compiler/main/java/dagger/internal/codegen/processingstep/ModuleProcessingStep.java b/dagger-compiler/main/java/dagger/internal/codegen/processingstep/ModuleProcessingStep.java index 8ed7da6dacf..922283f436e 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/processingstep/ModuleProcessingStep.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/processingstep/ModuleProcessingStep.java @@ -120,7 +120,7 @@ private void generateForMethodsIn(XTypeElement module) { generate(factoryGenerator, bindingFactory.providesMethodBinding(method, module)); } else if (method.hasAnnotation(XTypeNames.PRODUCES)) { generate(producerFactoryGenerator, bindingFactory.producesMethodBinding(method, module)); - } else if (method.hasAnnotation(XTypeNames.BINDS)) { + } else if (method.hasAnnotation(XTypeNames.BINDS) && !method.getParameters().isEmpty()) { inaccessibleMapKeyProxyGenerator.generate(bindsMethodBinding(module, method), messager); } } diff --git a/dagger-compiler/main/java/dagger/internal/codegen/validation/BindsMethodValidator.java b/dagger-compiler/main/java/dagger/internal/codegen/validation/BindsMethodValidator.java index 9cc87567f25..db967f69631 100644 --- a/dagger-compiler/main/java/dagger/internal/codegen/validation/BindsMethodValidator.java +++ b/dagger-compiler/main/java/dagger/internal/codegen/validation/BindsMethodValidator.java @@ -20,11 +20,13 @@ import static dagger.internal.codegen.validation.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING; import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT; import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS; +import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive; import androidx.room3.compiler.processing.XMethodElement; import androidx.room3.compiler.processing.XProcessingEnv; import androidx.room3.compiler.processing.XType; +import androidx.room3.compiler.processing.XTypeElement; import androidx.room3.compiler.processing.XVariableElement; import com.google.common.collect.ImmutableSet; import dagger.internal.codegen.base.ContributionType; @@ -40,6 +42,7 @@ final class BindsMethodValidator extends BindingMethodValidator { private final BindsTypeChecker bindsTypeChecker; private final DaggerSuperficialValidation superficialValidation; + private final InjectionAnnotations injectionAnnotations; @Inject BindsMethodValidator( @@ -60,6 +63,7 @@ final class BindsMethodValidator extends BindingMethodValidator { injectionAnnotations); this.bindsTypeChecker = bindsTypeChecker; this.superficialValidation = superficialValidation; + this.injectionAnnotations = injectionAnnotations; } @Override @@ -77,12 +81,16 @@ private class Validator extends MethodValidator { @Override protected void checkParameters() { - if (method.getParameters().size() != 1) { + if (method.getParameters().size() > 1) { report.addError( bindingMethods( - "must have exactly one parameter, whose type is assignable to the return type")); - } else { + "may have at most one parameter." + + " To bind an @Inject constructor, use a zero-parameter @Binds method." + + " To bind an instance, use a one-parameter @Binds method.")); + } else if (method.getParameters().size() == 1) { super.checkParameters(); + } else { // Parameters size == 0 + checkZeroParameterBinds(); } } @@ -118,6 +126,67 @@ protected void checkParameter(XVariableElement parameter) { } } + private void checkZeroParameterBinds() { + ContributionType contributionType = ContributionType.fromBindingElement(method); + if (!contributionType.equals(ContributionType.UNIQUE)) { + report.addError( + "Parameterless @Binds methods cannot be used with multibinding annotations" + + " (@IntoSet, @ElementsIntoSet, @IntoMap).", + method); + } + + if (!method.getAnnotationsAnnotatedWith(XTypeNames.MAP_KEY).isEmpty()) { + report.addError("Parameterless @Binds methods cannot have a @MapKey.", method); + } + + XType returnType = method.getReturnType(); + if (!isDeclared(returnType)) { + report.addError("Parameterless @Binds methods must return a declared type.", method); + return; + } + // Disallow parameterized types for parameterless @Binds. + // This feature is intended for making the @Inject binding of a concrete, non-generic + // class explicit to apply method-level annotations. + // Binding specific instantiations of generic types should be done with @Provides, + // which can properly handle dependencies related to the type parameters. + // Allowing generics here would lead to ambiguity in annotation scoping and + // dependency resolution. + if (!returnType.getTypeArguments().isEmpty()) { + report.addError("Parameterless @Binds methods cannot return a parameterized type.", method); + return; + } + XTypeElement returnTypeElement = returnType.getTypeElement(); + if (returnTypeElement == null) { + report.addError("Parameterless @Binds methods must return a valid type.", method); + return; + } + if (!returnTypeElement.getTypeParameters().isEmpty()) { + report.addError( + "Parameterless @Binds methods cannot bind generic types with @Inject constructors.", + method); + } + if (injectionAnnotations.getQualifier(method).isPresent()) { + report.addError("Parameterless @Binds methods may not have qualifiers.", method); + } + if (injectionAnnotations.getScope(method).isPresent()) { + report.addError("Parameterless @Binds methods may not be scoped.", method); + } + if (injectionAnnotations.getScope(returnTypeElement).isPresent()) { + report.addError( + "Parameterless @Binds methods cannot bind types with scoped @Inject constructors.", + method); + } + if (returnTypeElement.getConstructors().stream() + .filter(InjectionAnnotations::hasInjectAnnotation) + .count() + != 1) { + report.addError( + "Parameterless @Binds methods must return a type with exactly one @Inject" + + " constructor.", + method); + } + } + private XType boxIfNecessary(XType maybePrimitive) { return isPrimitive(maybePrimitive) ? maybePrimitive.boxed() : maybePrimitive; } diff --git a/javatests/dagger/functional/explicitbinds/BUILD b/javatests/dagger/functional/explicitbinds/BUILD new file mode 100644 index 00000000000..8dde9cda276 --- /dev/null +++ b/javatests/dagger/functional/explicitbinds/BUILD @@ -0,0 +1,34 @@ +# Copyright (C) 2026 The Dagger Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load( + "//:build_defs.bzl", + "DOCLINT_HTML_AND_SYNTAX", + "DOCLINT_REFERENCES", +) +load("//:test_defs.bzl", "GenJavaTests") + +package(default_visibility = ["//:src"]) + +GenJavaTests( + name = "BindsExplicitForInjectTest", + srcs = ["BindsExplicitForInjectTest.java"], + javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES, + deps = [ + "//third_party/java/dagger", + "//third_party/java/jsr330_inject", + "//third_party/java/junit", + "//third_party/java/truth", + ], +) diff --git a/javatests/dagger/functional/explicitbinds/BindsExplicitForInjectTest.java b/javatests/dagger/functional/explicitbinds/BindsExplicitForInjectTest.java new file mode 100644 index 00000000000..47621439e40 --- /dev/null +++ b/javatests/dagger/functional/explicitbinds/BindsExplicitForInjectTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2026 The Dagger Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dagger.functional.explicitbinds; + +import static com.google.common.truth.Truth.assertThat; + +import dagger.Binds; +import dagger.Component; +import dagger.Module; +import javax.inject.Inject; +import javax.inject.Singleton; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class BindsExplicitForInjectTest { + + static class Foo { + @Inject + Foo() {} + } + + @Module + interface TestModule { + @Binds + Foo bindFoo(); + } + + @Singleton + @Component(modules = TestModule.class) + interface TestComponent { + Foo getFoo1(); + + Foo getFoo2(); + } + + @Test + public void testScopedBindsExplicitForInject() { + TestComponent component = DaggerBindsExplicitForInjectTest_TestComponent.create(); + assertThat(component.getFoo1()).isNotNull(); + assertThat(component.getFoo1()).isNotSameInstanceAs(component.getFoo2()); + } +} diff --git a/javatests/dagger/internal/codegen/BUILD b/javatests/dagger/internal/codegen/BUILD index b542c82e81b..ae1b612fd25 100644 --- a/javatests/dagger/internal/codegen/BUILD +++ b/javatests/dagger/internal/codegen/BUILD @@ -98,6 +98,7 @@ MEDIUM_TESTS = [ "ProductionGraphValidationTest.java", "SubcomponentValidationTest.java", "KeywordValidatorTest.java", + "BindsExplicitForInjectTest.java", ] DEPS = [ diff --git a/javatests/dagger/internal/codegen/BindsExplicitForInjectTest.java b/javatests/dagger/internal/codegen/BindsExplicitForInjectTest.java new file mode 100644 index 00000000000..9e958e9f814 --- /dev/null +++ b/javatests/dagger/internal/codegen/BindsExplicitForInjectTest.java @@ -0,0 +1,652 @@ +/* + * Copyright (C) 2026 The Dagger Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dagger.internal.codegen; + +import static dagger.internal.codegen.extension.DaggerCollectors.onlyElement; + +import androidx.room3.compiler.processing.util.Source; +import com.google.common.collect.ImmutableList; +import dagger.spi.model.Binding; +import dagger.spi.model.BindingGraph; +import dagger.spi.model.BindingGraphPlugin; +import dagger.spi.model.BindingKind; +import dagger.spi.model.DaggerTypeElement; +import dagger.spi.model.DiagnosticReporter; +import dagger.testing.compile.CompilerTests; +import dagger.testing.golden.GoldenFileRule; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public final class BindsExplicitForInjectTest { + @Parameters(name = "{0}") + public static ImmutableList parameters() { + return CompilerMode.TEST_PARAMETERS; + } + + @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule(); + + private final CompilerMode compilerMode; + + public BindsExplicitForInjectTest(CompilerMode compilerMode) { + this.compilerMode = compilerMode; + } + + private static final Source FOO = + CompilerTests.javaSource( + "test.Foo", + "package test;", + "import javax.inject.Inject;", + "class Foo {", + " @Inject Foo() {}", + "}"); + + @Test + public void testScopedBinds_fails() { + Source scopedModule = + CompilerTests.javaSource( + "test.ScopedModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "import javax.inject.Singleton;", + "@Module", + "interface ScopedModule {", + " @Binds", + " @Singleton", + " Foo bindFoo();", + "}"); + Source scopedComponent = + CompilerTests.javaSource( + "test.ScopedComponent", + "package test;", + "import dagger.Component;", + "import javax.inject.Singleton;", + "@Singleton", + "@Component(modules = ScopedModule.class)", + "interface ScopedComponent {", + " Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(FOO, scopedModule, scopedComponent) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(2); + subject.hasErrorContaining("Parameterless @Binds methods may not be scoped."); + }); + } + + @Test + public void testUnscopedBinds_succeeds() { + Source bar = + CompilerTests.javaSource( + "test.Bar", + "package test;", + "import javax.inject.Inject;", + "class Bar {", + " @Inject Bar() {}", + "}"); + Source unscopedModule = + CompilerTests.javaSource( + "test.UnscopedModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface UnscopedModule {", + " @Binds", + " Bar bindBar();", + "}"); + Source unscopedComponent = + CompilerTests.javaSource( + "test.UnscopedComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = UnscopedModule.class)", + "interface UnscopedComponent {", + " Bar getBar();", + "}"); + CompilerTests.daggerCompiler(bar, unscopedModule, unscopedComponent) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(0); + subject.generatedSource(goldenFileRule.goldenSource("test/DaggerUnscopedComponent")); + subject.generatedSource( + goldenFileRule.goldenSource( + "test/Bar_Factory", compilerMode.isKotlinCodegenEnabled())); + }); + } + + @Test + public void testQualifiedBinds_fails() { + Source qualifiedModule = + CompilerTests.javaSource( + "test.QualifiedModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "import javax.inject.Qualifier;", + "@Qualifier @interface MyQualifier {}", + "@Module", + "interface QualifiedModule {", + " @Binds", + " @MyQualifier", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = QualifiedModule.class)", + "interface TestComponent {", + " @MyQualifier Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(FOO, qualifiedModule, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(2); + subject.hasErrorContaining("Parameterless @Binds methods may not have qualifiers."); + }); + } + + @Test + public void testScopedInject_fails() { + Source foo = + CompilerTests.javaSource( + "test.Foo", + "package test;", + "import javax.inject.Inject;", + "import javax.inject.Singleton;", + "@Singleton", + "class Foo {", + " @Inject Foo() {}", + "}"); + Source module = + CompilerTests.javaSource( + "test.TestModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface TestModule {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "import javax.inject.Singleton;", + "@Singleton", + "@Component(modules = TestModule.class)", + "interface TestComponent {", + " Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(foo, module, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(2); + subject.hasErrorContaining( + "Parameterless @Binds methods cannot bind types with scoped @Inject" + + " constructors."); + }); + } + + @Test + public void testImplicitAndExplicitInDifferentComponents_succeeds() { + Source bar = + CompilerTests.javaSource( + "test.Bar", + "package test;", + "import javax.inject.Inject;", + "class Bar {", + " @Inject Bar() {}", + "}"); + Source unscopedModule = + CompilerTests.javaSource( + "test.UnscopedModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface UnscopedModule {", + " @Binds", + " Bar bindBar();", + "}"); + // This component uses the explicit @Binds binding from UnscopedModule + Source explicitComponent = + CompilerTests.javaSource( + "test.ExplicitComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = UnscopedModule.class)", + "interface ExplicitComponent {", + " Bar getBar();", + "}"); + // This component does not install UnscopedModule, so it will use implicit @Inject + Source implicitComponent = + CompilerTests.javaSource( + "test.ImplicitComponent", + "package test;", + "import dagger.Component;", + "@Component", + "interface ImplicitComponent {", + " Bar getBar();", + "}"); + CompilerTests.daggerCompiler(bar, unscopedModule, explicitComponent, implicitComponent) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(0); + }); + } + + @Test + public void testExplicitBindsAndImplicitInjectDependency_succeeds() { + Source bar = CompilerTests.javaSource("test.Bar", "package test;", "class Bar {}"); + Source module1 = + CompilerTests.javaSource( + "test.Module1", + "package test;", + "import dagger.Module;", + "import dagger.Provides;", + "@Module", + "class Module1 {", + " @Provides Bar provideBar(Foo foo) { return new Bar(); }", + "}"); + Source module2 = + CompilerTests.javaSource( + "test.Module2", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface Module2 {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = {Module1.class, Module2.class})", + "interface TestComponent {", + " Foo getFoo();", + " Bar getBar();", + "}"); + CompilerTests.daggerCompiler(FOO, bar, module1, module2, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(0); + }); + } + + @Test + public void testBindInSubcomponentAndUsageInComponentAndSubcomponent_fails() { + Source childModule = + CompilerTests.javaSource( + "test.ChildModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface ChildModule {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "import dagger.Subcomponent;", + "@Component", + "interface TestComponent {", + " Foo getFoo();", + " ChildComponent subcomponent();", + "}"); + Source childComponent = + CompilerTests.javaSource( + "test.ChildComponent", + "package test;", + "import dagger.Subcomponent;", + "@Subcomponent(modules = ChildModule.class)", + "interface ChildComponent {", + " Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(FOO, childModule, component, childComponent) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(1); + subject.hasErrorContaining("Foo is bound multiple times"); + }); + } + + @Test + public void testBindInSubcomponentAndUsageOnlyInComponent_succeeds() { + Source childModule = + CompilerTests.javaSource( + "test.ChildModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface ChildModule {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "import dagger.Subcomponent;", + "@Component", + "interface TestComponent {", + " Foo getFoo();", + " ChildComponent subcomponent();", + "}"); + Source childComponent = + CompilerTests.javaSource( + "test.ChildComponent", + "package test;", + "import dagger.Subcomponent;", + "@Subcomponent(modules = ChildModule.class)", + "interface ChildComponent {}"); + CompilerTests.daggerCompiler(FOO, childModule, component, childComponent) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(0); + }); + } + + @Test + public void testOverriddenInjectWithProvides_fails() { + Source module1 = + CompilerTests.javaSource( + "test.Module1", + "package test;", + "import dagger.Module;", + "import dagger.Provides;", + "@Module", + "class Module1 {", + " @Provides Foo provideFoo() { return new Foo(); }", + "}"); + Source module2 = + CompilerTests.javaSource( + "test.Module2", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface Module2 {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = {Module1.class, Module2.class})", + "interface TestComponent {", + " Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(FOO, module1, module2, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(1); + subject.hasErrorContaining("Foo is bound multiple times"); + }); + } + + @Test + public void testTwoExplicitBinds_fails() { + Source module1 = + CompilerTests.javaSource( + "test.Module1", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface Module1 {", + " @Binds", + " Foo bindFoo();", + "}"); + Source module2 = + CompilerTests.javaSource( + "test.Module2", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface Module2 {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = {Module1.class, Module2.class})", + "interface TestComponent {", + " Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(FOO, module1, module2, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(1); + subject.hasErrorContaining("Foo is bound multiple times"); + }); + } + + @Test + public void testGenericInjectBinds_fails() { + Source foo = + CompilerTests.javaSource( + "test.Foo", + "package test;", + "import javax.inject.Inject;", + "class Foo {", + " @Inject Foo() {}", + "}"); + Source module = + CompilerTests.javaSource( + "test.TestModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface TestModule {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = TestModule.class)", + "interface TestComponent {", + " Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(foo, module, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(2); + subject.hasErrorContaining( + "Parameterless @Binds methods cannot return a parameterized type."); + }); + } + + @Test + public void testIntoSetBinds_fails() { + Source testModule = + CompilerTests.javaSource( + "test.TestModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "import dagger.multibindings.IntoSet;", + "@Module", + "interface TestModule {", + " @Binds", + " @IntoSet", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "import java.util.Set;", + "@Component(modules = TestModule.class)", + "interface TestComponent {", + " Set getFoo();", + "}"); + CompilerTests.daggerCompiler(FOO, testModule, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(2); + subject.hasErrorContaining( + "Parameterless @Binds methods cannot be used with multibinding annotations"); + }); + } + + @Test + public void testMapKeyBinds_fails() { + Source testModule = + CompilerTests.javaSource( + "test.TestModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "import dagger.MapKey;", + "@MapKey", + "@interface MyMapKey {", + " String value();", + "}", + "@Module", + "interface TestModule {", + " @Binds", + " @MyMapKey(\"foo\")", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "import java.util.Map;", + "@Component(modules = TestModule.class)", + "interface TestComponent {", + " Map getFoo();", + "}"); + CompilerTests.daggerCompiler(FOO, testModule, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(3); + subject.hasErrorContaining("Parameterless @Binds methods cannot have a @MapKey."); + subject.hasErrorContaining("@Binds methods of non map type cannot declare a map key"); + }); + } + + @Test + public void testGenericInjectConstructor_fails() { + Source foo = + CompilerTests.javaSource( + "test.Foo", + "package test;", + "import javax.inject.Inject;", + "class Foo {", + " @Inject Foo() {}", + "}"); + Source module = + CompilerTests.javaSource( + "test.TestModule", + "package test;", + "import dagger.Binds;", + "import dagger.Module;", + "@Module", + "interface TestModule {", + " @Binds", + " Foo bindFoo();", + "}"); + Source component = + CompilerTests.javaSource( + "test.TestComponent", + "package test;", + "import dagger.Component;", + "@Component(modules = TestModule.class)", + "interface TestComponent {", + " Foo getFoo();", + "}"); + CompilerTests.daggerCompiler(foo, module, component) + .withProcessingOptions(compilerMode.processorOptions()) + .compile( + subject -> { + subject.hasErrorCount(2); + subject.hasErrorContaining( + "Parameterless @Binds methods cannot bind generic types with @Inject" + + " constructors."); + }); + } + + static class MetadataInspector implements BindingGraphPlugin { + public static final AtomicReference foundBinding = new AtomicReference<>(); + public static final AtomicReference expectedModule = new AtomicReference<>(); + + @Override + public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) { + if (graph.isFullBindingGraph()) { + return; + } + Binding binding = + graph.bindings().stream() + .filter( + b -> + b.key().toString().equals("test.FooImpl") + && b.kind() == BindingKind.INJECTION) + .collect(onlyElement()); + foundBinding.set(binding); + expectedModule.set( + binding + .contributingModule() + .orElseThrow( + () -> new IllegalStateException("Binding should have contributing module"))); + } + } +} diff --git a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java index 36219267989..5a55e76d081 100644 --- a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java +++ b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java @@ -172,7 +172,9 @@ public void tooManyQualifiersOnParameter() { @Test public void noParameters() { - assertThatMethod("@Binds abstract Object noParameters();").hasError("one parameter"); + assertThatMethod("@Binds abstract Object noParameters();") + .hasError( + "Parameterless @Binds methods must return a type with exactly one @Inject constructor"); } @Test diff --git a/javatests/dagger/internal/codegen/ComponentValidationTest.java b/javatests/dagger/internal/codegen/ComponentValidationTest.java index 72e657bd551..9ec4dce4c92 100644 --- a/javatests/dagger/internal/codegen/ComponentValidationTest.java +++ b/javatests/dagger/internal/codegen/ComponentValidationTest.java @@ -254,12 +254,11 @@ public void componentWithInvalidModule() { .compile( subject -> { subject.hasErrorCount(2); - subject.hasErrorContaining("test.BadModule has errors") - .onSource(component) - .onLine(5); - subject.hasErrorContaining( - "@Binds methods must have exactly one parameter, whose type is assignable to " - + "the return type") + subject.hasErrorContaining("test.BadModule has errors").onSource(component).onLine(5); + subject + .hasErrorContaining( + "Parameterless @Binds methods must return a type with exactly one @Inject" + + " constructor") .onSource(module) .onLine(8); }); diff --git a/javatests/dagger/internal/codegen/ModuleValidationTest.java b/javatests/dagger/internal/codegen/ModuleValidationTest.java index f07b7144d20..bcfc2de82a0 100644 --- a/javatests/dagger/internal/codegen/ModuleValidationTest.java +++ b/javatests/dagger/internal/codegen/ModuleValidationTest.java @@ -378,12 +378,11 @@ public void invalidIncludedModule() { .compile( subject -> { subject.hasErrorCount(2); - subject.hasErrorContaining("test.BadModule has errors") - .onSource(module) - .onLine(5); - subject.hasErrorContaining( - "@Binds methods must have exactly one parameter, whose type is " - + "assignable to the return type") + subject.hasErrorContaining("test.BadModule has errors").onSource(module).onLine(5); + subject + .hasErrorContaining( + "Parameterless @Binds methods must return a type with exactly one @Inject" + + " constructor") .onSource(badModule) .onLine(8); }); diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_DEFAULT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_DEFAULT_MODE new file mode 100644 index 00000000000..da3c2586678 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_DEFAULT_MODE @@ -0,0 +1,42 @@ +package test; + +import dagger.internal.DaggerGenerated; +import dagger.internal.Factory; +import dagger.internal.QualifierMetadata; +import dagger.internal.ScopeMetadata; +import javax.annotation.processing.Generated; + +@ScopeMetadata +@QualifierMetadata +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava", + "cast", + "deprecation", + "nullness:initialization.field.uninitialized" +}) +public final class Bar_Factory implements Factory { + @Override + public Bar get() { + return newInstance(); + } + + public static Bar_Factory create() { + return InstanceHolder.INSTANCE; + } + + public static Bar newInstance() { + return new Bar(); + } + + private static final class InstanceHolder { + static final Bar_Factory INSTANCE = new Bar_Factory(); + } +} diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_FAST_INIT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_FAST_INIT_MODE new file mode 100644 index 00000000000..da3c2586678 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_FAST_INIT_MODE @@ -0,0 +1,42 @@ +package test; + +import dagger.internal.DaggerGenerated; +import dagger.internal.Factory; +import dagger.internal.QualifierMetadata; +import dagger.internal.ScopeMetadata; +import javax.annotation.processing.Generated; + +@ScopeMetadata +@QualifierMetadata +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava", + "cast", + "deprecation", + "nullness:initialization.field.uninitialized" +}) +public final class Bar_Factory implements Factory { + @Override + public Bar get() { + return newInstance(); + } + + public static Bar_Factory create() { + return InstanceHolder.INSTANCE; + } + + public static Bar newInstance() { + return new Bar(); + } + + private static final class InstanceHolder { + static final Bar_Factory INSTANCE = new Bar_Factory(); + } +} diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_KT_DEFAULT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_KT_DEFAULT_MODE new file mode 100644 index 00000000000..5ad7647677c --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_KT_DEFAULT_MODE @@ -0,0 +1,38 @@ +package test + +import dagger.`internal`.DaggerGenerated +import dagger.`internal`.Factory +import dagger.`internal`.QualifierMetadata +import dagger.`internal`.ScopeMetadata +import javax.`annotation`.processing.Generated +import kotlin.Any +import kotlin.Suppress +import kotlin.jvm.JvmStatic + +@ScopeMetadata +@QualifierMetadata +@DaggerGenerated +@Generated( + value = ["dagger.internal.codegen.ComponentProcessor"], + comments = "https://dagger.dev", +) +@Suppress(names = [ + "UNCHECKED_CAST", + "USELESS_CAST", + "DEPRECATION" +]) +public class Bar_Factory : Factory { + public override fun `get`(): Any? = newInstance() + + private object InstanceHolder { + public val INSTANCE: Bar_Factory = Bar_Factory() + } + + public companion object { + @JvmStatic + public fun create(): Bar_Factory = InstanceHolder.INSTANCE + + @JvmStatic + public fun newInstance(): Any? = Bar() + } +} diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_KT_FAST_INIT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_KT_FAST_INIT_MODE new file mode 100644 index 00000000000..5ad7647677c --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.Bar_Factory_KT_FAST_INIT_MODE @@ -0,0 +1,38 @@ +package test + +import dagger.`internal`.DaggerGenerated +import dagger.`internal`.Factory +import dagger.`internal`.QualifierMetadata +import dagger.`internal`.ScopeMetadata +import javax.`annotation`.processing.Generated +import kotlin.Any +import kotlin.Suppress +import kotlin.jvm.JvmStatic + +@ScopeMetadata +@QualifierMetadata +@DaggerGenerated +@Generated( + value = ["dagger.internal.codegen.ComponentProcessor"], + comments = "https://dagger.dev", +) +@Suppress(names = [ + "UNCHECKED_CAST", + "USELESS_CAST", + "DEPRECATION" +]) +public class Bar_Factory : Factory { + public override fun `get`(): Any? = newInstance() + + private object InstanceHolder { + public val INSTANCE: Bar_Factory = Bar_Factory() + } + + public companion object { + @JvmStatic + public fun create(): Bar_Factory = InstanceHolder.INSTANCE + + @JvmStatic + public fun newInstance(): Any? = Bar() + } +} diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_DEFAULT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_DEFAULT_MODE new file mode 100644 index 00000000000..c50021989b9 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_DEFAULT_MODE @@ -0,0 +1,54 @@ +package test; + +import dagger.internal.DaggerGenerated; +import javax.annotation.processing.Generated; + +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava", + "cast", + "deprecation", + "nullness:initialization.field.uninitialized" +}) +final class DaggerUnscopedComponent { + private DaggerUnscopedComponent() { + } + + public static Builder builder() { + return new Builder(); + } + + public static UnscopedComponent create() { + return new Builder().build(); + } + + static final class Builder { + private Builder() { + } + + public UnscopedComponent build() { + return new UnscopedComponentImpl(); + } + } + + private static final class UnscopedComponentImpl implements UnscopedComponent { + private final UnscopedComponentImpl unscopedComponentImpl = this; + + UnscopedComponentImpl() { + + + } + + @Override + public Bar getBar() { + return new Bar(); + } + } +} \ No newline at end of file diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_FAST_INIT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_FAST_INIT_MODE new file mode 100644 index 00000000000..74ae1455606 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_FAST_INIT_MODE @@ -0,0 +1,54 @@ +package test; + +import dagger.internal.DaggerGenerated; +import javax.annotation.processing.Generated; + +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava", + "cast", + "deprecation", + "nullness:initialization.field.uninitialized" +}) +final class DaggerUnscopedComponent { + private DaggerUnscopedComponent() { + } + + public static Builder builder() { + return new Builder(); + } + + public static UnscopedComponent create() { + return new Builder().build(); + } + + static final class Builder { + private Builder() { + } + + public UnscopedComponent build() { + return new UnscopedComponentImpl(); + } + } + + private static final class UnscopedComponentImpl implements UnscopedComponent { + private final UnscopedComponentImpl unscopedComponentImpl = this; + + UnscopedComponentImpl() { + + + } + + @Override + public Bar getBar() { + return new Bar(); + } + } +} diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_KT_DEFAULT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_KT_DEFAULT_MODE new file mode 100644 index 00000000000..90d3d819b05 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_KT_DEFAULT_MODE @@ -0,0 +1,54 @@ +package test; + +import dagger.internal.DaggerGenerated; +import javax.annotation.processing.Generated; + +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava", + "cast", + "deprecation", + "nullness:initialization.field.uninitialized" +}) +final class DaggerUnscopedComponent { + private DaggerUnscopedComponent() { + } + + public static Builder builder() { + return new Builder(); + } + + public static UnscopedComponent create() { + return new Builder().build(); + } + + static final class Builder { + private Builder() { + } + + public UnscopedComponent build() { + return new UnscopedComponentImpl(); + } + } + + private static final class UnscopedComponentImpl implements UnscopedComponent { + private final UnscopedComponentImpl unscopedComponentImpl = this; + + UnscopedComponentImpl() { + + + } + + @Override + public Bar getBar() { + return (Bar) ((Object) (Bar_Factory.newInstance())); + } + } +} diff --git a/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_KT_FAST_INIT_MODE b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_KT_FAST_INIT_MODE new file mode 100644 index 00000000000..90d3d819b05 --- /dev/null +++ b/javatests/dagger/internal/codegen/goldens/BindsExplicitForInjectTest/testUnscopedBinds_succeeds/test.DaggerUnscopedComponent_KT_FAST_INIT_MODE @@ -0,0 +1,54 @@ +package test; + +import dagger.internal.DaggerGenerated; +import javax.annotation.processing.Generated; + +@DaggerGenerated +@Generated( + value = "dagger.internal.codegen.ComponentProcessor", + comments = "https://dagger.dev" +) +@SuppressWarnings({ + "unchecked", + "rawtypes", + "KotlinInternal", + "KotlinInternalInJava", + "cast", + "deprecation", + "nullness:initialization.field.uninitialized" +}) +final class DaggerUnscopedComponent { + private DaggerUnscopedComponent() { + } + + public static Builder builder() { + return new Builder(); + } + + public static UnscopedComponent create() { + return new Builder().build(); + } + + static final class Builder { + private Builder() { + } + + public UnscopedComponent build() { + return new UnscopedComponentImpl(); + } + } + + private static final class UnscopedComponentImpl implements UnscopedComponent { + private final UnscopedComponentImpl unscopedComponentImpl = this; + + UnscopedComponentImpl() { + + + } + + @Override + public Bar getBar() { + return (Bar) ((Object) (Bar_Factory.newInstance())); + } + } +}