• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
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