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