• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google LLC
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 package com.google.auto.common;
17 
18 import static com.google.auto.common.MoreElements.asExecutable;
19 import static com.google.auto.common.MoreElements.asPackage;
20 import static com.google.auto.common.MoreStreams.toImmutableMap;
21 import static com.google.auto.common.MoreStreams.toImmutableSet;
22 import static com.google.auto.common.SuperficialValidation.validateElement;
23 import static com.google.common.base.Preconditions.checkNotNull;
24 import static com.google.common.base.Preconditions.checkState;
25 import static com.google.common.collect.Iterables.transform;
26 import static com.google.common.collect.Multimaps.filterKeys;
27 import static java.util.Objects.requireNonNull;
28 import static javax.lang.model.element.ElementKind.PACKAGE;
29 import static javax.tools.Diagnostic.Kind.ERROR;
30 
31 import com.google.common.base.Ascii;
32 import com.google.common.base.Optional;
33 import com.google.common.base.Predicates;
34 import com.google.common.collect.ImmutableList;
35 import com.google.common.collect.ImmutableMap;
36 import com.google.common.collect.ImmutableSet;
37 import com.google.common.collect.ImmutableSetMultimap;
38 import com.google.common.collect.Iterables;
39 import com.google.common.collect.LinkedHashMultimap;
40 import com.google.common.collect.SetMultimap;
41 import com.google.common.collect.Sets;
42 import java.lang.annotation.Annotation;
43 import java.util.LinkedHashSet;
44 import java.util.Objects;
45 import java.util.Set;
46 import javax.annotation.processing.AbstractProcessor;
47 import javax.annotation.processing.Messager;
48 import javax.annotation.processing.ProcessingEnvironment;
49 import javax.annotation.processing.Processor;
50 import javax.annotation.processing.RoundEnvironment;
51 import javax.lang.model.element.Element;
52 import javax.lang.model.element.ExecutableElement;
53 import javax.lang.model.element.Name;
54 import javax.lang.model.element.PackageElement;
55 import javax.lang.model.element.TypeElement;
56 import javax.lang.model.type.ErrorType;
57 import javax.lang.model.util.Elements;
58 import javax.lang.model.util.SimpleElementVisitor8;
59 import org.checkerframework.checker.nullness.qual.Nullable;
60 
61 /**
62  * An abstract {@link Processor} implementation that defers processing of {@link Element}s to later
63  * rounds if they cannot be processed.
64  *
65  * <p>Subclasses put their processing logic in {@link Step} implementations. The steps are passed to
66  * the processor by returning them in the {@link #steps()} method, and can access the {@link
67  * ProcessingEnvironment} using {@link #processingEnv}.
68  *
69  * <p>Any logic that needs to happen once per round can be specified by overriding {@link
70  * #postRound(RoundEnvironment)}.
71  *
72  * <h3>Ill-formed elements are deferred</h3>
73  *
74  * Any annotated element whose nearest enclosing type is not well-formed is deferred, and not passed
75  * to any {@code Step}. This helps processors to avoid many common pitfalls, such as {@link
76  * ErrorType} instances, {@link ClassCastException}s and badly coerced types.
77  *
78  * <p>A non-package element is considered well-formed if its type, type parameters, parameters,
79  * default values, supertypes, annotations, and enclosed elements are. Package elements are treated
80  * similarly, except that their enclosed elements are not validated. See {@link
81  * SuperficialValidation#validateElement(Element)} for details.
82  *
83  * <p>The primary disadvantage to this validation is that any element that forms a circular
84  * dependency with a type generated by another {@code BasicAnnotationProcessor} will never compile
85  * because the element will never be fully complete. All such compilations will fail with an error
86  * message on the offending type that describes the issue.
87  *
88  * <h3>Each {@code Step} can defer elements</h3>
89  *
90  * <p>Each {@code Step} can defer elements by including them in the set returned by {@link
91  * Step#process(ImmutableSetMultimap)}; elements deferred by a step will be passed back to that step
92  * in a later round of processing.
93  *
94  * <p>This feature is useful when one processor may depend on code generated by another, independent
95  * processor, in a way that isn't caught by the well-formedness check described above. For example,
96  * if an element {@code A} cannot be processed because processing it depends on the existence of
97  * some class {@code B}, then {@code A} should be deferred until a later round of processing, when
98  * {@code B} will have been generated by another processor.
99  *
100  * <p>If {@code A} directly references {@code B}, then the well-formedness check will correctly
101  * defer processing of {@code A} until {@code B} has been generated.
102  *
103  * <p>However, if {@code A} references {@code B} only indirectly (for example, from within a method
104  * body), then the well-formedness check will not defer processing {@code A}, but a processing step
105  * can reject {@code A}.
106  */
107 public abstract class BasicAnnotationProcessor extends AbstractProcessor {
108 
109   private final Set<ElementName> deferredElementNames = new LinkedHashSet<>();
110   private final SetMultimap<Step, ElementName> elementsDeferredBySteps =
111       LinkedHashMultimap.create();
112 
113   private Elements elements;
114   private Messager messager;
115   private ImmutableList<? extends Step> steps;
116 
117   @Override
init(ProcessingEnvironment processingEnv)118   public final synchronized void init(ProcessingEnvironment processingEnv) {
119     super.init(processingEnv);
120     this.elements = processingEnv.getElementUtils();
121     this.messager = processingEnv.getMessager();
122     this.steps = ImmutableList.copyOf(steps());
123   }
124 
125   /**
126    * Creates {@linkplain ProcessingStep processing steps} for this processor. {@link #processingEnv}
127    * is guaranteed to be set when this method is invoked.
128    *
129    * @deprecated Implement {@link #steps()} instead.
130    */
131   @Deprecated
initSteps()132   protected Iterable<? extends ProcessingStep> initSteps() {
133     throw new AssertionError("If steps() is not implemented, initSteps() must be.");
134   }
135 
136   /**
137    * Creates {@linkplain Step processing steps} for this processor. {@link #processingEnv} is
138    * guaranteed to be set when this method is invoked.
139    *
140    * <p>Note: If you are migrating some steps from {@link ProcessingStep} to {@link Step}, then you
141    * can call {@link #asStep(ProcessingStep)} on any unmigrated steps.
142    */
steps()143   protected Iterable<? extends Step> steps() {
144     return Iterables.transform(initSteps(), BasicAnnotationProcessor::asStep);
145   }
146 
147   /**
148    * An optional hook for logic to be executed at the end of each round.
149    *
150    * @deprecated use {@link #postRound(RoundEnvironment)} instead
151    */
152   @Deprecated
postProcess()153   protected void postProcess() {}
154 
155   /** An optional hook for logic to be executed at the end of each round. */
postRound(RoundEnvironment roundEnv)156   protected void postRound(RoundEnvironment roundEnv) {
157     if (!roundEnv.processingOver()) {
158       postProcess();
159     }
160   }
161 
getSupportedAnnotationTypeElements()162   private ImmutableSet<TypeElement> getSupportedAnnotationTypeElements() {
163     checkState(steps != null);
164     return steps.stream()
165         .flatMap(step -> getSupportedAnnotationTypeElements(step).stream())
166         .collect(toImmutableSet());
167   }
168 
getSupportedAnnotationTypeElements(Step step)169   private ImmutableSet<TypeElement> getSupportedAnnotationTypeElements(Step step) {
170     return step.annotations().stream()
171         .map(elements::getTypeElement)
172         .filter(Objects::nonNull)
173         .collect(toImmutableSet());
174   }
175 
176   /**
177    * Returns the set of supported annotation types as collected from registered {@linkplain Step
178    * processing steps}.
179    */
180   @Override
getSupportedAnnotationTypes()181   public final ImmutableSet<String> getSupportedAnnotationTypes() {
182     checkState(steps != null);
183     return steps.stream()
184         .flatMap(step -> step.annotations().stream())
185         .collect(toImmutableSet());
186   }
187 
188   @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)189   public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
190     checkState(elements != null);
191     checkState(messager != null);
192     checkState(steps != null);
193 
194     // If this is the last round, report all of the missing elements if there
195     // were no errors raised in the round; otherwise reporting the missing
196     // elements just adds noise the output.
197     if (roundEnv.processingOver()) {
198       postRound(roundEnv);
199       if (!roundEnv.errorRaised()) {
200         reportMissingElements(
201             ImmutableSet.<ElementName>builder()
202                 .addAll(deferredElementNames)
203                 .addAll(elementsDeferredBySteps.values())
204                 .build());
205       }
206       return false;
207     }
208 
209     process(validElements(roundEnv));
210 
211     postRound(roundEnv);
212 
213     return false;
214   }
215 
216   /** Processes the valid elements, including those previously deferred by each step. */
process(ImmutableSetMultimap<TypeElement, Element> validElements)217   private void process(ImmutableSetMultimap<TypeElement, Element> validElements) {
218     for (Step step : steps) {
219       ImmutableSet<TypeElement> annotationTypes = getSupportedAnnotationTypeElements(step);
220       ImmutableSetMultimap<TypeElement, Element> stepElements =
221           new ImmutableSetMultimap.Builder<TypeElement, Element>()
222               .putAll(indexByAnnotation(elementsDeferredBySteps.get(step), annotationTypes))
223               .putAll(filterKeys(validElements, Predicates.in(annotationTypes)))
224               .build();
225       if (stepElements.isEmpty()) {
226         elementsDeferredBySteps.removeAll(step);
227       } else {
228         Set<? extends Element> rejectedElements =
229             step.process(toClassNameKeyedMultimap(stepElements));
230         elementsDeferredBySteps.replaceValues(
231             step, transform(rejectedElements, ElementName::forAnnotatedElement));
232       }
233     }
234   }
235 
reportMissingElements(Set<ElementName> missingElementNames)236   private void reportMissingElements(Set<ElementName> missingElementNames) {
237     for (ElementName missingElementName : missingElementNames) {
238       Optional<? extends Element> missingElement = missingElementName.getElement(elements);
239       if (missingElement.isPresent()) {
240         messager.printMessage(
241             ERROR,
242             processingErrorMessage(
243                 "this " + Ascii.toLowerCase(missingElement.get().getKind().name())),
244             missingElement.get());
245       } else {
246         messager.printMessage(ERROR, processingErrorMessage(missingElementName.name()));
247       }
248     }
249   }
250 
processingErrorMessage(String target)251   private String processingErrorMessage(String target) {
252     return String.format(
253         "[%s:MiscError] %s was unable to process %s because not all of its dependencies could be "
254             + "resolved. Check for compilation errors or a circular dependency with generated "
255             + "code.",
256         getClass().getSimpleName(), getClass().getCanonicalName(), target);
257   }
258 
259   /**
260    * Returns the valid annotated elements contained in all of the deferred elements. If none are
261    * found for a deferred element, defers it again.
262    */
validElements(RoundEnvironment roundEnv)263   private ImmutableSetMultimap<TypeElement, Element> validElements(RoundEnvironment roundEnv) {
264     ImmutableSet<ElementName> prevDeferredElementNames = ImmutableSet.copyOf(deferredElementNames);
265     deferredElementNames.clear();
266 
267     ImmutableSetMultimap.Builder<TypeElement, Element> deferredElementsByAnnotationBuilder =
268         ImmutableSetMultimap.builder();
269     for (ElementName deferredElementName : prevDeferredElementNames) {
270       Optional<? extends Element> deferredElement = deferredElementName.getElement(elements);
271       if (deferredElement.isPresent()) {
272         findAnnotatedElements(
273             deferredElement.get(),
274             getSupportedAnnotationTypeElements(),
275             deferredElementsByAnnotationBuilder);
276       } else {
277         deferredElementNames.add(deferredElementName);
278       }
279     }
280 
281     ImmutableSetMultimap<TypeElement, Element> deferredElementsByAnnotation =
282         deferredElementsByAnnotationBuilder.build();
283 
284     ImmutableSetMultimap.Builder<TypeElement, Element> validElements =
285         ImmutableSetMultimap.builder();
286 
287     Set<ElementName> validElementNames = new LinkedHashSet<>();
288 
289     // Look at the elements we've found and the new elements from this round and validate them.
290     for (TypeElement annotationType : getSupportedAnnotationTypeElements()) {
291       Set<? extends Element> roundElements = roundEnv.getElementsAnnotatedWith(annotationType);
292       ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationType);
293       for (Element element : Sets.union(roundElements, prevRoundElements)) {
294         ElementName elementName = ElementName.forAnnotatedElement(element);
295         boolean isValidElement =
296             validElementNames.contains(elementName)
297                 || (!deferredElementNames.contains(elementName)
298                     && validateElement(
299                         element.getKind().equals(PACKAGE) ? element : getEnclosingType(element)));
300         if (isValidElement) {
301           validElements.put(annotationType, element);
302           validElementNames.add(elementName);
303         } else {
304           deferredElementNames.add(elementName);
305         }
306       }
307     }
308 
309     return validElements.build();
310   }
311 
indexByAnnotation( Set<ElementName> annotatedElements, ImmutableSet<TypeElement> annotationTypes)312   private ImmutableSetMultimap<TypeElement, Element> indexByAnnotation(
313       Set<ElementName> annotatedElements, ImmutableSet<TypeElement> annotationTypes) {
314     ImmutableSetMultimap.Builder<TypeElement, Element> deferredElements =
315         ImmutableSetMultimap.builder();
316     for (ElementName elementName : annotatedElements) {
317       Optional<? extends Element> element = elementName.getElement(elements);
318       if (element.isPresent()) {
319         findAnnotatedElements(element.get(), annotationTypes, deferredElements);
320       }
321     }
322     return deferredElements.build();
323   }
324 
325   /**
326    * Adds {@code element} and its enclosed elements to {@code annotatedElements} if they are
327    * annotated with any annotations in {@code annotationTypes}. Does not traverse to member types of
328    * {@code element}, so that if {@code Outer} is passed in the example below, looking for
329    * {@code @X}, then {@code Outer}, {@code Outer.foo}, and {@code Outer.foo()} will be added to the
330    * multimap, but neither {@code Inner} nor its members will.
331    *
332    * <pre><code>
333    *   {@literal @}X class Outer {
334    *     {@literal @}X Object foo;
335    *     {@literal @}X void foo() {}
336    *     {@literal @}X static class Inner {
337    *       {@literal @}X Object bar;
338    *       {@literal @}X void bar() {}
339    *     }
340    *   }
341    * </code></pre>
342    */
findAnnotatedElements( Element element, ImmutableSet<TypeElement> annotationTypes, ImmutableSetMultimap.Builder<TypeElement, Element> annotatedElements)343   private static void findAnnotatedElements(
344       Element element,
345       ImmutableSet<TypeElement> annotationTypes,
346       ImmutableSetMultimap.Builder<TypeElement, Element> annotatedElements) {
347     for (Element enclosedElement : element.getEnclosedElements()) {
348       if (!enclosedElement.getKind().isClass() && !enclosedElement.getKind().isInterface()) {
349         findAnnotatedElements(enclosedElement, annotationTypes, annotatedElements);
350       }
351     }
352 
353     // element.getEnclosedElements() does NOT return parameter elements
354     if (element instanceof ExecutableElement) {
355       for (Element parameterElement : asExecutable(element).getParameters()) {
356         findAnnotatedElements(parameterElement, annotationTypes, annotatedElements);
357       }
358     }
359     for (TypeElement annotationType : annotationTypes) {
360       if (isAnnotationPresent(element, annotationType)) {
361         annotatedElements.put(annotationType, element);
362       }
363     }
364   }
365 
isAnnotationPresent(Element element, TypeElement annotationType)366   private static boolean isAnnotationPresent(Element element, TypeElement annotationType) {
367     return element.getAnnotationMirrors().stream()
368         .anyMatch(
369             mirror -> MoreTypes.asTypeElement(mirror.getAnnotationType()).equals(annotationType));
370   }
371 
372   /**
373    * Returns the nearest enclosing {@link TypeElement} to the current element, throwing an {@link
374    * IllegalArgumentException} if the provided {@link Element} is a {@link PackageElement} or is
375    * otherwise not enclosed by a type.
376    */
377   // TODO(user) move to MoreElements and make public.
getEnclosingType(Element element)378   private static TypeElement getEnclosingType(Element element) {
379     return element.accept(
380         new SimpleElementVisitor8<TypeElement, Void>() {
381           @Override
382           protected TypeElement defaultAction(Element e, Void p) {
383             return e.getEnclosingElement().accept(this, p);
384           }
385 
386           @Override
387           public TypeElement visitType(TypeElement e, Void p) {
388             return e;
389           }
390 
391           @Override
392           public TypeElement visitPackage(PackageElement e, Void p) {
393             throw new IllegalArgumentException();
394           }
395         },
396         null);
397   }
398 
399   private static ImmutableSetMultimap<String, Element> toClassNameKeyedMultimap(
400       SetMultimap<TypeElement, Element> elements) {
401     ImmutableSetMultimap.Builder<String, Element> builder = ImmutableSetMultimap.builder();
402     elements
403         .asMap()
404         .forEach(
405             (annotation, element) ->
406                 builder.putAll(annotation.getQualifiedName().toString(), element));
407     return builder.build();
408   }
409 
410   /**
411    * Wraps the passed {@link ProcessingStep} in a {@link Step}. This is a convenience method to
412    * allow incremental migration to a String-based API. This method can be used to return a not yet
413    * converted {@link ProcessingStep} from {@link BasicAnnotationProcessor#steps()}.
414    */
415   protected static Step asStep(ProcessingStep processingStep) {
416     return new ProcessingStepAsStep(processingStep);
417   }
418 
419   /**
420    * The unit of processing logic that runs under the guarantee that all elements are complete and
421    * well-formed. A step may reject elements that are not ready for processing but may be at a later
422    * round.
423    */
424   public interface Step {
425 
426     /**
427      * The set of fully-qualified annotation type names processed by this step.
428      *
429      * <p>Warning: If the returned names are not names of annotations, they'll be ignored.
430      */
431     Set<String> annotations();
432 
433     /**
434      * The implementation of processing logic for the step. It is guaranteed that the keys in {@code
435      * elementsByAnnotation} will be a subset of the set returned by {@link #annotations()}.
436      *
437      * @return the elements (a subset of the values of {@code elementsByAnnotation}) that this step
438      *     is unable to process, possibly until a later processing round. These elements will be
439      *     passed back to this step at the next round of processing.
440      */
441     Set<? extends Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation);
442   }
443 
444   /**
445    * The unit of processing logic that runs under the guarantee that all elements are complete and
446    * well-formed. A step may reject elements that are not ready for processing but may be at a later
447    * round.
448    *
449    * @deprecated Implement {@link Step} instead. See {@link BasicAnnotationProcessor#steps()}.
450    */
451   @Deprecated
452   public interface ProcessingStep {
453 
454     /** The set of annotation types processed by this step. */
455     Set<? extends Class<? extends Annotation>> annotations();
456 
457     /**
458      * The implementation of processing logic for the step. It is guaranteed that the keys in {@code
459      * elementsByAnnotation} will be a subset of the set returned by {@link #annotations()}.
460      *
461      * @return the elements (a subset of the values of {@code elementsByAnnotation}) that this step
462      *     is unable to process, possibly until a later processing round. These elements will be
463      *     passed back to this step at the next round of processing.
464      */
465     Set<? extends Element> process(
466         SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation);
467   }
468 
469   private static class ProcessingStepAsStep implements Step {
470 
471     private final ProcessingStep processingStep;
472     private final ImmutableMap<String, Class<? extends Annotation>> annotationsByName;
473 
474     ProcessingStepAsStep(ProcessingStep processingStep) {
475       this.processingStep = processingStep;
476       this.annotationsByName =
477           processingStep.annotations().stream()
478               .collect(
479                   toImmutableMap(
480                       c -> requireNonNull(c.getCanonicalName()),
481                       (Class<? extends Annotation> aClass) -> aClass));
482     }
483 
484     @Override
485     public Set<String> annotations() {
486       return annotationsByName.keySet();
487     }
488 
489     @Override
490     public Set<? extends Element> process(
491         ImmutableSetMultimap<String, Element> elementsByAnnotation) {
492       return processingStep.process(toClassKeyedMultimap(elementsByAnnotation));
493     }
494 
495     private ImmutableSetMultimap<Class<? extends Annotation>, Element> toClassKeyedMultimap(
496         SetMultimap<String, Element> elements) {
497       ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> builder =
498           ImmutableSetMultimap.builder();
499       elements
500           .asMap()
501           .forEach(
502               (annotationName, annotatedElements) -> {
503                 Class<? extends Annotation> annotation = annotationsByName.get(annotationName);
504                 if (annotation != null) { // should not be null
505                   builder.putAll(annotation, annotatedElements);
506                 }
507               });
508       return builder.build();
509     }
510   }
511 
512   /**
513    * A package or type name.
514    *
515    * <p>It's unfortunate that we have to track types and packages separately, but since there are
516    * two different methods to look them up in {@link Elements}, we end up with a lot of parallel
517    * logic. :(
518    *
519    * <p>Packages declared (and annotated) in {@code package-info.java} are tracked as deferred
520    * packages, type elements are tracked directly, and all other elements are tracked via their
521    * nearest enclosing type.
522    */
523   private static final class ElementName {
524     private enum Kind {
525       PACKAGE_NAME,
526       TYPE_NAME,
527     }
528 
529     private final Kind kind;
530     private final String name;
531 
532     private ElementName(Kind kind, Name name) {
533       this.kind = checkNotNull(kind);
534       this.name = name.toString();
535     }
536 
537     /**
538      * An {@link ElementName} for an annotated element. If {@code element} is a package, uses the
539      * fully qualified name of the package. If it's a type, uses its fully qualified name.
540      * Otherwise, uses the fully-qualified name of the nearest enclosing type.
541      */
542     static ElementName forAnnotatedElement(Element element) {
543       return element.getKind() == PACKAGE
544           ? new ElementName(Kind.PACKAGE_NAME, asPackage(element).getQualifiedName())
545           : new ElementName(Kind.TYPE_NAME, getEnclosingType(element).getQualifiedName());
546     }
547 
548     /** The fully-qualified name of the element. */
549     String name() {
550       return name;
551     }
552 
553     /**
554      * The {@link Element} whose fully-qualified name is {@link #name()}. Absent if the relevant
555      * method on {@link Elements} returns {@code null}.
556      */
557     Optional<? extends Element> getElement(Elements elements) {
558       return Optional.fromNullable(
559           kind == Kind.PACKAGE_NAME
560               ? elements.getPackageElement(name)
561               : elements.getTypeElement(name));
562     }
563 
564     @Override
565     public boolean equals(@Nullable Object object) {
566       if (!(object instanceof ElementName)) {
567         return false;
568       }
569 
570       ElementName that = (ElementName) object;
571       return this.kind == that.kind && this.name.equals(that.name);
572     }
573 
574     @Override
575     public int hashCode() {
576       return Objects.hash(kind, name);
577     }
578   }
579 }
580