• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.writing;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Verify.verifyNotNull;
21 import static com.squareup.javapoet.ClassName.OBJECT;
22 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
23 import static com.squareup.javapoet.MethodSpec.methodBuilder;
24 import static com.squareup.javapoet.TypeSpec.classBuilder;
25 import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
26 import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
27 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
28 import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
29 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
30 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.FUTURE_RETURN_VALUE_IGNORED;
31 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
32 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
33 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
34 import static dagger.internal.codegen.javapoet.TypeNames.FUTURES;
35 import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
36 import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER_TOKEN;
37 import static dagger.internal.codegen.javapoet.TypeNames.VOID_CLASS;
38 import static dagger.internal.codegen.javapoet.TypeNames.listOf;
39 import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
40 import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
41 import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
42 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
43 import static java.util.stream.Collectors.joining;
44 import static java.util.stream.Collectors.toList;
45 import static javax.lang.model.element.Modifier.FINAL;
46 import static javax.lang.model.element.Modifier.PRIVATE;
47 import static javax.lang.model.element.Modifier.PROTECTED;
48 import static javax.lang.model.element.Modifier.PUBLIC;
49 import static javax.lang.model.element.Modifier.STATIC;
50 
51 import androidx.room.compiler.processing.XElement;
52 import androidx.room.compiler.processing.XFiler;
53 import androidx.room.compiler.processing.XProcessingEnv;
54 import androidx.room.compiler.processing.XType;
55 import com.google.common.collect.ImmutableList;
56 import com.google.common.collect.ImmutableMap;
57 import com.google.common.collect.ImmutableSet;
58 import com.google.common.collect.Iterables;
59 import com.squareup.javapoet.ClassName;
60 import com.squareup.javapoet.CodeBlock;
61 import com.squareup.javapoet.FieldSpec;
62 import com.squareup.javapoet.MethodSpec;
63 import com.squareup.javapoet.ParameterizedTypeName;
64 import com.squareup.javapoet.TypeName;
65 import com.squareup.javapoet.TypeSpec;
66 import dagger.internal.codegen.base.SourceFileGenerator;
67 import dagger.internal.codegen.base.UniqueNameSet;
68 import dagger.internal.codegen.binding.Binding;
69 import dagger.internal.codegen.binding.FrameworkField;
70 import dagger.internal.codegen.binding.KeyFactory;
71 import dagger.internal.codegen.binding.ProductionBinding;
72 import dagger.internal.codegen.binding.SourceFiles;
73 import dagger.internal.codegen.compileroption.CompilerOptions;
74 import dagger.internal.codegen.javapoet.AnnotationSpecs;
75 import dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression;
76 import dagger.internal.codegen.javapoet.TypeNames;
77 import dagger.internal.codegen.model.DependencyRequest;
78 import dagger.internal.codegen.model.Key;
79 import dagger.internal.codegen.model.RequestKind;
80 import java.util.ArrayList;
81 import java.util.List;
82 import java.util.Map.Entry;
83 import java.util.Optional;
84 import javax.inject.Inject;
85 
86 /** Generates {@code Producer} implementations from {@link ProductionBinding} instances. */
87 public final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBinding> {
88   private final CompilerOptions compilerOptions;
89   private final KeyFactory keyFactory;
90   private final SourceFiles sourceFiles;
91 
92   @Inject
ProducerFactoryGenerator( XFiler filer, XProcessingEnv processingEnv, CompilerOptions compilerOptions, KeyFactory keyFactory, SourceFiles sourceFiles)93   ProducerFactoryGenerator(
94       XFiler filer,
95       XProcessingEnv processingEnv,
96       CompilerOptions compilerOptions,
97       KeyFactory keyFactory,
98       SourceFiles sourceFiles) {
99     super(filer, processingEnv);
100     this.compilerOptions = compilerOptions;
101     this.keyFactory = keyFactory;
102     this.sourceFiles = sourceFiles;
103   }
104 
105   @Override
originatingElement(ProductionBinding binding)106   public XElement originatingElement(ProductionBinding binding) {
107     // we only create factories for bindings that have a binding element
108     return binding.bindingElement().get();
109   }
110 
111   @Override
topLevelTypes(ProductionBinding binding)112   public ImmutableList<TypeSpec.Builder> topLevelTypes(ProductionBinding binding) {
113     // We don't want to write out resolved bindings -- we want to write out the generic version.
114     checkArgument(!binding.unresolved().isPresent());
115     checkArgument(binding.bindingElement().isPresent());
116 
117     TypeName providedTypeName = binding.contributedType().getTypeName();
118     TypeName futureTypeName = listenableFutureOf(providedTypeName);
119 
120     ClassName generatedTypeName = generatedClassNameForBinding(binding);
121     TypeSpec.Builder factoryBuilder =
122         classBuilder(generatedTypeName)
123             .addModifiers(PUBLIC, FINAL)
124             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
125 
126     UniqueNameSet uniqueFieldNames = new UniqueNameSet();
127     ImmutableMap.Builder<DependencyRequest, FieldSpec> fieldsBuilder = ImmutableMap.builder();
128 
129     MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PRIVATE);
130 
131     Optional<FieldSpec> moduleField =
132         binding.requiresModuleInstance()
133             ? Optional.of(
134                 addFieldAndConstructorParameter(
135                     factoryBuilder,
136                     constructorBuilder,
137                     uniqueFieldNames.getUniqueName("module"),
138                     binding.bindingTypeElement().get().getType().getTypeName()))
139             : Optional.empty();
140 
141     List<CodeBlock> frameworkFieldAssignments = new ArrayList<>();
142 
143     String executorParameterName = null;
144     String monitorParameterName = null;
145     ImmutableMap<DependencyRequest, FrameworkField> bindingFieldsForDependencies =
146         generateBindingFieldsForDependencies(binding);
147     for (Entry<DependencyRequest, FrameworkField> entry : bindingFieldsForDependencies.entrySet()) {
148       DependencyRequest dependency = entry.getKey();
149       Key key = dependency.key();
150       FrameworkField bindingField = entry.getValue();
151       String fieldName = uniqueFieldNames.getUniqueName(bindingField.name());
152       if (key.equals(keyFactory.forProductionImplementationExecutor())) {
153         executorParameterName = fieldName;
154         constructorBuilder.addParameter(bindingField.type(), executorParameterName);
155       } else if (key.equals(keyFactory.forProductionComponentMonitor())) {
156         monitorParameterName = fieldName;
157         constructorBuilder.addParameter(bindingField.type(), monitorParameterName);
158       } else {
159         FieldSpec field =
160             addFieldAndConstructorParameter(
161                 factoryBuilder, constructorBuilder, fieldName, bindingField.type());
162         fieldsBuilder.put(dependency, field);
163         frameworkFieldAssignments.add(fieldAssignment(field, bindingField));
164       }
165     }
166     ImmutableMap<DependencyRequest, FieldSpec> fields = fieldsBuilder.build();
167 
168     constructorBuilder.addStatement(
169         "super($N, $L, $N)",
170         verifyNotNull(monitorParameterName),
171         producerTokenConstruction(generatedTypeName, binding),
172         verifyNotNull(executorParameterName));
173 
174     if (binding.requiresModuleInstance()) {
175       assignField(constructorBuilder, moduleField.get(), null);
176     }
177 
178     constructorBuilder.addCode(CodeBlock.join(frameworkFieldAssignments, "\n"));
179 
180     MethodSpec.Builder collectDependenciesBuilder =
181         methodBuilder("collectDependencies").addAnnotation(Override.class).addModifiers(PROTECTED);
182 
183     ImmutableList<DependencyRequest> asyncDependencies = asyncDependencies(binding);
184     for (DependencyRequest dependency : asyncDependencies) {
185       TypeName futureType = listenableFutureOf(asyncDependencyType(dependency));
186       CodeBlock futureAccess = CodeBlock.of("$N.get()", fields.get(dependency));
187       collectDependenciesBuilder.addStatement(
188           "$T $L = $L",
189           futureType,
190           dependencyFutureName(dependency),
191           dependency.kind().equals(RequestKind.PRODUCED)
192               ? CodeBlock.of("$T.createFutureProduced($L)", PRODUCERS, futureAccess)
193               : futureAccess);
194     }
195     FutureTransform futureTransform = createFutureTransform(fields, binding, asyncDependencies);
196 
197     collectDependenciesBuilder
198         .returns(listenableFutureOf(futureTransform.applyArgType()))
199         .addStatement("return $L", futureTransform.futureCodeBlock());
200 
201     MethodSpec.Builder callProducesMethod =
202         methodBuilder("callProducesMethod")
203             .returns(futureTypeName)
204             .addAnnotation(Override.class)
205             .addModifiers(PUBLIC)
206             .addParameter(futureTransform.applyArgType(), futureTransform.applyArgName())
207             .addExceptions(binding.thrownTypes().stream().map(XType::getTypeName).collect(toList()))
208             .addCode(
209                 getInvocationCodeBlock(
210                     binding, providedTypeName, futureTransform.parameterCodeBlocks()));
211     if (futureTransform.hasUncheckedCast()) {
212       callProducesMethod.addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED));
213     }
214 
215     MethodSpec constructor = constructorBuilder.build();
216     factoryBuilder
217         .superclass(
218             ParameterizedTypeName.get(
219                 TypeNames.ABSTRACT_PRODUCES_METHOD_PRODUCER,
220                 futureTransform.applyArgType(),
221                 providedTypeName))
222         .addMethod(constructor)
223         .addMethod(staticFactoryMethod(binding, constructor))
224         .addMethod(collectDependenciesBuilder.build())
225         .addMethod(callProducesMethod.build());
226 
227     gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
228 
229     // TODO(gak): write a sensible toString
230     return ImmutableList.of(factoryBuilder);
231   }
232 
staticFactoryMethod(ProductionBinding binding, MethodSpec constructor)233   private MethodSpec staticFactoryMethod(ProductionBinding binding, MethodSpec constructor) {
234     return MethodSpec.methodBuilder("create")
235         .addModifiers(PUBLIC, STATIC)
236         .returns(parameterizedGeneratedTypeNameForBinding(binding))
237         .addTypeVariables(bindingTypeElementTypeVariableNames(binding))
238         .addParameters(constructor.parameters)
239         .addStatement(
240             "return new $T($L)",
241             parameterizedGeneratedTypeNameForBinding(binding),
242             constructor.parameters.stream()
243                 .map(p -> CodeBlock.of("$N", p.name))
244                 .collect(toParametersCodeBlock()))
245         .build();
246   }
247 
248   // TODO(ronshapiro): consolidate versions of these
addFieldAndConstructorParameter( TypeSpec.Builder typeBuilder, MethodSpec.Builder constructorBuilder, String variableName, TypeName variableType)249   private static FieldSpec addFieldAndConstructorParameter(
250       TypeSpec.Builder typeBuilder,
251       MethodSpec.Builder constructorBuilder,
252       String variableName,
253       TypeName variableType) {
254     FieldSpec field = FieldSpec.builder(variableType, variableName, PRIVATE, FINAL).build();
255     typeBuilder.addField(field);
256     constructorBuilder.addParameter(field.type, field.name);
257     return field;
258   }
259 
fieldAssignment(FieldSpec field, FrameworkField frameworkField)260   private static CodeBlock fieldAssignment(FieldSpec field, FrameworkField frameworkField) {
261     CodeBlock.Builder statement = CodeBlock.builder();
262     if (frameworkField.type() != null
263         && TypeNames.rawTypeName(frameworkField.type()).equals(TypeNames.PRODUCER)) {
264       statement.addStatement(
265           "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, TypeNames.PRODUCERS);
266     } else {
267       statement.addStatement("this.$1N = $1N", field);
268     }
269     return statement.build();
270   }
271 
assignField( MethodSpec.Builder constructorBuilder, FieldSpec field, ParameterizedTypeName type)272   private static void assignField(
273       MethodSpec.Builder constructorBuilder, FieldSpec field, ParameterizedTypeName type) {
274     if (type != null && type.rawType.equals(TypeNames.PRODUCER)) {
275       constructorBuilder.addStatement(
276           "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, TypeNames.PRODUCERS);
277     } else {
278       constructorBuilder.addStatement("this.$1N = $1N", field);
279     }
280   }
281 
282   /** Returns a list of dependencies that are generated asynchronously. */
asyncDependencies(Binding binding)283   private static ImmutableList<DependencyRequest> asyncDependencies(Binding binding) {
284     return binding.dependencies().stream()
285         .filter(ProducerFactoryGenerator::isAsyncDependency)
286         .collect(toImmutableList());
287   }
288 
producerTokenConstruction( ClassName generatedTypeName, ProductionBinding binding)289   private CodeBlock producerTokenConstruction(
290       ClassName generatedTypeName, ProductionBinding binding) {
291     CodeBlock producerTokenArgs =
292         compilerOptions.writeProducerNameInToken()
293             ? CodeBlock.of(
294                 "$S",
295                 String.format(
296                     "%s#%s",
297                     binding.bindingTypeElement().get().getClassName(),
298                     getSimpleName(binding.bindingElement().get())))
299             : CodeBlock.of("$T.class", generatedTypeName);
300     return CodeBlock.of("$T.create($L)", PRODUCER_TOKEN, producerTokenArgs);
301   }
302 
303   /** Returns a name of the variable representing this dependency's future. */
dependencyFutureName(DependencyRequest dependency)304   private static String dependencyFutureName(DependencyRequest dependency) {
305     return getSimpleName(dependency.requestElement().get().xprocessing()) + "Future";
306   }
307 
createFutureTransform( ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding, ImmutableList<DependencyRequest> asyncDependencies)308   private FutureTransform createFutureTransform(
309         ImmutableMap<DependencyRequest, FieldSpec> fields,
310         ProductionBinding binding,
311         ImmutableList<DependencyRequest> asyncDependencies) {
312       if (asyncDependencies.isEmpty()) {
313         return new NoArgFutureTransform(fields, binding);
314       } else if (asyncDependencies.size() == 1) {
315         return new SingleArgFutureTransform(
316             fields, binding, Iterables.getOnlyElement(asyncDependencies));
317       } else {
318         return new MultiArgFutureTransform(fields, binding, asyncDependencies);
319       }
320     }
321 
322   /** Represents the transformation of an input future by a producer method. */
323   abstract class FutureTransform {
324     protected final ImmutableMap<DependencyRequest, FieldSpec> fields;
325     protected final ProductionBinding binding;
326 
FutureTransform(ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding)327     FutureTransform(ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding) {
328       this.fields = fields;
329       this.binding = binding;
330     }
331 
332     /** The code block representing the future that should be transformed. */
futureCodeBlock()333     abstract CodeBlock futureCodeBlock();
334 
335     /** The type of the argument to the apply method. */
applyArgType()336     abstract TypeName applyArgType();
337 
338     /** The name of the argument to the apply method */
applyArgName()339     abstract String applyArgName();
340 
341     /** The code blocks to be passed to the produces method itself. */
parameterCodeBlocks()342     abstract ImmutableList<CodeBlock> parameterCodeBlocks();
343 
344     /** Whether the transform method has an unchecked cast. */
hasUncheckedCast()345     boolean hasUncheckedCast() {
346       return false;
347     }
348 
frameworkTypeUsageStatement(DependencyRequest dependency)349     CodeBlock frameworkTypeUsageStatement(DependencyRequest dependency) {
350       return sourceFiles.frameworkTypeUsageStatement(
351           CodeBlock.of("$N", fields.get(dependency)), dependency.kind());
352     }
353   }
354 
355   final class NoArgFutureTransform extends FutureTransform {
NoArgFutureTransform( ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding)356     NoArgFutureTransform(
357         ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding) {
358       super(fields, binding);
359     }
360 
361     @Override
futureCodeBlock()362     CodeBlock futureCodeBlock() {
363       return CodeBlock.of("$T.<$T>immediateFuture(null)", FUTURES, VOID_CLASS);
364     }
365 
366     @Override
applyArgType()367     TypeName applyArgType() {
368       return VOID_CLASS;
369     }
370 
371     @Override
applyArgName()372     String applyArgName() {
373       return "ignoredVoidArg";
374     }
375 
376     @Override
parameterCodeBlocks()377     ImmutableList<CodeBlock> parameterCodeBlocks() {
378       return binding.explicitDependencies().stream()
379           .map(this::frameworkTypeUsageStatement)
380           .collect(toImmutableList());
381     }
382   }
383 
384   final class SingleArgFutureTransform extends FutureTransform {
385     private final DependencyRequest asyncDependency;
386 
SingleArgFutureTransform( ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding, DependencyRequest asyncDependency)387     SingleArgFutureTransform(
388         ImmutableMap<DependencyRequest, FieldSpec> fields,
389         ProductionBinding binding,
390         DependencyRequest asyncDependency) {
391       super(fields, binding);
392       this.asyncDependency = asyncDependency;
393     }
394 
395     @Override
futureCodeBlock()396     CodeBlock futureCodeBlock() {
397       return CodeBlock.of("$L", dependencyFutureName(asyncDependency));
398     }
399 
400     @Override
applyArgType()401     TypeName applyArgType() {
402       return asyncDependencyType(asyncDependency);
403     }
404 
405     @Override
applyArgName()406     String applyArgName() {
407       String argName = getSimpleName(asyncDependency.requestElement().get().xprocessing());
408       if (argName.equals("module")) {
409         return "moduleArg";
410       }
411       return argName;
412     }
413 
414     @Override
parameterCodeBlocks()415     ImmutableList<CodeBlock> parameterCodeBlocks() {
416       ImmutableList.Builder<CodeBlock> parameterCodeBlocks = ImmutableList.builder();
417       for (DependencyRequest dependency : binding.explicitDependencies()) {
418         // We really want to compare instances here, because asyncDependency is an element in the
419         // set binding.dependencies().
420         if (dependency == asyncDependency) {
421           parameterCodeBlocks.add(CodeBlock.of("$L", applyArgName()));
422         } else {
423           parameterCodeBlocks.add(frameworkTypeUsageStatement(dependency));
424         }
425       }
426       return parameterCodeBlocks.build();
427     }
428   }
429 
430   final class MultiArgFutureTransform extends FutureTransform {
431     private final ImmutableList<DependencyRequest> asyncDependencies;
432 
MultiArgFutureTransform( ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding, ImmutableList<DependencyRequest> asyncDependencies)433     MultiArgFutureTransform(
434         ImmutableMap<DependencyRequest, FieldSpec> fields,
435         ProductionBinding binding,
436         ImmutableList<DependencyRequest> asyncDependencies) {
437       super(fields, binding);
438       this.asyncDependencies = asyncDependencies;
439     }
440 
441     @Override
futureCodeBlock()442     CodeBlock futureCodeBlock() {
443       return CodeBlock.of(
444           "$T.<$T>allAsList($L)",
445           FUTURES,
446           OBJECT,
447           asyncDependencies
448               .stream()
449               .map(ProducerFactoryGenerator::dependencyFutureName)
450               .collect(joining(", ")));
451     }
452 
453     @Override
applyArgType()454     TypeName applyArgType() {
455       return listOf(OBJECT);
456     }
457 
458     @Override
applyArgName()459     String applyArgName() {
460       return "args";
461     }
462 
463     @Override
parameterCodeBlocks()464     ImmutableList<CodeBlock> parameterCodeBlocks() {
465       int argIndex = 0;
466       ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
467       for (DependencyRequest dependency : binding.explicitDependencies()) {
468         if (isAsyncDependency(dependency)) {
469           codeBlocks.add(
470               CodeBlock.of(
471                   "($T) $L.get($L)", asyncDependencyType(dependency), applyArgName(), argIndex));
472           argIndex++;
473         } else {
474           codeBlocks.add(frameworkTypeUsageStatement(dependency));
475         }
476       }
477       return codeBlocks.build();
478     }
479 
480     @Override
hasUncheckedCast()481     boolean hasUncheckedCast() {
482       return true;
483     }
484   }
485 
isAsyncDependency(DependencyRequest dependency)486   private static boolean isAsyncDependency(DependencyRequest dependency) {
487     switch (dependency.kind()) {
488       case INSTANCE:
489       case PRODUCED:
490         return true;
491       default:
492         return false;
493     }
494   }
495 
asyncDependencyType(DependencyRequest dependency)496   private static TypeName asyncDependencyType(DependencyRequest dependency) {
497     TypeName keyName = dependency.key().type().xprocessing().getTypeName();
498     switch (dependency.kind()) {
499       case INSTANCE:
500         return keyName;
501       case PRODUCED:
502         return producedOf(keyName);
503       default:
504         throw new AssertionError();
505     }
506   }
507 
508   /**
509    * Creates a code block for the invocation of the producer method from the module, which should be
510    * used entirely within a method body.
511    *
512    * @param binding The binding to generate the invocation code block for.
513    * @param providedTypeName The type name that should be provided by this producer.
514    * @param parameterCodeBlocks The code blocks for all the parameters to the producer method.
515    */
getInvocationCodeBlock( ProductionBinding binding, TypeName providedTypeName, ImmutableList<CodeBlock> parameterCodeBlocks)516   private CodeBlock getInvocationCodeBlock(
517       ProductionBinding binding,
518       TypeName providedTypeName,
519       ImmutableList<CodeBlock> parameterCodeBlocks) {
520     CodeBlock moduleCodeBlock =
521         CodeBlock.of(
522             "$L.$L($L)",
523             binding.requiresModuleInstance()
524                 ? "module"
525                 : CodeBlock.of("$T", binding.bindingTypeElement().get().getClassName()),
526             getSimpleName(binding.bindingElement().get()),
527             makeParametersCodeBlock(parameterCodeBlocks));
528 
529     final CodeBlock returnCodeBlock;
530     switch (binding.productionKind().get()) {
531       case IMMEDIATE:
532         returnCodeBlock =
533             CodeBlock.of("$T.<$T>immediateFuture($L)", FUTURES, providedTypeName, moduleCodeBlock);
534         break;
535       case FUTURE:
536         returnCodeBlock = moduleCodeBlock;
537         break;
538       case SET_OF_FUTURE:
539         returnCodeBlock = CodeBlock.of("$T.allAsSet($L)", PRODUCERS, moduleCodeBlock);
540         break;
541       default:
542         throw new AssertionError();
543     }
544     return CodeBlock.of("return $L;", returnCodeBlock);
545   }
546 
547   @Override
warningSuppressions()548   protected ImmutableSet<Suppression> warningSuppressions() {
549     // TODO(beder): examine if we can remove this or prevent subtypes of Future from being produced
550     return ImmutableSet.of(FUTURE_RETURN_VALUE_IGNORED);
551   }
552 }
553