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; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations; 20 import static com.google.auto.common.Visibility.PRIVATE; 21 import static com.google.auto.common.Visibility.PUBLIC; 22 import static com.google.auto.common.Visibility.effectiveVisibilityOfElement; 23 import static com.google.common.collect.Iterables.getOnlyElement; 24 import static dagger.internal.codegen.ComponentAnnotation.componentAnnotation; 25 import static dagger.internal.codegen.ComponentAnnotation.isComponentAnnotation; 26 import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation; 27 import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations; 28 import static dagger.internal.codegen.ConfigurationAnnotations.getSubcomponentCreator; 29 import static dagger.internal.codegen.DaggerStreams.toImmutableSet; 30 import static dagger.internal.codegen.ModuleAnnotation.isModuleAnnotation; 31 import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation; 32 import static dagger.internal.codegen.MoreAnnotationMirrors.simpleName; 33 import static dagger.internal.codegen.MoreAnnotationValues.asType; 34 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent; 35 import static dagger.internal.codegen.ValidationType.NONE; 36 import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror; 37 import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent; 38 import static java.util.EnumSet.noneOf; 39 import static java.util.stream.Collectors.joining; 40 import static javax.lang.model.element.Modifier.ABSTRACT; 41 import static javax.lang.model.element.Modifier.STATIC; 42 import static javax.lang.model.util.ElementFilter.methodsIn; 43 44 import com.google.auto.common.MoreElements; 45 import com.google.auto.common.MoreTypes; 46 import com.google.auto.common.Visibility; 47 import com.google.common.base.Joiner; 48 import com.google.common.collect.ArrayListMultimap; 49 import com.google.common.collect.ImmutableList; 50 import com.google.common.collect.ImmutableSet; 51 import com.google.common.collect.ListMultimap; 52 import com.google.common.collect.Sets; 53 import com.google.errorprone.annotations.FormatMethod; 54 import dagger.Module; 55 import dagger.Subcomponent; 56 import dagger.internal.codegen.langmodel.DaggerElements; 57 import dagger.internal.codegen.langmodel.DaggerTypes; 58 import dagger.model.BindingGraph; 59 import dagger.producers.ProducerModule; 60 import dagger.producers.ProductionSubcomponent; 61 import java.lang.annotation.Annotation; 62 import java.util.Collection; 63 import java.util.EnumSet; 64 import java.util.HashMap; 65 import java.util.HashSet; 66 import java.util.List; 67 import java.util.Map; 68 import java.util.Map.Entry; 69 import java.util.Optional; 70 import java.util.Set; 71 import javax.inject.Inject; 72 import javax.inject.Scope; 73 import javax.inject.Singleton; 74 import javax.lang.model.element.AnnotationMirror; 75 import javax.lang.model.element.AnnotationValue; 76 import javax.lang.model.element.ElementKind; 77 import javax.lang.model.element.ExecutableElement; 78 import javax.lang.model.element.TypeElement; 79 import javax.lang.model.type.DeclaredType; 80 import javax.lang.model.type.TypeMirror; 81 import javax.lang.model.util.SimpleAnnotationValueVisitor8; 82 import javax.lang.model.util.SimpleTypeVisitor8; 83 84 /** 85 * A {@linkplain ValidationReport validator} for {@link Module}s or {@link ProducerModule}s. 86 */ 87 @Singleton 88 final class ModuleValidator { 89 private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_TYPES = 90 ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class); 91 private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_CREATOR_TYPES = 92 ImmutableSet.of( 93 Subcomponent.Builder.class, 94 Subcomponent.Factory.class, 95 ProductionSubcomponent.Builder.class, 96 ProductionSubcomponent.Factory.class); 97 private static final Optional<Class<?>> ANDROID_PROCESSOR; 98 private static final String CONTRIBUTES_ANDROID_INJECTOR_NAME = 99 "dagger.android.ContributesAndroidInjector"; 100 private static final String ANDROID_PROCESSOR_NAME = "dagger.android.processor.AndroidProcessor"; 101 102 static { 103 Class<?> clazz; 104 try { 105 clazz = Class.forName(ANDROID_PROCESSOR_NAME, false, ModuleValidator.class.getClassLoader()); 106 } catch (ClassNotFoundException ignored) { 107 clazz = null; 108 } 109 ANDROID_PROCESSOR = Optional.ofNullable(clazz); 110 } 111 112 private final DaggerTypes types; 113 private final DaggerElements elements; 114 private final AnyBindingMethodValidator anyBindingMethodValidator; 115 private final MethodSignatureFormatter methodSignatureFormatter; 116 private final ComponentDescriptorFactory componentDescriptorFactory; 117 private final BindingGraphFactory bindingGraphFactory; 118 private final BindingGraphConverter bindingGraphConverter; 119 private final BindingGraphValidator bindingGraphValidator; 120 private final CompilerOptions compilerOptions; 121 private final Map<TypeElement, ValidationReport<TypeElement>> cache = new HashMap<>(); 122 private final Set<TypeElement> knownModules = new HashSet<>(); 123 124 @Inject ModuleValidator( DaggerTypes types, DaggerElements elements, AnyBindingMethodValidator anyBindingMethodValidator, MethodSignatureFormatter methodSignatureFormatter, ComponentDescriptorFactory componentDescriptorFactory, BindingGraphFactory bindingGraphFactory, BindingGraphConverter bindingGraphConverter, BindingGraphValidator bindingGraphValidator, CompilerOptions compilerOptions)125 ModuleValidator( 126 DaggerTypes types, 127 DaggerElements elements, 128 AnyBindingMethodValidator anyBindingMethodValidator, 129 MethodSignatureFormatter methodSignatureFormatter, 130 ComponentDescriptorFactory componentDescriptorFactory, 131 BindingGraphFactory bindingGraphFactory, 132 BindingGraphConverter bindingGraphConverter, 133 BindingGraphValidator bindingGraphValidator, 134 CompilerOptions compilerOptions) { 135 this.types = types; 136 this.elements = elements; 137 this.anyBindingMethodValidator = anyBindingMethodValidator; 138 this.methodSignatureFormatter = methodSignatureFormatter; 139 this.componentDescriptorFactory = componentDescriptorFactory; 140 this.bindingGraphFactory = bindingGraphFactory; 141 this.bindingGraphConverter = bindingGraphConverter; 142 this.bindingGraphValidator = bindingGraphValidator; 143 this.compilerOptions = compilerOptions; 144 } 145 146 /** 147 * Adds {@code modules} to the set of module types that will be validated during this compilation 148 * step. If a component or module includes a module that is not in this set, that included module 149 * is assumed to be valid because it was processed in a previous compilation step. If it were 150 * invalid, that previous compilation step would have failed and blocked this one. 151 * 152 * <p>This logic depends on this method being called before {@linkplain #validate(TypeElement) 153 * validating} any module or {@linkplain #validateReferencedModules(TypeElement, AnnotationMirror, 154 * ImmutableSet, Set) component}. 155 */ addKnownModules(Collection<TypeElement> modules)156 void addKnownModules(Collection<TypeElement> modules) { 157 knownModules.addAll(modules); 158 } 159 160 /** Returns a validation report for a module type. */ validate(TypeElement module)161 ValidationReport<TypeElement> validate(TypeElement module) { 162 return validate(module, new HashSet<>()); 163 } 164 validate( TypeElement module, Set<TypeElement> visitedModules)165 private ValidationReport<TypeElement> validate( 166 TypeElement module, Set<TypeElement> visitedModules) { 167 if (visitedModules.add(module)) { 168 return reentrantComputeIfAbsent(cache, module, m -> validateUncached(module, visitedModules)); 169 } 170 return ValidationReport.about(module).build(); 171 } 172 validateUncached( TypeElement module, Set<TypeElement> visitedModules)173 private ValidationReport<TypeElement> validateUncached( 174 TypeElement module, Set<TypeElement> visitedModules) { 175 ValidationReport.Builder<TypeElement> builder = ValidationReport.about(module); 176 ModuleKind moduleKind = ModuleKind.forAnnotatedElement(module).get(); 177 178 ListMultimap<String, ExecutableElement> allMethodsByName = ArrayListMultimap.create(); 179 ListMultimap<String, ExecutableElement> bindingMethodsByName = ArrayListMultimap.create(); 180 181 Set<ModuleMethodKind> methodKinds = noneOf(ModuleMethodKind.class); 182 TypeElement contributesAndroidInjectorElement = 183 elements.getTypeElement(CONTRIBUTES_ANDROID_INJECTOR_NAME); 184 TypeMirror contributesAndroidInjector = 185 contributesAndroidInjectorElement != null 186 ? contributesAndroidInjectorElement.asType() 187 : null; 188 for (ExecutableElement moduleMethod : methodsIn(module.getEnclosedElements())) { 189 if (anyBindingMethodValidator.isBindingMethod(moduleMethod)) { 190 builder.addSubreport(anyBindingMethodValidator.validate(moduleMethod)); 191 bindingMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod); 192 methodKinds.add(ModuleMethodKind.ofMethod(moduleMethod)); 193 } 194 allMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod); 195 196 for (AnnotationMirror annotation : moduleMethod.getAnnotationMirrors()) { 197 if (!ANDROID_PROCESSOR.isPresent() 198 && MoreTypes.equivalence() 199 .equivalent(contributesAndroidInjector, annotation.getAnnotationType())) { 200 builder.addSubreport( 201 ValidationReport.about(moduleMethod) 202 .addError( 203 String.format( 204 "@%s was used, but %s was not found on the processor path", 205 CONTRIBUTES_ANDROID_INJECTOR_NAME, ANDROID_PROCESSOR_NAME)) 206 .build()); 207 break; 208 } 209 } 210 } 211 212 if (methodKinds.containsAll( 213 EnumSet.of(ModuleMethodKind.ABSTRACT_DECLARATION, ModuleMethodKind.INSTANCE_BINDING))) { 214 builder.addError( 215 String.format( 216 "A @%s may not contain both non-static and abstract binding methods", 217 moduleKind.annotation().getSimpleName())); 218 } 219 220 validateModuleVisibility(module, moduleKind, builder); 221 validateMethodsWithSameName(builder, bindingMethodsByName); 222 if (module.getKind() != ElementKind.INTERFACE) { 223 validateBindingMethodOverrides(module, builder, allMethodsByName, bindingMethodsByName); 224 } 225 validateModifiers(module, builder); 226 validateReferencedModules(module, moduleKind, visitedModules, builder); 227 validateReferencedSubcomponents(module, moduleKind, builder); 228 validateNoScopeAnnotationsOnModuleElement(module, moduleKind, builder); 229 validateSelfCycles(module, builder); 230 231 if (builder.build().isClean() 232 && !compilerOptions.fullBindingGraphValidationType(module).equals(NONE)) { 233 validateModuleBindings(module, builder); 234 } 235 236 return builder.build(); 237 } 238 validateReferencedSubcomponents( final TypeElement subject, ModuleKind moduleKind, final ValidationReport.Builder<TypeElement> builder)239 private void validateReferencedSubcomponents( 240 final TypeElement subject, 241 ModuleKind moduleKind, 242 final ValidationReport.Builder<TypeElement> builder) { 243 // TODO(ronshapiro): use validateTypesAreDeclared when it is checked in 244 ModuleAnnotation moduleAnnotation = moduleAnnotation(moduleKind.getModuleAnnotation(subject)); 245 for (AnnotationValue subcomponentAttribute : 246 moduleAnnotation.subcomponentsAsAnnotationValues()) { 247 asType(subcomponentAttribute) 248 .accept( 249 new SimpleTypeVisitor8<Void, Void>() { 250 @Override 251 protected Void defaultAction(TypeMirror e, Void aVoid) { 252 builder.addError( 253 e + " is not a valid subcomponent type", 254 subject, 255 moduleAnnotation.annotation(), 256 subcomponentAttribute); 257 return null; 258 } 259 260 @Override 261 public Void visitDeclared(DeclaredType declaredType, Void aVoid) { 262 TypeElement attributeType = MoreTypes.asTypeElement(declaredType); 263 if (isAnyAnnotationPresent(attributeType, SUBCOMPONENT_TYPES)) { 264 validateSubcomponentHasBuilder( 265 attributeType, moduleAnnotation.annotation(), builder); 266 } else { 267 builder.addError( 268 isAnyAnnotationPresent(attributeType, SUBCOMPONENT_CREATOR_TYPES) 269 ? moduleSubcomponentsIncludesCreator(attributeType) 270 : moduleSubcomponentsIncludesNonSubcomponent(attributeType), 271 subject, 272 moduleAnnotation.annotation(), 273 subcomponentAttribute); 274 } 275 276 return null; 277 } 278 }, 279 null); 280 } 281 } 282 moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent)283 private static String moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent) { 284 return notSubcomponent.getQualifiedName() 285 + " is not a @Subcomponent or @ProductionSubcomponent"; 286 } 287 moduleSubcomponentsIncludesCreator( TypeElement moduleSubcomponentsAttribute)288 private static String moduleSubcomponentsIncludesCreator( 289 TypeElement moduleSubcomponentsAttribute) { 290 TypeElement subcomponentType = 291 MoreElements.asType(moduleSubcomponentsAttribute.getEnclosingElement()); 292 ComponentCreatorAnnotation creatorAnnotation = 293 getOnlyElement(getCreatorAnnotations(moduleSubcomponentsAttribute)); 294 return String.format( 295 "%s is a @%s.%s. Did you mean to use %s?", 296 moduleSubcomponentsAttribute.getQualifiedName(), 297 subcomponentAnnotation(subcomponentType).get().simpleName(), 298 creatorAnnotation.creatorKind().typeName(), 299 subcomponentType.getQualifiedName()); 300 } 301 validateSubcomponentHasBuilder( TypeElement subcomponentAttribute, AnnotationMirror moduleAnnotation, ValidationReport.Builder<TypeElement> builder)302 private static void validateSubcomponentHasBuilder( 303 TypeElement subcomponentAttribute, 304 AnnotationMirror moduleAnnotation, 305 ValidationReport.Builder<TypeElement> builder) { 306 if (getSubcomponentCreator(subcomponentAttribute).isPresent()) { 307 return; 308 } 309 builder.addError( 310 moduleSubcomponentsDoesntHaveCreator(subcomponentAttribute, moduleAnnotation), 311 builder.getSubject(), 312 moduleAnnotation); 313 } 314 moduleSubcomponentsDoesntHaveCreator( TypeElement subcomponent, AnnotationMirror moduleAnnotation)315 private static String moduleSubcomponentsDoesntHaveCreator( 316 TypeElement subcomponent, AnnotationMirror moduleAnnotation) { 317 return String.format( 318 "%1$s doesn't have a @%2$s.Builder or @%2$s.Factory, which is required when used with " 319 + "@%3$s.subcomponents", 320 subcomponent.getQualifiedName(), 321 subcomponentAnnotation(subcomponent).get().simpleName(), 322 simpleName(moduleAnnotation)); 323 } 324 325 enum ModuleMethodKind { 326 ABSTRACT_DECLARATION, 327 INSTANCE_BINDING, 328 STATIC_BINDING, 329 ; 330 ofMethod(ExecutableElement moduleMethod)331 static ModuleMethodKind ofMethod(ExecutableElement moduleMethod) { 332 if (moduleMethod.getModifiers().contains(STATIC)) { 333 return STATIC_BINDING; 334 } else if (moduleMethod.getModifiers().contains(ABSTRACT)) { 335 return ABSTRACT_DECLARATION; 336 } else { 337 return INSTANCE_BINDING; 338 } 339 } 340 } 341 validateModifiers( TypeElement subject, ValidationReport.Builder<TypeElement> builder)342 private void validateModifiers( 343 TypeElement subject, ValidationReport.Builder<TypeElement> builder) { 344 // This coupled with the check for abstract modules in ComponentValidator guarantees that 345 // only modules without type parameters are referenced from @Component(modules={...}). 346 if (!subject.getTypeParameters().isEmpty() && !subject.getModifiers().contains(ABSTRACT)) { 347 builder.addError("Modules with type parameters must be abstract", subject); 348 } 349 } 350 validateMethodsWithSameName( ValidationReport.Builder<TypeElement> builder, ListMultimap<String, ExecutableElement> bindingMethodsByName)351 private void validateMethodsWithSameName( 352 ValidationReport.Builder<TypeElement> builder, 353 ListMultimap<String, ExecutableElement> bindingMethodsByName) { 354 for (Entry<String, Collection<ExecutableElement>> entry : 355 bindingMethodsByName.asMap().entrySet()) { 356 if (entry.getValue().size() > 1) { 357 for (ExecutableElement offendingMethod : entry.getValue()) { 358 builder.addError( 359 String.format( 360 "Cannot have more than one binding method with the same name in a single module"), 361 offendingMethod); 362 } 363 } 364 } 365 } 366 validateReferencedModules( TypeElement subject, ModuleKind moduleKind, Set<TypeElement> visitedModules, ValidationReport.Builder<TypeElement> builder)367 private void validateReferencedModules( 368 TypeElement subject, 369 ModuleKind moduleKind, 370 Set<TypeElement> visitedModules, 371 ValidationReport.Builder<TypeElement> builder) { 372 // Validate that all the modules we include are valid for inclusion. 373 AnnotationMirror mirror = moduleKind.getModuleAnnotation(subject); 374 builder.addSubreport( 375 validateReferencedModules( 376 subject, mirror, moduleKind.legalIncludedModuleKinds(), visitedModules)); 377 } 378 379 /** 380 * Validates modules included in a given module or installed in a given component. 381 * 382 * <p>Checks that the referenced modules are non-generic types annotated with {@code @Module} or 383 * {@code @ProducerModule}. 384 * 385 * <p>If the referenced module is in the {@linkplain #addKnownModules(Collection) known modules 386 * set} and has errors, reports an error at that module's inclusion. 387 * 388 * @param annotatedType the annotated module or component 389 * @param annotation the annotation specifying the referenced modules ({@code @Component}, 390 * {@code @ProductionComponent}, {@code @Subcomponent}, {@code @ProductionSubcomponent}, 391 * {@code @Module}, or {@code @ProducerModule}) 392 * @param validModuleKinds the module kinds that the annotated type is permitted to include 393 */ validateReferencedModules( TypeElement annotatedType, AnnotationMirror annotation, ImmutableSet<ModuleKind> validModuleKinds, Set<TypeElement> visitedModules)394 ValidationReport<TypeElement> validateReferencedModules( 395 TypeElement annotatedType, 396 AnnotationMirror annotation, 397 ImmutableSet<ModuleKind> validModuleKinds, 398 Set<TypeElement> visitedModules) { 399 ValidationReport.Builder<TypeElement> subreport = ValidationReport.about(annotatedType); 400 ImmutableSet<? extends Class<? extends Annotation>> validModuleAnnotations = 401 validModuleKinds.stream().map(ModuleKind::annotation).collect(toImmutableSet()); 402 403 for (AnnotationValue includedModule : getModules(annotation)) { 404 asType(includedModule) 405 .accept( 406 new SimpleTypeVisitor8<Void, Void>() { 407 @Override 408 protected Void defaultAction(TypeMirror mirror, Void p) { 409 reportError("%s is not a valid module type.", mirror); 410 return null; 411 } 412 413 @Override 414 public Void visitDeclared(DeclaredType t, Void p) { 415 TypeElement module = MoreElements.asType(t.asElement()); 416 if (!t.getTypeArguments().isEmpty()) { 417 reportError( 418 "%s is listed as a module, but has type parameters", 419 module.getQualifiedName()); 420 } 421 if (!isAnyAnnotationPresent(module, validModuleAnnotations)) { 422 reportError( 423 "%s is listed as a module, but is not annotated with %s", 424 module.getQualifiedName(), 425 (validModuleAnnotations.size() > 1 ? "one of " : "") 426 + validModuleAnnotations 427 .stream() 428 .map(otherClass -> "@" + otherClass.getSimpleName()) 429 .collect(joining(", "))); 430 } else if (knownModules.contains(module) 431 && !validate(module, visitedModules).isClean()) { 432 reportError("%s has errors", module.getQualifiedName()); 433 } 434 return null; 435 } 436 437 @FormatMethod 438 private void reportError(String format, Object... args) { 439 subreport.addError( 440 String.format(format, args), annotatedType, annotation, includedModule); 441 } 442 }, 443 null); 444 } 445 return subreport.build(); 446 } 447 getModules(AnnotationMirror annotation)448 private static ImmutableList<AnnotationValue> getModules(AnnotationMirror annotation) { 449 if (isModuleAnnotation(annotation)) { 450 return moduleAnnotation(annotation).includesAsAnnotationValues(); 451 } 452 if (isComponentAnnotation(annotation)) { 453 return componentAnnotation(annotation).moduleValues(); 454 } 455 throw new IllegalArgumentException(String.format("unsupported annotation: %s", annotation)); 456 } 457 validateBindingMethodOverrides( TypeElement subject, ValidationReport.Builder<TypeElement> builder, ListMultimap<String, ExecutableElement> allMethodsByName, ListMultimap<String, ExecutableElement> bindingMethodsByName)458 private void validateBindingMethodOverrides( 459 TypeElement subject, 460 ValidationReport.Builder<TypeElement> builder, 461 ListMultimap<String, ExecutableElement> allMethodsByName, 462 ListMultimap<String, ExecutableElement> bindingMethodsByName) { 463 // For every binding method, confirm it overrides nothing *and* nothing overrides it. 464 // Consider the following hierarchy: 465 // class Parent { 466 // @Provides Foo a() {} 467 // @Provides Foo b() {} 468 // Foo c() {} 469 // } 470 // class Child extends Parent { 471 // @Provides Foo a() {} 472 // Foo b() {} 473 // @Provides Foo c() {} 474 // } 475 // In each of those cases, we want to fail. "a" is clear, "b" because Child is overriding 476 // a binding method in Parent, and "c" because Child is defining a binding method that overrides 477 // Parent. 478 TypeElement currentClass = subject; 479 TypeMirror objectType = elements.getTypeElement(Object.class).asType(); 480 // We keep track of methods that failed so we don't spam with multiple failures. 481 Set<ExecutableElement> failedMethods = Sets.newHashSet(); 482 while (!types.isSameType(currentClass.getSuperclass(), objectType)) { 483 currentClass = MoreElements.asType(types.asElement(currentClass.getSuperclass())); 484 List<ExecutableElement> superclassMethods = methodsIn(currentClass.getEnclosedElements()); 485 for (ExecutableElement superclassMethod : superclassMethods) { 486 String name = superclassMethod.getSimpleName().toString(); 487 // For each method in the superclass, confirm our binding methods don't override it 488 for (ExecutableElement bindingMethod : bindingMethodsByName.get(name)) { 489 if (failedMethods.add(bindingMethod) 490 && elements.overrides(bindingMethod, superclassMethod, subject)) { 491 builder.addError( 492 String.format( 493 "Binding methods may not override another method. Overrides: %s", 494 methodSignatureFormatter.format(superclassMethod)), 495 bindingMethod); 496 } 497 } 498 // For each binding method in superclass, confirm our methods don't override it. 499 if (anyBindingMethodValidator.isBindingMethod(superclassMethod)) { 500 for (ExecutableElement method : allMethodsByName.get(name)) { 501 if (failedMethods.add(method) 502 && elements.overrides(method, superclassMethod, subject)) { 503 builder.addError( 504 String.format( 505 "Binding methods may not be overridden in modules. Overrides: %s", 506 methodSignatureFormatter.format(superclassMethod)), 507 method); 508 } 509 } 510 } 511 allMethodsByName.put(superclassMethod.getSimpleName().toString(), superclassMethod); 512 } 513 } 514 } 515 validateModuleVisibility( final TypeElement moduleElement, ModuleKind moduleKind, final ValidationReport.Builder<?> reportBuilder)516 private void validateModuleVisibility( 517 final TypeElement moduleElement, 518 ModuleKind moduleKind, 519 final ValidationReport.Builder<?> reportBuilder) { 520 ModuleAnnotation moduleAnnotation = 521 moduleAnnotation(getAnnotationMirror(moduleElement, moduleKind.annotation()).get()); 522 Visibility moduleVisibility = Visibility.ofElement(moduleElement); 523 Visibility moduleEffectiveVisibility = effectiveVisibilityOfElement(moduleElement); 524 if (moduleVisibility.equals(PRIVATE)) { 525 reportBuilder.addError("Modules cannot be private.", moduleElement); 526 } else if (moduleEffectiveVisibility.equals(PRIVATE)) { 527 reportBuilder.addError("Modules cannot be enclosed in private types.", moduleElement); 528 } 529 530 switch (moduleElement.getNestingKind()) { 531 case ANONYMOUS: 532 throw new IllegalStateException("Can't apply @Module to an anonymous class"); 533 case LOCAL: 534 throw new IllegalStateException("Local classes shouldn't show up in the processor"); 535 case MEMBER: 536 case TOP_LEVEL: 537 if (moduleEffectiveVisibility.equals(PUBLIC)) { 538 ImmutableSet<TypeElement> invalidVisibilityIncludes = 539 getModuleIncludesWithInvalidVisibility(moduleAnnotation); 540 if (!invalidVisibilityIncludes.isEmpty()) { 541 reportBuilder.addError( 542 String.format( 543 "This module is public, but it includes non-public (or effectively non-public) " 544 + "modules (%s) that have non-static, non-abstract binding methods. Either " 545 + "reduce the visibility of this module, make the included modules " 546 + "public, or make all of the binding methods on the included modules " 547 + "abstract or static.", 548 formatListForErrorMessage(invalidVisibilityIncludes.asList())), 549 moduleElement); 550 } 551 } 552 } 553 } 554 getModuleIncludesWithInvalidVisibility( ModuleAnnotation moduleAnnotation)555 private ImmutableSet<TypeElement> getModuleIncludesWithInvalidVisibility( 556 ModuleAnnotation moduleAnnotation) { 557 return moduleAnnotation.includes().stream() 558 .filter(include -> !effectiveVisibilityOfElement(include).equals(PUBLIC)) 559 .filter(this::requiresModuleInstance) 560 .collect(toImmutableSet()); 561 } 562 563 /** 564 * Returns {@code true} if a module instance is needed for any of the binding methods on the 565 * given {@code module}. This is the case when the module has any binding methods that are neither 566 * {@code abstract} nor {@code static}. 567 */ requiresModuleInstance(TypeElement module)568 private boolean requiresModuleInstance(TypeElement module) { 569 // Note elements.getAllMembers(module) rather than module.getEnclosedElements() here: we need to 570 // include binding methods declared in supertypes because unlike most other validations being 571 // done in this class, which assume that supertype binding methods will be validated in a 572 // separate call to the validator since the supertype itself must be a @Module, we need to look 573 // at all the binding methods in the module's type hierarchy here. 574 return methodsIn(elements.getAllMembers(module)).stream() 575 .filter(method -> anyBindingMethodValidator.isBindingMethod(method)) 576 .map(ExecutableElement::getModifiers) 577 .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC)); 578 } 579 validateNoScopeAnnotationsOnModuleElement( TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report)580 private void validateNoScopeAnnotationsOnModuleElement( 581 TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report) { 582 for (AnnotationMirror scope : getAnnotatedAnnotations(module, Scope.class)) { 583 report.addError( 584 String.format( 585 "@%ss cannot be scoped. Did you mean to scope a method instead?", 586 moduleKind.annotation().getSimpleName()), 587 module, 588 scope); 589 } 590 } 591 validateSelfCycles( TypeElement module, ValidationReport.Builder<TypeElement> builder)592 private void validateSelfCycles( 593 TypeElement module, ValidationReport.Builder<TypeElement> builder) { 594 ModuleAnnotation moduleAnnotation = moduleAnnotation(module).get(); 595 moduleAnnotation 596 .includesAsAnnotationValues() 597 .forEach( 598 value -> 599 value.accept( 600 new SimpleAnnotationValueVisitor8<Void, Void>() { 601 @Override 602 public Void visitType(TypeMirror includedModule, Void aVoid) { 603 if (MoreTypes.equivalence().equivalent(module.asType(), includedModule)) { 604 String moduleKind = moduleAnnotation.annotationClass().getSimpleName(); 605 builder.addError( 606 String.format("@%s cannot include themselves.", moduleKind), 607 module, 608 moduleAnnotation.annotation(), 609 value); 610 } 611 return null; 612 } 613 }, 614 null)); 615 } 616 validateModuleBindings( TypeElement module, ValidationReport.Builder<TypeElement> report)617 private void validateModuleBindings( 618 TypeElement module, ValidationReport.Builder<TypeElement> report) { 619 BindingGraph bindingGraph = 620 bindingGraphConverter.convert( 621 bindingGraphFactory.create( 622 componentDescriptorFactory.moduleComponentDescriptor(module), true)); 623 if (!bindingGraphValidator.isValid(bindingGraph)) { 624 // Since the validator uses a DiagnosticReporter to report errors, the ValdiationReport won't 625 // have any Items for them. We have to tell the ValidationReport that some errors were 626 // reported for the subject. 627 report.markDirty(); 628 } 629 } 630 formatListForErrorMessage(List<?> things)631 private static String formatListForErrorMessage(List<?> things) { 632 switch (things.size()) { 633 case 0: 634 return ""; 635 case 1: 636 return things.get(0).toString(); 637 default: 638 StringBuilder output = new StringBuilder(); 639 Joiner.on(", ").appendTo(output, things.subList(0, things.size() - 1)); 640 output.append(" and ").append(things.get(things.size() - 1)); 641 return output.toString(); 642 } 643 } 644 } 645