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