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.validation; 18 19 import static com.google.auto.common.MoreElements.asType; 20 import static com.google.auto.common.MoreElements.isAnnotationPresent; 21 import static com.google.auto.common.MoreTypes.asDeclared; 22 import static com.google.auto.common.MoreTypes.asExecutable; 23 import static com.google.auto.common.MoreTypes.asTypeElement; 24 import static com.google.common.base.Verify.verify; 25 import static com.google.common.collect.Iterables.getOnlyElement; 26 import static com.google.common.collect.Multimaps.asMap; 27 import static com.google.common.collect.Sets.intersection; 28 import static dagger.internal.codegen.base.ComponentAnnotation.anyComponentAnnotation; 29 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation; 30 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent; 31 import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.creatorAnnotationsFor; 32 import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.productionCreatorAnnotations; 33 import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.subcomponentCreatorAnnotations; 34 import static dagger.internal.codegen.binding.ComponentKind.annotationsFor; 35 import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes; 36 import static dagger.internal.codegen.binding.ConfigurationAnnotations.getTransitiveModules; 37 import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.builderMethodRequiresNoArgs; 38 import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent; 39 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 40 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 41 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror; 42 import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation; 43 import static java.util.Comparator.comparing; 44 import static javax.lang.model.element.ElementKind.CLASS; 45 import static javax.lang.model.element.ElementKind.INTERFACE; 46 import static javax.lang.model.element.Modifier.ABSTRACT; 47 import static javax.lang.model.type.TypeKind.VOID; 48 import static javax.lang.model.util.ElementFilter.methodsIn; 49 50 import com.google.auto.common.MoreTypes; 51 import com.google.common.collect.HashMultimap; 52 import com.google.common.collect.ImmutableList; 53 import com.google.common.collect.ImmutableSet; 54 import com.google.common.collect.LinkedHashMultimap; 55 import com.google.common.collect.Maps; 56 import com.google.common.collect.SetMultimap; 57 import com.google.common.collect.Sets; 58 import dagger.Component; 59 import dagger.Reusable; 60 import dagger.internal.codegen.base.ClearableCache; 61 import dagger.internal.codegen.base.ComponentAnnotation; 62 import dagger.internal.codegen.binding.ComponentKind; 63 import dagger.internal.codegen.binding.DependencyRequestFactory; 64 import dagger.internal.codegen.binding.ErrorMessages; 65 import dagger.internal.codegen.binding.MethodSignatureFormatter; 66 import dagger.internal.codegen.binding.ModuleKind; 67 import dagger.internal.codegen.langmodel.DaggerElements; 68 import dagger.internal.codegen.langmodel.DaggerTypes; 69 import dagger.model.DependencyRequest; 70 import dagger.model.Key; 71 import dagger.producers.CancellationPolicy; 72 import dagger.producers.ProductionComponent; 73 import java.lang.annotation.Annotation; 74 import java.util.Collection; 75 import java.util.HashMap; 76 import java.util.HashSet; 77 import java.util.List; 78 import java.util.Map; 79 import java.util.Optional; 80 import java.util.Set; 81 import javax.inject.Inject; 82 import javax.inject.Singleton; 83 import javax.lang.model.element.AnnotationMirror; 84 import javax.lang.model.element.Element; 85 import javax.lang.model.element.ExecutableElement; 86 import javax.lang.model.element.TypeElement; 87 import javax.lang.model.element.VariableElement; 88 import javax.lang.model.type.DeclaredType; 89 import javax.lang.model.type.ExecutableType; 90 import javax.lang.model.type.TypeMirror; 91 import javax.lang.model.type.TypeVisitor; 92 import javax.lang.model.util.SimpleTypeVisitor8; 93 94 /** 95 * Performs superficial validation of the contract of the {@link Component} and {@link 96 * ProductionComponent} annotations. 97 */ 98 @Singleton 99 public final class ComponentValidator implements ClearableCache { 100 private final DaggerElements elements; 101 private final DaggerTypes types; 102 private final ModuleValidator moduleValidator; 103 private final ComponentCreatorValidator creatorValidator; 104 private final DependencyRequestValidator dependencyRequestValidator; 105 private final MembersInjectionValidator membersInjectionValidator; 106 private final MethodSignatureFormatter methodSignatureFormatter; 107 private final DependencyRequestFactory dependencyRequestFactory; 108 private final Map<TypeElement, ValidationReport<TypeElement>> reports = new HashMap<>(); 109 110 @Inject ComponentValidator( DaggerElements elements, DaggerTypes types, ModuleValidator moduleValidator, ComponentCreatorValidator creatorValidator, DependencyRequestValidator dependencyRequestValidator, MembersInjectionValidator membersInjectionValidator, MethodSignatureFormatter methodSignatureFormatter, DependencyRequestFactory dependencyRequestFactory)111 ComponentValidator( 112 DaggerElements elements, 113 DaggerTypes types, 114 ModuleValidator moduleValidator, 115 ComponentCreatorValidator creatorValidator, 116 DependencyRequestValidator dependencyRequestValidator, 117 MembersInjectionValidator membersInjectionValidator, 118 MethodSignatureFormatter methodSignatureFormatter, 119 DependencyRequestFactory dependencyRequestFactory) { 120 this.elements = elements; 121 this.types = types; 122 this.moduleValidator = moduleValidator; 123 this.creatorValidator = creatorValidator; 124 this.dependencyRequestValidator = dependencyRequestValidator; 125 this.membersInjectionValidator = membersInjectionValidator; 126 this.methodSignatureFormatter = methodSignatureFormatter; 127 this.dependencyRequestFactory = dependencyRequestFactory; 128 } 129 130 @Override clearCache()131 public void clearCache() { 132 reports.clear(); 133 } 134 135 /** Validates the given component. */ validate(TypeElement component)136 public ValidationReport<TypeElement> validate(TypeElement component) { 137 return reentrantComputeIfAbsent(reports, component, this::validateUncached); 138 } 139 validateUncached(TypeElement component)140 private ValidationReport<TypeElement> validateUncached(TypeElement component) { 141 return new ElementValidator(component).validateElement(); 142 } 143 144 private class ElementValidator { 145 private final TypeElement component; 146 private final ValidationReport.Builder<TypeElement> report; 147 private final ImmutableSet<ComponentKind> componentKinds; 148 149 // Populated by ComponentMethodValidators 150 private final SetMultimap<Element, ExecutableElement> referencedSubcomponents = 151 LinkedHashMultimap.create(); 152 ElementValidator(TypeElement component)153 ElementValidator(TypeElement component) { 154 this.component = component; 155 this.report = ValidationReport.about(component); 156 this.componentKinds = ComponentKind.getComponentKinds(component); 157 } 158 componentKind()159 private ComponentKind componentKind() { 160 return getOnlyElement(componentKinds); 161 } 162 componentAnnotation()163 private ComponentAnnotation componentAnnotation() { 164 return anyComponentAnnotation(component).get(); 165 } 166 componentType()167 private DeclaredType componentType() { 168 return asDeclared(component.asType()); 169 } 170 validateElement()171 ValidationReport<TypeElement> validateElement() { 172 if (componentKinds.size() > 1) { 173 return moreThanOneComponentAnnotation(); 174 } 175 176 validateUseOfCancellationPolicy(); 177 validateIsAbstractType(); 178 validateCreators(); 179 validateNoReusableAnnotation(); 180 validateComponentMethods(); 181 validateNoConflictingEntryPoints(); 182 validateSubcomponentReferences(); 183 validateComponentDependencies(); 184 validateReferencedModules(); 185 validateSubcomponents(); 186 187 return report.build(); 188 } 189 moreThanOneComponentAnnotation()190 private ValidationReport<TypeElement> moreThanOneComponentAnnotation() { 191 String error = 192 "Components may not be annotated with more than one component annotation: found " 193 + annotationsFor(componentKinds); 194 report.addError(error, component); 195 return report.build(); 196 } 197 validateUseOfCancellationPolicy()198 private void validateUseOfCancellationPolicy() { 199 if (isAnnotationPresent(component, CancellationPolicy.class) 200 && !componentKind().isProducer()) { 201 report.addError( 202 "@CancellationPolicy may only be applied to production components and subcomponents", 203 component); 204 } 205 } 206 validateIsAbstractType()207 private void validateIsAbstractType() { 208 if (!component.getKind().equals(INTERFACE) 209 && !(component.getKind().equals(CLASS) && component.getModifiers().contains(ABSTRACT))) { 210 report.addError( 211 String.format( 212 "@%s may only be applied to an interface or abstract class", 213 componentKind().annotation().getSimpleName()), 214 component); 215 } 216 } 217 validateCreators()218 private void validateCreators() { 219 ImmutableList<DeclaredType> creators = 220 creatorAnnotationsFor(componentAnnotation()).stream() 221 .flatMap(annotation -> enclosedAnnotatedTypes(component, annotation).stream()) 222 .collect(toImmutableList()); 223 creators.forEach( 224 creator -> report.addSubreport(creatorValidator.validate(asTypeElement(creator)))); 225 if (creators.size() > 1) { 226 report.addError( 227 String.format( 228 ErrorMessages.componentMessagesFor(componentKind()).moreThanOne(), creators), 229 component); 230 } 231 } 232 validateNoReusableAnnotation()233 private void validateNoReusableAnnotation() { 234 Optional<AnnotationMirror> reusableAnnotation = 235 getAnnotationMirror(component, Reusable.class); 236 if (reusableAnnotation.isPresent()) { 237 report.addError( 238 "@Reusable cannot be applied to components or subcomponents", 239 component, 240 reusableAnnotation.get()); 241 } 242 } 243 validateComponentMethods()244 private void validateComponentMethods() { 245 elements.getUnimplementedMethods(component).stream() 246 .map(ComponentMethodValidator::new) 247 .forEachOrdered(ComponentMethodValidator::validateMethod); 248 } 249 250 private class ComponentMethodValidator { 251 private final ExecutableElement method; 252 private final ExecutableType resolvedMethod; 253 private final List<? extends TypeMirror> parameterTypes; 254 private final List<? extends VariableElement> parameters; 255 private final TypeMirror returnType; 256 ComponentMethodValidator(ExecutableElement method)257 ComponentMethodValidator(ExecutableElement method) { 258 this.method = method; 259 this.resolvedMethod = asExecutable(types.asMemberOf(componentType(), method)); 260 this.parameterTypes = resolvedMethod.getParameterTypes(); 261 this.parameters = method.getParameters(); 262 this.returnType = resolvedMethod.getReturnType(); 263 } 264 validateMethod()265 void validateMethod() { 266 validateNoTypeVariables(); 267 268 // abstract methods are ones we have to implement, so they each need to be validated 269 // first, check the return type. if it's a subcomponent, validate that method as 270 // such. 271 Optional<AnnotationMirror> subcomponentAnnotation = subcomponentAnnotation(); 272 if (subcomponentAnnotation.isPresent()) { 273 validateSubcomponentFactoryMethod(subcomponentAnnotation.get()); 274 } else if (subcomponentCreatorAnnotation().isPresent()) { 275 validateSubcomponentCreatorMethod(); 276 } else { 277 // if it's not a subcomponent... 278 switch (parameters.size()) { 279 case 0: 280 validateProvisionMethod(); 281 break; 282 case 1: 283 validateMembersInjectionMethod(); 284 break; 285 default: 286 reportInvalidMethod(); 287 break; 288 } 289 } 290 } 291 validateNoTypeVariables()292 private void validateNoTypeVariables() { 293 if (!resolvedMethod.getTypeVariables().isEmpty()) { 294 report.addError("Component methods cannot have type variables", method); 295 } 296 } 297 subcomponentAnnotation()298 private Optional<AnnotationMirror> subcomponentAnnotation() { 299 return checkForAnnotations( 300 returnType, 301 componentKind().legalSubcomponentKinds().stream() 302 .map(ComponentKind::annotation) 303 .collect(toImmutableSet())); 304 } 305 subcomponentCreatorAnnotation()306 private Optional<AnnotationMirror> subcomponentCreatorAnnotation() { 307 return checkForAnnotations( 308 returnType, 309 componentAnnotation().isProduction() 310 ? intersection(subcomponentCreatorAnnotations(), productionCreatorAnnotations()) 311 : subcomponentCreatorAnnotations()); 312 } 313 validateSubcomponentFactoryMethod(AnnotationMirror subcomponentAnnotation)314 private void validateSubcomponentFactoryMethod(AnnotationMirror subcomponentAnnotation) { 315 referencedSubcomponents.put(MoreTypes.asElement(returnType), method); 316 317 ComponentKind subcomponentKind = 318 ComponentKind.forAnnotatedElement(MoreTypes.asTypeElement(returnType)).get(); 319 ImmutableSet<TypeElement> moduleTypes = 320 ComponentAnnotation.componentAnnotation(subcomponentAnnotation).modules(); 321 322 // TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse 323 // subcomponents and their modules separately from how it is done in ComponentDescriptor and 324 // ModuleDescriptor 325 @SuppressWarnings("deprecation") 326 ImmutableSet<TypeElement> transitiveModules = 327 getTransitiveModules(types, elements, moduleTypes); 328 329 Set<TypeElement> variableTypes = Sets.newHashSet(); 330 331 for (int i = 0; i < parameterTypes.size(); i++) { 332 VariableElement parameter = parameters.get(i); 333 TypeMirror parameterType = parameterTypes.get(i); 334 Optional<TypeElement> moduleType = 335 parameterType.accept( 336 new SimpleTypeVisitor8<Optional<TypeElement>, Void>() { 337 @Override 338 protected Optional<TypeElement> defaultAction(TypeMirror e, Void p) { 339 return Optional.empty(); 340 } 341 342 @Override 343 public Optional<TypeElement> visitDeclared(DeclaredType t, Void p) { 344 for (ModuleKind moduleKind : subcomponentKind.legalModuleKinds()) { 345 if (isAnnotationPresent(t.asElement(), moduleKind.annotation())) { 346 return Optional.of(MoreTypes.asTypeElement(t)); 347 } 348 } 349 return Optional.empty(); 350 } 351 }, 352 null); 353 if (moduleType.isPresent()) { 354 if (variableTypes.contains(moduleType.get())) { 355 report.addError( 356 String.format( 357 "A module may only occur once an an argument in a Subcomponent factory " 358 + "method, but %s was already passed.", 359 moduleType.get().getQualifiedName()), 360 parameter); 361 } 362 if (!transitiveModules.contains(moduleType.get())) { 363 report.addError( 364 String.format( 365 "%s is present as an argument to the %s factory method, but is not one of the" 366 + " modules used to implement the subcomponent.", 367 moduleType.get().getQualifiedName(), 368 MoreTypes.asTypeElement(returnType).getQualifiedName()), 369 method); 370 } 371 variableTypes.add(moduleType.get()); 372 } else { 373 report.addError( 374 String.format( 375 "Subcomponent factory methods may only accept modules, but %s is not.", 376 parameterType), 377 parameter); 378 } 379 } 380 } 381 validateSubcomponentCreatorMethod()382 private void validateSubcomponentCreatorMethod() { 383 referencedSubcomponents.put(MoreTypes.asElement(returnType).getEnclosingElement(), method); 384 385 if (!parameters.isEmpty()) { 386 report.addError(builderMethodRequiresNoArgs(), method); 387 } 388 389 TypeElement creatorElement = MoreTypes.asTypeElement(returnType); 390 // TODO(sameb): The creator validator right now assumes the element is being compiled 391 // in this pass, which isn't true here. We should change error messages to spit out 392 // this method as the subject and add the original subject to the message output. 393 report.addSubreport(creatorValidator.validate(creatorElement)); 394 } 395 validateProvisionMethod()396 private void validateProvisionMethod() { 397 dependencyRequestValidator.validateDependencyRequest(report, method, returnType); 398 } 399 validateMembersInjectionMethod()400 private void validateMembersInjectionMethod() { 401 TypeMirror parameterType = getOnlyElement(parameterTypes); 402 report.addSubreport( 403 membersInjectionValidator.validateMembersInjectionMethod(method, parameterType)); 404 if (!(returnType.getKind().equals(VOID) || types.isSameType(returnType, parameterType))) { 405 report.addError( 406 "Members injection methods may only return the injected type or void.", method); 407 } 408 } 409 reportInvalidMethod()410 private void reportInvalidMethod() { 411 report.addError( 412 "This method isn't a valid provision method, members injection method or " 413 + "subcomponent factory method. Dagger cannot implement this method", 414 method); 415 } 416 } 417 validateNoConflictingEntryPoints()418 private void validateNoConflictingEntryPoints() { 419 // Collect entry point methods that are not overridden by others. If the "same" method is 420 // inherited from more than one supertype, each will be in the multimap. 421 SetMultimap<String, ExecutableElement> entryPointMethods = HashMultimap.create(); 422 423 methodsIn(elements.getAllMembers(component)).stream() 424 .filter( 425 method -> 426 isEntryPoint(method, asExecutable(types.asMemberOf(componentType(), method)))) 427 .forEach( 428 method -> 429 addMethodUnlessOverridden( 430 method, entryPointMethods.get(method.getSimpleName().toString()))); 431 432 for (Set<ExecutableElement> methods : asMap(entryPointMethods).values()) { 433 if (distinctKeys(methods).size() > 1) { 434 reportConflictingEntryPoints(methods); 435 } 436 } 437 } 438 reportConflictingEntryPoints(Collection<ExecutableElement> methods)439 private void reportConflictingEntryPoints(Collection<ExecutableElement> methods) { 440 verify( 441 methods.stream().map(ExecutableElement::getEnclosingElement).distinct().count() 442 == methods.size(), 443 "expected each method to be declared on a different type: %s", 444 methods); 445 StringBuilder message = new StringBuilder("conflicting entry point declarations:"); 446 methodSignatureFormatter 447 .typedFormatter(componentType()) 448 .formatIndentedList( 449 message, 450 ImmutableList.sortedCopyOf( 451 comparing( 452 method -> asType(method.getEnclosingElement()).getQualifiedName().toString()), 453 methods), 454 1); 455 report.addError(message.toString()); 456 } 457 validateSubcomponentReferences()458 private void validateSubcomponentReferences() { 459 Maps.filterValues(referencedSubcomponents.asMap(), methods -> methods.size() > 1) 460 .forEach( 461 (subcomponent, methods) -> 462 report.addError( 463 String.format(moreThanOneRefToSubcomponent(), subcomponent, methods), 464 component)); 465 } 466 validateComponentDependencies()467 private void validateComponentDependencies() { 468 for (TypeMirror type : componentAnnotation().dependencyTypes()) { 469 type.accept(CHECK_DEPENDENCY_TYPES, report); 470 } 471 } 472 validateReferencedModules()473 private void validateReferencedModules() { 474 report.addSubreport( 475 moduleValidator.validateReferencedModules( 476 component, 477 componentAnnotation().annotation(), 478 componentKind().legalModuleKinds(), 479 new HashSet<>())); 480 } 481 validateSubcomponents()482 private void validateSubcomponents() { 483 // Make sure we validate any subcomponents we're referencing. 484 for (Element subcomponent : referencedSubcomponents.keySet()) { 485 ValidationReport<TypeElement> subreport = validate(asType(subcomponent)); 486 report.addSubreport(subreport); 487 } 488 } 489 distinctKeys(Set<ExecutableElement> methods)490 private ImmutableSet<Key> distinctKeys(Set<ExecutableElement> methods) { 491 return methods.stream() 492 .map(this::dependencyRequest) 493 .map(DependencyRequest::key) 494 .collect(toImmutableSet()); 495 } 496 dependencyRequest(ExecutableElement method)497 private DependencyRequest dependencyRequest(ExecutableElement method) { 498 ExecutableType methodType = asExecutable(types.asMemberOf(componentType(), method)); 499 return ComponentKind.forAnnotatedElement(component).get().isProducer() 500 ? dependencyRequestFactory.forComponentProductionMethod(method, methodType) 501 : dependencyRequestFactory.forComponentProvisionMethod(method, methodType); 502 } 503 } 504 isEntryPoint(ExecutableElement method, ExecutableType methodType)505 private static boolean isEntryPoint(ExecutableElement method, ExecutableType methodType) { 506 return method.getModifiers().contains(ABSTRACT) 507 && method.getParameters().isEmpty() 508 && !methodType.getReturnType().getKind().equals(VOID) 509 && methodType.getTypeVariables().isEmpty(); 510 } 511 addMethodUnlessOverridden(ExecutableElement method, Set<ExecutableElement> methods)512 private void addMethodUnlessOverridden(ExecutableElement method, Set<ExecutableElement> methods) { 513 if (methods.stream().noneMatch(existingMethod -> overridesAsDeclared(existingMethod, method))) { 514 methods.removeIf(existingMethod -> overridesAsDeclared(method, existingMethod)); 515 methods.add(method); 516 } 517 } 518 519 /** 520 * Returns {@code true} if {@code overrider} overrides {@code overridden} considered from within 521 * the type that declares {@code overrider}. 522 */ 523 // TODO(dpb): Does this break for ECJ? overridesAsDeclared(ExecutableElement overrider, ExecutableElement overridden)524 private boolean overridesAsDeclared(ExecutableElement overrider, ExecutableElement overridden) { 525 return elements.overrides(overrider, overridden, asType(overrider.getEnclosingElement())); 526 } 527 528 private static final TypeVisitor<Void, ValidationReport.Builder<?>> CHECK_DEPENDENCY_TYPES = 529 new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() { 530 @Override 531 protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) { 532 report.addError(type + " is not a valid component dependency type"); 533 return null; 534 } 535 536 @Override 537 public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) { 538 if (moduleAnnotation(MoreTypes.asTypeElement(type)).isPresent()) { 539 report.addError(type + " is a module, which cannot be a component dependency"); 540 } 541 return null; 542 } 543 }; 544 checkForAnnotations( TypeMirror type, final Set<? extends Class<? extends Annotation>> annotations)545 private static Optional<AnnotationMirror> checkForAnnotations( 546 TypeMirror type, final Set<? extends Class<? extends Annotation>> annotations) { 547 return type.accept( 548 new SimpleTypeVisitor8<Optional<AnnotationMirror>, Void>(Optional.empty()) { 549 @Override 550 public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void p) { 551 return getAnyAnnotation(t.asElement(), annotations); 552 } 553 }, 554 null); 555 } 556 } 557