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