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