Skip to content

Commit

Permalink
Fix @AssistedFactory method returning parameterized Foo with fixed ty…
Browse files Browse the repository at this point in the history
…pe parameters.

The issue was that in this case the FooFactory_Impl's delegate, the Foo_Factory field, needs to use the declared type arguments from DeclaredType.getTypeArguments() rather than the type variables from TypeElement.getTypeParameters().

RELNOTES=Fixes @AssistedFactory for parameterized Foo with fixed type parameters.
PiperOrigin-RevId: 354427308
  • Loading branch information
bcorso authored and Dagger Team committed Jan 29, 2021
1 parent 9a90151 commit 552f430
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 20 deletions.
48 changes: 28 additions & 20 deletions java/dagger/internal/codegen/AssistedFactoryProcessingStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
import dagger.internal.codegen.base.SourceFileGenerationException;
import dagger.internal.codegen.base.SourceFileGenerator;
import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
import dagger.internal.codegen.binding.Binding;
import dagger.internal.codegen.binding.BindingFactory;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.langmodel.DaggerElements;
Expand Down Expand Up @@ -305,13 +304,7 @@ public Element originatingElement(ProvisionBinding binding) {
@Override
public Optional<TypeSpec.Builder> write(ProvisionBinding binding) {
TypeElement factory = asType(binding.bindingElement().get());
ExecutableElement factoryMethod =
AssistedInjectionAnnotations.assistedFactoryMethod(factory, elements, types);
ExecutableType factoryMethodType =
asExecutable(types.asMemberOf(asDeclared(binding.key().type()), factoryMethod));
TypeElement returnElement = asTypeElement(factoryMethodType.getReturnType());
ParameterSpec delegateFactoryParam =
ParameterSpec.builder(delegateFactoryTypeName(returnElement), "delegateFactory").build();

TypeSpec.Builder builder =
TypeSpec.classBuilder(nameGeneratedType(binding))
.addModifiers(PUBLIC, FINAL)
Expand All @@ -326,6 +319,15 @@ public Optional<TypeSpec.Builder> write(ProvisionBinding binding) {
builder.superclass(factory.asType());
}

// Define all types associated with the @AssistedFactory before generating the implementation.
DeclaredType factoryType = asDeclared(binding.key().type());
ExecutableElement factoryMethod =
AssistedInjectionAnnotations.assistedFactoryMethod(factory, elements, types);
ExecutableType factoryMethodType = asExecutable(types.asMemberOf(factoryType, factoryMethod));
DeclaredType returnType = asDeclared(factoryMethodType.getReturnType());
TypeElement returnElement = asTypeElement(returnType);
ParameterSpec delegateFactoryParam =
ParameterSpec.builder(delegateFactoryTypeName(returnType), "delegateFactory").build();
builder
.addField(
FieldSpec.builder(delegateFactoryParam.type, delegateFactoryParam.name)
Expand Down Expand Up @@ -363,19 +365,25 @@ public Optional<TypeSpec.Builder> write(ProvisionBinding binding) {
return Optional.of(builder);
}

private TypeName delegateFactoryTypeName(TypeElement returnElement) {
Binding delegateBinding =
bindingFactory.injectionBinding(
getOnlyElement(assistedInjectedConstructors(returnElement)), Optional.empty());
ImmutableList<TypeVariableName> typeParameters =
returnElement.getTypeParameters().stream()
.map(TypeVariableName::get)
.collect(toImmutableList());
return typeParameters.isEmpty()
? generatedClassNameForBinding(delegateBinding)
/** Returns the generated factory {@link TypeName type} for an @AssistedInject constructor. */
private TypeName delegateFactoryTypeName(DeclaredType assistedInjectType) {
// The name of the generated factory for the assisted inject type,
// e.g. an @AssistedInject Foo(...) {...} constructor will generate a Foo_Factory class.
ClassName generatedFactoryClassName =
generatedClassNameForBinding(
bindingFactory.injectionBinding(
getOnlyElement(assistedInjectedConstructors(asTypeElement(assistedInjectType))),
Optional.empty()));

// Return the factory type resolved with the same type parameters as the assisted inject type.
return assistedInjectType.getTypeArguments().isEmpty()
? generatedFactoryClassName
: ParameterizedTypeName.get(
generatedClassNameForBinding(delegateBinding),
typeParameters.toArray(new TypeName[0]));
generatedFactoryClassName,
assistedInjectType.getTypeArguments().stream()
.map(TypeName::get)
.collect(toImmutableList())
.toArray(new TypeName[0]));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ interface ParentComponent {
// Tests a parameterized Factory with same type as binding
ParameterizedFooFactory<Dep1, Dep1> bindingParameterizedFooFactory();

// Tests a parameterized Factory with fixed type parameters
FixedParameterizedFooFactory fixedParameterizedFooFactory();

// Tests a parameterized Factory that extends an interface with a parameterized return type
ExtendedFooFactory<Dep2, AssistedDep2> extendedParameterizedFooFactory();

Expand Down Expand Up @@ -175,6 +178,32 @@ public void testBindingParameterizedFooFactory() {
assertThat(parameterizedFoo.factory).isNotNull();
}

@AssistedFactory
interface FixedParameterizedFooFactory {
ParameterizedFoo<Dep2, AssistedDep2> create(
AssistedDep1 assistedDep1, AssistedDep2 assistedDep2, int assistedInt);
}

@Test
public void testFixedParameterizedFooFactory() {
AssistedDep1 assistedDep1 = new AssistedDep1();
AssistedDep2 assistedDep2 = new AssistedDep2();
int assistedInt = 7;
ParameterizedFoo<Dep2, AssistedDep2> parameterizedFoo =
DaggerAssistedFactoryParameterizedTest_ParentComponent.create()
.fixedParameterizedFooFactory()
.create(assistedDep1, assistedDep2, assistedInt);
assertThat(parameterizedFoo.dep1).isNotNull();
assertThat(parameterizedFoo.depTProvider).isNotNull();
assertThat(parameterizedFoo.depTProvider.get()).isNotNull();
assertThat(parameterizedFoo.dep3).isNotNull();
assertThat(parameterizedFoo.dep4).isNotNull();
assertThat(parameterizedFoo.assistedDep1).isEqualTo(assistedDep1);
assertThat(parameterizedFoo.assistedDepT).isEqualTo(assistedDep2);
assertThat(parameterizedFoo.assistedInt).isEqualTo(assistedInt);
assertThat(parameterizedFoo.factory).isNotNull();
}

interface ParameterizedFactory<ReturnT, DepT, AssistedDepT> {
// Use different parameter names than Foo to make sure we're not assuming they're the same.
ReturnT create(
Expand Down

0 comments on commit 552f430

Please sign in to comment.