1 /* 2 * Copyright (C) 2020 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.processingstep; 18 19 import static com.google.common.collect.Iterables.getOnlyElement; 20 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedFactoryMethods; 21 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors; 22 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; 23 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 24 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 25 import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY; 26 import static dagger.internal.codegen.javapoet.TypeNames.daggerProviderOf; 27 import static dagger.internal.codegen.javapoet.TypeNames.providerOf; 28 import static dagger.internal.codegen.langmodel.Accessibility.accessibleTypeName; 29 import static dagger.internal.codegen.xprocessing.MethodSpecs.overriding; 30 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement; 31 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 32 import static dagger.internal.codegen.xprocessing.XMethodElements.hasTypeParameters; 33 import static dagger.internal.codegen.xprocessing.XProcessingEnvs.isPreJava8SourceVersion; 34 import static dagger.internal.codegen.xprocessing.XTypeElements.typeVariableNames; 35 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 36 import static java.util.stream.Collectors.joining; 37 import static javax.lang.model.element.Modifier.FINAL; 38 import static javax.lang.model.element.Modifier.PRIVATE; 39 import static javax.lang.model.element.Modifier.PUBLIC; 40 import static javax.lang.model.element.Modifier.STATIC; 41 42 import androidx.room.compiler.processing.XElement; 43 import androidx.room.compiler.processing.XExecutableParameterElement; 44 import androidx.room.compiler.processing.XFiler; 45 import androidx.room.compiler.processing.XMessager; 46 import androidx.room.compiler.processing.XMethodElement; 47 import androidx.room.compiler.processing.XProcessingEnv; 48 import androidx.room.compiler.processing.XType; 49 import androidx.room.compiler.processing.XTypeElement; 50 import com.google.common.collect.ImmutableList; 51 import com.google.common.collect.ImmutableSet; 52 import com.squareup.javapoet.ClassName; 53 import com.squareup.javapoet.CodeBlock; 54 import com.squareup.javapoet.FieldSpec; 55 import com.squareup.javapoet.MethodSpec; 56 import com.squareup.javapoet.ParameterSpec; 57 import com.squareup.javapoet.ParameterizedTypeName; 58 import com.squareup.javapoet.TypeName; 59 import com.squareup.javapoet.TypeSpec; 60 import dagger.internal.codegen.base.SourceFileGenerationException; 61 import dagger.internal.codegen.base.SourceFileGenerator; 62 import dagger.internal.codegen.binding.AssistedInjectionAnnotations; 63 import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedFactoryMetadata; 64 import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedParameter; 65 import dagger.internal.codegen.binding.BindingFactory; 66 import dagger.internal.codegen.binding.MethodSignatureFormatter; 67 import dagger.internal.codegen.binding.ProvisionBinding; 68 import dagger.internal.codegen.javapoet.TypeNames; 69 import dagger.internal.codegen.validation.ValidationReport; 70 import dagger.internal.codegen.xprocessing.XTypes; 71 import java.util.HashSet; 72 import java.util.Optional; 73 import java.util.Set; 74 import javax.inject.Inject; 75 76 /** An annotation processor for {@link dagger.assisted.AssistedFactory}-annotated types. */ 77 final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<XTypeElement> { 78 private final XProcessingEnv processingEnv; 79 @SuppressWarnings("HidingField") 80 private final XMessager messager; 81 private final XFiler filer; 82 private final BindingFactory bindingFactory; 83 private final MethodSignatureFormatter methodSignatureFormatter; 84 @SuppressWarnings("HidingField") 85 private final SuperficialValidator superficialValidator; 86 87 @Inject AssistedFactoryProcessingStep( XProcessingEnv processingEnv, XMessager messager, XFiler filer, BindingFactory bindingFactory, MethodSignatureFormatter methodSignatureFormatter, SuperficialValidator superficialValidator)88 AssistedFactoryProcessingStep( 89 XProcessingEnv processingEnv, 90 XMessager messager, 91 XFiler filer, 92 BindingFactory bindingFactory, 93 MethodSignatureFormatter methodSignatureFormatter, 94 SuperficialValidator superficialValidator) { 95 this.processingEnv = processingEnv; 96 this.messager = messager; 97 this.filer = filer; 98 this.bindingFactory = bindingFactory; 99 this.methodSignatureFormatter = methodSignatureFormatter; 100 this.superficialValidator = superficialValidator; 101 } 102 103 @Override annotationClassNames()104 public ImmutableSet<ClassName> annotationClassNames() { 105 return ImmutableSet.of(TypeNames.ASSISTED_FACTORY); 106 } 107 108 @Override process(XTypeElement factory, ImmutableSet<ClassName> annotations)109 protected void process(XTypeElement factory, ImmutableSet<ClassName> annotations) { 110 ValidationReport report = new AssistedFactoryValidator().validate(factory); 111 report.printMessagesTo(messager); 112 if (report.isClean()) { 113 try { 114 ProvisionBinding binding = bindingFactory.assistedFactoryBinding(factory, Optional.empty()); 115 new AssistedFactoryImplGenerator().generate(binding); 116 } catch (SourceFileGenerationException e) { 117 e.printMessageTo(messager); 118 } 119 } 120 } 121 122 private final class AssistedFactoryValidator { validate(XTypeElement factory)123 ValidationReport validate(XTypeElement factory) { 124 ValidationReport.Builder report = ValidationReport.about(factory); 125 126 if (!factory.isAbstract()) { 127 return report 128 .addError( 129 "The @AssistedFactory-annotated type must be either an abstract class or " 130 + "interface.", 131 factory) 132 .build(); 133 } 134 135 if (factory.isNested() && !factory.isStatic()) { 136 report.addError("Nested @AssistedFactory-annotated types must be static. ", factory); 137 } 138 139 ImmutableSet<XMethodElement> abstractFactoryMethods = assistedFactoryMethods(factory); 140 141 if (abstractFactoryMethods.isEmpty()) { 142 report.addError( 143 "The @AssistedFactory-annotated type is missing an abstract, non-default method " 144 + "whose return type matches the assisted injection type.", 145 factory); 146 } 147 148 for (XMethodElement method : abstractFactoryMethods) { 149 XType returnType = method.asMemberOf(factory.getType()).getReturnType(); 150 // The default superficial validation only applies to the @AssistedFactory-annotated 151 // element, so we have to manually check the superficial validation of the @AssistedInject 152 // element before using it to ensure it's ready for processing. 153 if (isDeclared(returnType)) { 154 superficialValidator.throwIfNearestEnclosingTypeNotValid(returnType.getTypeElement()); 155 } 156 if (!isAssistedInjectionType(returnType)) { 157 report.addError( 158 String.format( 159 "Invalid return type: %s. An assisted factory's abstract method must return a " 160 + "type with an @AssistedInject-annotated constructor.", 161 XTypes.toStableString(returnType)), 162 method); 163 } 164 if (hasTypeParameters(method)) { 165 report.addError( 166 "@AssistedFactory does not currently support type parameters in the creator " 167 + "method. See https://github.com/google/dagger/issues/2279", 168 method); 169 } 170 } 171 172 if (abstractFactoryMethods.size() > 1) { 173 report.addError( 174 "The @AssistedFactory-annotated type should contain a single abstract, non-default" 175 + " method but found multiple: " 176 + abstractFactoryMethods.stream() 177 .map(methodSignatureFormatter::formatWithoutReturnType) 178 .collect(toImmutableList()), 179 factory); 180 } 181 182 if (!report.build().isClean()) { 183 return report.build(); 184 } 185 186 AssistedFactoryMetadata metadata = AssistedFactoryMetadata.create(factory.getType()); 187 188 // Note: We check uniqueness of the @AssistedInject constructor parameters in 189 // AssistedInjectProcessingStep. We need to check uniqueness for here too because we may 190 // have resolved some type parameters that were not resolved in the @AssistedInject type. 191 Set<AssistedParameter> uniqueAssistedParameters = new HashSet<>(); 192 for (AssistedParameter assistedParameter : metadata.assistedFactoryAssistedParameters()) { 193 if (!uniqueAssistedParameters.add(assistedParameter)) { 194 report.addError( 195 "@AssistedFactory method has duplicate @Assisted types: " + assistedParameter, 196 assistedParameter.element()); 197 } 198 } 199 200 if (!ImmutableSet.copyOf(metadata.assistedInjectAssistedParameters()) 201 .equals(ImmutableSet.copyOf(metadata.assistedFactoryAssistedParameters()))) { 202 report.addError( 203 String.format( 204 "The parameters in the factory method must match the @Assisted parameters in %s." 205 + "\n Actual: %s#%s(%s)" 206 + "\n Expected: %s#%s(%s)", 207 XTypes.toStableString(metadata.assistedInjectType()), 208 metadata.factory().getQualifiedName(), 209 getSimpleName(metadata.factoryMethod()), 210 metadata.factoryMethod().getParameters().stream() 211 .map(XExecutableParameterElement::getType) 212 .map(XTypes::toStableString) 213 .collect(joining(", ")), 214 metadata.factory().getQualifiedName(), 215 getSimpleName(metadata.factoryMethod()), 216 metadata.assistedInjectAssistedParameters().stream() 217 .map(AssistedParameter::type) 218 .map(XTypes::toStableString) 219 .collect(joining(", "))), 220 metadata.factoryMethod()); 221 } 222 223 return report.build(); 224 } 225 isAssistedInjectionType(XType type)226 private boolean isAssistedInjectionType(XType type) { 227 return isDeclared(type) 228 && AssistedInjectionAnnotations.isAssistedInjectionType(type.getTypeElement()); 229 } 230 } 231 232 /** Generates an implementation of the {@link dagger.assisted.AssistedFactory}-annotated class. */ 233 private final class AssistedFactoryImplGenerator extends SourceFileGenerator<ProvisionBinding> { AssistedFactoryImplGenerator()234 AssistedFactoryImplGenerator() { 235 super(filer, processingEnv); 236 } 237 238 @Override originatingElement(ProvisionBinding binding)239 public XElement originatingElement(ProvisionBinding binding) { 240 return binding.bindingElement().get(); 241 } 242 243 // For each @AssistedFactory-annotated type, we generates a class named "*_Impl" that implements 244 // that type. 245 // 246 // Note that this class internally delegates to the @AssistedInject generated class, which 247 // contains the actual implementation logic for creating the @AssistedInject type. The reason we 248 // need both of these generated classes is because while the @AssistedInject generated class 249 // knows how to create the @AssistedInject type, it doesn't know about all of the 250 // @AssistedFactory interfaces that it needs to extend when it's generated. Thus, the role of 251 // the @AssistedFactory generated class is purely to implement the @AssistedFactory type. 252 // Furthermore, while we could have put all of the logic into the @AssistedFactory generated 253 // class and not generate the @AssistedInject generated class, having the @AssistedInject 254 // generated class ensures we have proper accessibility to the @AssistedInject type, and reduces 255 // duplicate logic if there are multiple @AssistedFactory types for the same @AssistedInject 256 // type. 257 // 258 // Example: 259 // public class FooFactory_Impl implements FooFactory { 260 // private final Foo_Factory delegateFactory; 261 // 262 // FooFactory_Impl(Foo_Factory delegateFactory) { 263 // this.delegateFactory = delegateFactory; 264 // } 265 // 266 // @Override 267 // public Foo createFoo(AssistedDep assistedDep) { 268 // return delegateFactory.get(assistedDep); 269 // } 270 // 271 // public static Provider<FooFactory> create(Foo_Factory delegateFactory) { 272 // return InstanceFactory.create(new FooFactory_Impl(delegateFactory)); 273 // } 274 // } 275 @Override topLevelTypes(ProvisionBinding binding)276 public ImmutableList<TypeSpec.Builder> topLevelTypes(ProvisionBinding binding) { 277 XTypeElement factory = asTypeElement(binding.bindingElement().get()); 278 279 ClassName name = generatedClassNameForBinding(binding); 280 TypeSpec.Builder builder = 281 TypeSpec.classBuilder(name) 282 .addModifiers(PUBLIC, FINAL) 283 .addTypeVariables(typeVariableNames(factory)); 284 285 if (factory.isInterface()) { 286 builder.addSuperinterface(factory.getType().getTypeName()); 287 } else { 288 builder.superclass(factory.getType().getTypeName()); 289 } 290 291 AssistedFactoryMetadata metadata = AssistedFactoryMetadata.create(factory.getType()); 292 ParameterSpec delegateFactoryParam = 293 ParameterSpec.builder( 294 delegateFactoryTypeName(metadata.assistedInjectType()), "delegateFactory") 295 .build(); 296 builder 297 .addField( 298 FieldSpec.builder(delegateFactoryParam.type, delegateFactoryParam.name) 299 .addModifiers(PRIVATE, FINAL) 300 .build()) 301 .addMethod( 302 MethodSpec.constructorBuilder() 303 .addParameter(delegateFactoryParam) 304 .addStatement("this.$1N = $1N", delegateFactoryParam) 305 .build()) 306 .addMethod( 307 overriding(metadata.factoryMethod(), metadata.factoryType()) 308 .addStatement( 309 "return $N.get($L)", 310 delegateFactoryParam, 311 // Use the order of the parameters from the @AssistedInject constructor but 312 // use the parameter names of the @AssistedFactory method. 313 metadata.assistedInjectAssistedParameters().stream() 314 .map(metadata.assistedFactoryAssistedParametersMap()::get) 315 .map(param -> CodeBlock.of("$L", param.getJvmName())) 316 .collect(toParametersCodeBlock())) 317 .build()) 318 // In a future release, we should delete this javax method. This will still be a breaking 319 // change, but keeping compatibility for a while should reduce the likelihood of breakages 320 // as it would require components built at much older versions using factories built at 321 // newer versions to break. 322 .addMethod( 323 MethodSpec.methodBuilder("create") 324 .addModifiers(PUBLIC, STATIC) 325 .addParameter(delegateFactoryParam) 326 .addTypeVariables(typeVariableNames(metadata.assistedInjectElement())) 327 .returns(providerOf(factory.getType().getTypeName())) 328 .addStatement( 329 "return $T.$Lcreate(new $T($N))", 330 INSTANCE_FACTORY, 331 // Java 7 type inference requires the method call provide the exact type here. 332 isPreJava8SourceVersion(processingEnv) 333 ? CodeBlock.of( 334 "<$T>", 335 accessibleTypeName(metadata.factoryType(), name, processingEnv)) 336 : CodeBlock.of(""), 337 name, 338 delegateFactoryParam) 339 .build()) 340 // Normally we would have called this just "create", but because of backwards 341 // compatibility we can't have two methods with the same name/arguments returning 342 // different Provider types. 343 .addMethod( 344 MethodSpec.methodBuilder("createFactoryProvider") 345 .addModifiers(PUBLIC, STATIC) 346 .addParameter(delegateFactoryParam) 347 .addTypeVariables(typeVariableNames(metadata.assistedInjectElement())) 348 .returns(daggerProviderOf(factory.getType().getTypeName())) 349 .addStatement( 350 "return $T.$Lcreate(new $T($N))", 351 INSTANCE_FACTORY, 352 // Java 7 type inference requires the method call provide the exact type here. 353 isPreJava8SourceVersion(processingEnv) 354 ? CodeBlock.of( 355 "<$T>", 356 accessibleTypeName(metadata.factoryType(), name, processingEnv)) 357 : CodeBlock.of(""), 358 name, 359 delegateFactoryParam) 360 .build()); 361 return ImmutableList.of(builder); 362 } 363 364 /** Returns the generated factory {@link TypeName type} for an @AssistedInject constructor. */ delegateFactoryTypeName(XType assistedInjectType)365 private TypeName delegateFactoryTypeName(XType assistedInjectType) { 366 // The name of the generated factory for the assisted inject type, 367 // e.g. an @AssistedInject Foo(...) {...} constructor will generate a Foo_Factory class. 368 ClassName generatedFactoryClassName = 369 generatedClassNameForBinding( 370 bindingFactory.injectionBinding( 371 getOnlyElement(assistedInjectedConstructors(assistedInjectType.getTypeElement())), 372 Optional.empty())); 373 374 // Return the factory type resolved with the same type parameters as the assisted inject type. 375 return assistedInjectType.getTypeArguments().isEmpty() 376 ? generatedFactoryClassName 377 : ParameterizedTypeName.get( 378 generatedFactoryClassName, 379 assistedInjectType.getTypeArguments().stream() 380 .map(XType::getTypeName) 381 .collect(toImmutableList()) 382 .toArray(new TypeName[0])); 383 } 384 } 385 } 386