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.MoreElements.isAnnotationPresent; 21 import static com.google.auto.common.MoreStreams.toImmutableMap; 22 import static com.google.auto.common.MoreStreams.toImmutableSet; 23 import static com.google.auto.common.SuperficialValidation.validateElement; 24 import static com.google.common.base.Preconditions.checkNotNull; 25 import static com.google.common.base.Preconditions.checkState; 26 import static com.google.common.collect.Iterables.transform; 27 import static com.google.common.collect.Multimaps.filterKeys; 28 import static java.util.Objects.requireNonNull; 29 import static javax.lang.model.element.ElementKind.PACKAGE; 30 import static javax.tools.Diagnostic.Kind.ERROR; 31 32 import com.google.common.base.Ascii; 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.Optional; 46 import java.util.Set; 47 import javax.annotation.processing.AbstractProcessor; 48 import javax.annotation.processing.Messager; 49 import javax.annotation.processing.ProcessingEnvironment; 50 import javax.annotation.processing.Processor; 51 import javax.annotation.processing.RoundEnvironment; 52 import javax.lang.model.element.Element; 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().flatMap(step -> step.annotations().stream()).collect(toImmutableSet()); 184 } 185 186 @Override process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)187 public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 188 checkState(elements != null); 189 checkState(messager != null); 190 checkState(steps != null); 191 192 // If this is the last round, report all of the missing elements if there 193 // were no errors raised in the round; otherwise reporting the missing 194 // elements just adds noise the output. 195 if (roundEnv.processingOver()) { 196 postRound(roundEnv); 197 if (!roundEnv.errorRaised()) { 198 reportMissingElements( 199 ImmutableSet.<ElementName>builder() 200 .addAll(deferredElementNames) 201 .addAll(elementsDeferredBySteps.values()) 202 .build()); 203 } 204 return false; 205 } 206 207 process(validElements(roundEnv)); 208 209 postRound(roundEnv); 210 211 return false; 212 } 213 214 /** Processes the valid elements, including those previously deferred by each step. */ process(ImmutableSetMultimap<TypeElement, Element> validElements)215 private void process(ImmutableSetMultimap<TypeElement, Element> validElements) { 216 for (Step step : steps) { 217 ImmutableSet<TypeElement> annotationTypes = getSupportedAnnotationTypeElements(step); 218 ImmutableSetMultimap<TypeElement, Element> stepElements = 219 new ImmutableSetMultimap.Builder<TypeElement, Element>() 220 .putAll(indexByAnnotation(elementsDeferredBySteps.get(step), annotationTypes)) 221 .putAll(filterKeys(validElements, Predicates.in(annotationTypes))) 222 .build(); 223 if (stepElements.isEmpty()) { 224 elementsDeferredBySteps.removeAll(step); 225 } else { 226 Set<? extends Element> rejectedElements = 227 step.process(toClassNameKeyedMultimap(stepElements)); 228 elementsDeferredBySteps.replaceValues( 229 step, transform(rejectedElements, ElementName::forAnnotatedElement)); 230 } 231 } 232 } 233 reportMissingElements(Set<ElementName> missingElementNames)234 private void reportMissingElements(Set<ElementName> missingElementNames) { 235 for (ElementName missingElementName : missingElementNames) { 236 Optional<? extends Element> missingElement = missingElementName.getElement(elements); 237 if (missingElement.isPresent()) { 238 messager.printMessage( 239 ERROR, 240 processingErrorMessage( 241 "this " + Ascii.toLowerCase(missingElement.get().getKind().name())), 242 missingElement.get()); 243 } else { 244 messager.printMessage(ERROR, processingErrorMessage(missingElementName.name())); 245 } 246 } 247 } 248 processingErrorMessage(String target)249 private String processingErrorMessage(String target) { 250 return String.format( 251 "[%s:MiscError] %s was unable to process %s because not all of its dependencies could be " 252 + "resolved. Check for compilation errors or a circular dependency with generated " 253 + "code.", 254 getClass().getSimpleName(), getClass().getCanonicalName(), target); 255 } 256 257 /** 258 * Returns the valid annotated elements contained in all of the deferred elements. If none are 259 * found for a deferred element, defers it again. 260 */ validElements(RoundEnvironment roundEnv)261 private ImmutableSetMultimap<TypeElement, Element> validElements(RoundEnvironment roundEnv) { 262 ImmutableSet<ElementName> prevDeferredElementNames = ImmutableSet.copyOf(deferredElementNames); 263 deferredElementNames.clear(); 264 265 ImmutableSetMultimap.Builder<TypeElement, Element> deferredElementsByAnnotationBuilder = 266 ImmutableSetMultimap.builder(); 267 for (ElementName deferredElementName : prevDeferredElementNames) { 268 Optional<? extends Element> deferredElement = deferredElementName.getElement(elements); 269 if (deferredElement.isPresent()) { 270 findAnnotatedElements( 271 deferredElement.get(), 272 getSupportedAnnotationTypeElements(), 273 deferredElementsByAnnotationBuilder); 274 } else { 275 deferredElementNames.add(deferredElementName); 276 } 277 } 278 279 ImmutableSetMultimap<TypeElement, Element> deferredElementsByAnnotation = 280 deferredElementsByAnnotationBuilder.build(); 281 282 ImmutableSetMultimap.Builder<TypeElement, Element> validElements = 283 ImmutableSetMultimap.builder(); 284 285 Set<ElementName> validElementNames = new LinkedHashSet<>(); 286 287 // Look at the elements we've found and the new elements from this round and validate them. 288 for (TypeElement annotationType : getSupportedAnnotationTypeElements()) { 289 Set<? extends Element> roundElements = roundEnv.getElementsAnnotatedWith(annotationType); 290 ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationType); 291 for (Element element : Sets.union(roundElements, prevRoundElements)) { 292 ElementName elementName = ElementName.forAnnotatedElement(element); 293 boolean isValidElement = 294 validElementNames.contains(elementName) 295 || (!deferredElementNames.contains(elementName) 296 && validateElement( 297 element.getKind().equals(PACKAGE) ? element : getEnclosingType(element))); 298 if (isValidElement) { 299 validElements.put(annotationType, element); 300 validElementNames.add(elementName); 301 } else { 302 deferredElementNames.add(elementName); 303 } 304 } 305 } 306 307 return validElements.build(); 308 } 309 indexByAnnotation( Set<ElementName> annotatedElements, ImmutableSet<TypeElement> annotationTypes)310 private ImmutableSetMultimap<TypeElement, Element> indexByAnnotation( 311 Set<ElementName> annotatedElements, ImmutableSet<TypeElement> annotationTypes) { 312 ImmutableSetMultimap.Builder<TypeElement, Element> deferredElements = 313 ImmutableSetMultimap.builder(); 314 for (ElementName elementName : annotatedElements) { 315 Optional<? extends Element> element = elementName.getElement(elements); 316 if (element.isPresent()) { 317 findAnnotatedElements(element.get(), annotationTypes, deferredElements); 318 } 319 } 320 return deferredElements.build(); 321 } 322 323 /** 324 * Adds {@code element} and its enclosed elements to {@code annotatedElements} if they are 325 * annotated with any annotations in {@code annotationTypes}. Does not traverse to member types of 326 * {@code element}, so that if {@code Outer} is passed in the example below, looking for 327 * {@code @X}, then {@code Outer}, {@code Outer.foo}, and {@code Outer.foo()} will be added to the 328 * multimap, but neither {@code Inner} nor its members will. 329 * 330 * <pre><code> 331 * {@literal @}X class Outer { 332 * {@literal @}X Object foo; 333 * {@literal @}X void foo() {} 334 * {@literal @}X static class Inner { 335 * {@literal @}X Object bar; 336 * {@literal @}X void bar() {} 337 * } 338 * } 339 * </code></pre> 340 */ findAnnotatedElements( Element element, ImmutableSet<TypeElement> annotationTypes, ImmutableSetMultimap.Builder<TypeElement, Element> annotatedElements)341 private static void findAnnotatedElements( 342 Element element, 343 ImmutableSet<TypeElement> annotationTypes, 344 ImmutableSetMultimap.Builder<TypeElement, Element> annotatedElements) { 345 for (Element enclosedElement : element.getEnclosedElements()) { 346 if (!enclosedElement.getKind().isClass() && !enclosedElement.getKind().isInterface()) { 347 findAnnotatedElements(enclosedElement, annotationTypes, annotatedElements); 348 } 349 } 350 351 // element.getEnclosedElements() does NOT return parameter elements 352 switch (element.getKind()) { 353 case METHOD: 354 case CONSTRUCTOR: 355 for (Element parameterElement : asExecutable(element).getParameters()) { 356 findAnnotatedElements(parameterElement, annotationTypes, annotatedElements); 357 } 358 break; 359 default: // do nothing 360 } 361 for (TypeElement annotationType : annotationTypes) { 362 if (isAnnotationPresent(element, annotationType)) { 363 annotatedElements.put(annotationType, element); 364 } 365 } 366 } 367 368 /** 369 * Returns the nearest enclosing {@link TypeElement} to the current element, throwing an {@link 370 * IllegalArgumentException} if the provided {@link Element} is a {@link PackageElement} or is 371 * otherwise not enclosed by a type. 372 */ 373 // TODO(user) move to MoreElements and make public. getEnclosingType(Element element)374 private static TypeElement getEnclosingType(Element element) { 375 return element.accept( 376 new SimpleElementVisitor8<TypeElement, Void>() { 377 @Override 378 protected TypeElement defaultAction(Element e, Void p) { 379 return e.getEnclosingElement().accept(this, p); 380 } 381 382 @Override 383 public TypeElement visitType(TypeElement e, Void p) { 384 return e; 385 } 386 387 @Override 388 public TypeElement visitPackage(PackageElement e, Void p) { 389 throw new IllegalArgumentException(); 390 } 391 }, 392 null); 393 } 394 395 private static ImmutableSetMultimap<String, Element> toClassNameKeyedMultimap( 396 SetMultimap<TypeElement, Element> elements) { 397 ImmutableSetMultimap.Builder<String, Element> builder = ImmutableSetMultimap.builder(); 398 elements 399 .asMap() 400 .forEach( 401 (annotation, element) -> 402 builder.putAll(annotation.getQualifiedName().toString(), element)); 403 return builder.build(); 404 } 405 406 /** 407 * Wraps the passed {@link ProcessingStep} in a {@link Step}. This is a convenience method to 408 * allow incremental migration to a String-based API. This method can be used to return a not yet 409 * converted {@link ProcessingStep} from {@link BasicAnnotationProcessor#steps()}. 410 */ 411 protected static Step asStep(ProcessingStep processingStep) { 412 return new ProcessingStepAsStep(processingStep); 413 } 414 415 /** 416 * The unit of processing logic that runs under the guarantee that all elements are complete and 417 * well-formed. A step may reject elements that are not ready for processing but may be at a later 418 * round. 419 */ 420 public interface Step { 421 422 /** 423 * The set of fully-qualified annotation type names processed by this step. 424 * 425 * <p>Warning: If the returned names are not names of annotations, they'll be ignored. 426 */ 427 Set<String> annotations(); 428 429 /** 430 * The implementation of processing logic for the step. It is guaranteed that the keys in {@code 431 * elementsByAnnotation} will be a subset of the set returned by {@link #annotations()}. 432 * 433 * @return the elements (a subset of the values of {@code elementsByAnnotation}) that this step 434 * is unable to process, possibly until a later processing round. These elements will be 435 * passed back to this step at the next round of processing. 436 */ 437 Set<? extends Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation); 438 } 439 440 /** 441 * The unit of processing logic that runs under the guarantee that all elements are complete and 442 * well-formed. A step may reject elements that are not ready for processing but may be at a later 443 * round. 444 * 445 * @deprecated Implement {@link Step} instead. See {@link BasicAnnotationProcessor#steps()}. 446 */ 447 @Deprecated 448 public interface ProcessingStep { 449 450 /** The set of annotation types processed by this step. */ 451 Set<? extends Class<? extends Annotation>> annotations(); 452 453 /** 454 * The implementation of processing logic for the step. It is guaranteed that the keys in {@code 455 * elementsByAnnotation} will be a subset of the set returned by {@link #annotations()}. 456 * 457 * @return the elements (a subset of the values of {@code elementsByAnnotation}) that this step 458 * is unable to process, possibly until a later processing round. These elements will be 459 * passed back to this step at the next round of processing. 460 */ 461 Set<? extends Element> process( 462 SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation); 463 } 464 465 private static class ProcessingStepAsStep implements Step { 466 467 private final ProcessingStep processingStep; 468 private final ImmutableMap<String, Class<? extends Annotation>> annotationsByName; 469 470 ProcessingStepAsStep(ProcessingStep processingStep) { 471 this.processingStep = processingStep; 472 this.annotationsByName = 473 processingStep.annotations().stream() 474 .collect( 475 toImmutableMap( 476 c -> requireNonNull(c.getCanonicalName()), 477 (Class<? extends Annotation> aClass) -> aClass)); 478 } 479 480 @Override 481 public Set<String> annotations() { 482 return annotationsByName.keySet(); 483 } 484 485 @Override 486 public Set<? extends Element> process( 487 ImmutableSetMultimap<String, Element> elementsByAnnotation) { 488 return processingStep.process(toClassKeyedMultimap(elementsByAnnotation)); 489 } 490 491 private ImmutableSetMultimap<Class<? extends Annotation>, Element> toClassKeyedMultimap( 492 SetMultimap<String, Element> elements) { 493 ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> builder = 494 ImmutableSetMultimap.builder(); 495 elements 496 .asMap() 497 .forEach( 498 (annotationName, annotatedElements) -> { 499 Class<? extends Annotation> annotation = annotationsByName.get(annotationName); 500 if (annotation != null) { // should not be null 501 builder.putAll(annotation, annotatedElements); 502 } 503 }); 504 return builder.build(); 505 } 506 } 507 508 /** 509 * A package or type name. 510 * 511 * <p>It's unfortunate that we have to track types and packages separately, but since there are 512 * two different methods to look them up in {@link Elements}, we end up with a lot of parallel 513 * logic. :( 514 * 515 * <p>Packages declared (and annotated) in {@code package-info.java} are tracked as deferred 516 * packages, type elements are tracked directly, and all other elements are tracked via their 517 * nearest enclosing type. 518 */ 519 private static final class ElementName { 520 private enum Kind { 521 PACKAGE_NAME, 522 TYPE_NAME, 523 } 524 525 private final Kind kind; 526 private final String name; 527 528 private ElementName(Kind kind, Name name) { 529 this.kind = checkNotNull(kind); 530 this.name = name.toString(); 531 } 532 533 /** 534 * An {@link ElementName} for an annotated element. If {@code element} is a package, uses the 535 * fully qualified name of the package. If it's a type, uses its fully qualified name. 536 * Otherwise, uses the fully-qualified name of the nearest enclosing type. 537 * 538 * <p>A package can be annotated if it has a {@code package-info.java} with annotations on the 539 * package declaration. 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()}. Empty if the relevant 554 * method on {@link Elements} returns {@code null}. 555 */ 556 Optional<? extends Element> getElement(Elements elements) { 557 return Optional.ofNullable( 558 kind == Kind.PACKAGE_NAME 559 ? elements.getPackageElement(name) 560 : elements.getTypeElement(name)); 561 } 562 563 @Override 564 public boolean equals(@Nullable 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