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