Snap for 7550844 from d542bcf5c7c83f8c458763a5f6bb3356e07e80dc to mainline-tethering-release

Change-Id: I71d1adf860090433563762dc82b5f245bf4454ca
diff --git a/Android.bp b/Android.bp
index 7c7db3e..a7efec4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,17 @@
 package {
-    default_visibility: [":__subpackages__"]
+    default_visibility: [":__subpackages__"],
+    default_applicable_licenses: ["external_auto_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "external_auto_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "LICENSE",
+    ],
 }
diff --git a/METADATA b/METADATA
index abeb0af..dca6f71 100644
--- a/METADATA
+++ b/METADATA
@@ -11,7 +11,7 @@
     type: GIT
     value: "https://github.com/google/auto"
   }
-  version: "auto-value-1.7.3"
-  last_upgrade_date { year: 2020 month: 6 day: 12 }
+  version: "auto-value-1.7.4"
+  last_upgrade_date { year: 2020 month: 11 day: 18 }
   license_type: NOTICE
 }
diff --git a/android-annotation-stubs/Android.bp b/android-annotation-stubs/Android.bp
index a2bd9e9..3e0be03 100644
--- a/android-annotation-stubs/Android.bp
+++ b/android-annotation-stubs/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_auto_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_auto_license"],
+}
+
 java_library_host {
     name: "auto_android_annotation_stubs",
     srcs: ["src/**/*.java"],
diff --git a/common/Android.bp b/common/Android.bp
index 279b90d..25f7099 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_auto_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_auto_license"],
+}
+
 java_library_host {
     name: "auto_common",
     srcs: ["src/main/java/**/*.java"],
diff --git a/common/pom.xml b/common/pom.xml
index 7d1c877..e4a23e0 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -26,7 +26,7 @@
 
   <groupId>com.google.auto</groupId>
   <artifactId>auto-common</artifactId>
-  <version>HEAD-SNAPSHOT</version>
+  <version>0.11</version>
   <name>Auto Common Libraries</name>
   <description>
     Common utilities for creating annotation processors.
@@ -36,7 +36,7 @@
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <java.version>1.8</java.version>
-    <guava.version>27.0.1-jre</guava.version>
+    <guava.version>29.0-jre</guava.version>
     <truth.version>1.0.1</truth.version>
   </properties>
 
@@ -75,7 +75,7 @@
            If you use JavaPoet, you can use GeneratedAnnotationSpecs. -->
       <groupId>com.squareup</groupId>
       <artifactId>javapoet</artifactId>
-      <version>1.9.0</version>
+      <version>1.13.0</version>
       <optional>true</optional>
     </dependency>
 
@@ -107,7 +107,7 @@
     <dependency>
       <groupId>org.eclipse.jdt</groupId>
       <artifactId>ecj</artifactId>
-      <version>3.20.0</version>
+      <version>3.22.0</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git a/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java b/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java
index d3f67aa..375a4cb 100644
--- a/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java
+++ b/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java
@@ -17,12 +17,14 @@
 
 import static com.google.auto.common.MoreElements.asExecutable;
 import static com.google.auto.common.MoreElements.asPackage;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
 import static com.google.auto.common.SuperficialValidation.validateElement;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.Iterables.transform;
 import static com.google.common.collect.Multimaps.filterKeys;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
 import static javax.lang.model.element.ElementKind.PACKAGE;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
@@ -30,8 +32,10 @@
 import com.google.common.base.Optional;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
@@ -57,9 +61,9 @@
  * An abstract {@link Processor} implementation that defers processing of {@link Element}s to later
  * rounds if they cannot be processed.
  *
- * <p>Subclasses put their processing logic in {@link ProcessingStep} implementations. The steps are
- * passed to the processor by returning them in the {@link #initSteps()} method, and can access the
- * {@link ProcessingEnvironment} using {@link #processingEnv}.
+ * <p>Subclasses put their processing logic in {@link Step} implementations. The steps are passed to
+ * the processor by returning them in the {@link #steps()} method, and can access the {@link
+ * ProcessingEnvironment} using {@link #processingEnv}.
  *
  * <p>Any logic that needs to happen once per round can be specified by overriding {@link
  * #postRound(RoundEnvironment)}.
@@ -67,8 +71,8 @@
  * <h3>Ill-formed elements are deferred</h3>
  *
  * Any annotated element whose nearest enclosing type is not well-formed is deferred, and not passed
- * to any {@code ProcessingStep}. This helps processors to avoid many common pitfalls, such as
- * {@link ErrorType} instances, {@link ClassCastException}s and badly coerced types.
+ * to any {@code Step}. This helps processors to avoid many common pitfalls, such as {@link
+ * ErrorType} instances, {@link ClassCastException}s and badly coerced types.
  *
  * <p>A non-package element is considered well-formed if its type, type parameters, parameters,
  * default values, supertypes, annotations, and enclosed elements are. Package elements are treated
@@ -80,11 +84,11 @@
  * because the element will never be fully complete. All such compilations will fail with an error
  * message on the offending type that describes the issue.
  *
- * <h3>Each {@code ProcessingStep} can defer elements</h3>
+ * <h3>Each {@code Step} can defer elements</h3>
  *
- * <p>Each {@code ProcessingStep} can defer elements by including them in the set returned by {@link
- * ProcessingStep#process(SetMultimap)}; elements deferred by a step will be passed back to that
- * step in a later round of processing.
+ * <p>Each {@code Step} can defer elements by including them in the set returned by {@link
+ * Step#process(ImmutableSetMultimap)}; elements deferred by a step will be passed back to that step
+ * in a later round of processing.
  *
  * <p>This feature is useful when one processor may depend on code generated by another, independent
  * processor, in a way that isn't caught by the well-formedness check described above. For example,
@@ -102,26 +106,42 @@
 public abstract class BasicAnnotationProcessor extends AbstractProcessor {
 
   private final Set<ElementName> deferredElementNames = new LinkedHashSet<>();
-  private final SetMultimap<ProcessingStep, ElementName> elementsDeferredBySteps =
+  private final SetMultimap<Step, ElementName> elementsDeferredBySteps =
       LinkedHashMultimap.create();
 
   private Elements elements;
   private Messager messager;
-  private ImmutableList<? extends ProcessingStep> steps;
+  private ImmutableList<? extends Step> steps;
 
   @Override
   public final synchronized void init(ProcessingEnvironment processingEnv) {
     super.init(processingEnv);
     this.elements = processingEnv.getElementUtils();
     this.messager = processingEnv.getMessager();
-    this.steps = ImmutableList.copyOf(initSteps());
+    this.steps = ImmutableList.copyOf(steps());
   }
 
   /**
    * Creates {@linkplain ProcessingStep processing steps} for this processor. {@link #processingEnv}
    * is guaranteed to be set when this method is invoked.
+   *
+   * @deprecated Implement {@link #steps()} instead.
    */
-  protected abstract Iterable<? extends ProcessingStep> initSteps();
+  @Deprecated
+  protected Iterable<? extends ProcessingStep> initSteps() {
+    throw new AssertionError("If steps() is not implemented, initSteps() must be.");
+  }
+
+  /**
+   * Creates {@linkplain Step processing steps} for this processor. {@link #processingEnv} is
+   * guaranteed to be set when this method is invoked.
+   *
+   * <p>Note: If you are migrating some steps from {@link ProcessingStep} to {@link Step}, then you
+   * can call {@link #asStep(ProcessingStep)} on any unmigrated steps.
+   */
+  protected Iterable<? extends Step> steps() {
+    return Iterables.transform(initSteps(), BasicAnnotationProcessor::asStep);
+  }
 
   /**
    * An optional hook for logic to be executed at the end of each round.
@@ -138,26 +158,30 @@
     }
   }
 
-  private ImmutableSet<? extends Class<? extends Annotation>> getSupportedAnnotationClasses() {
+  private ImmutableSet<TypeElement> getSupportedAnnotationTypeElements() {
     checkState(steps != null);
-    ImmutableSet.Builder<Class<? extends Annotation>> builder = ImmutableSet.builder();
-    for (ProcessingStep step : steps) {
-      builder.addAll(step.annotations());
-    }
-    return builder.build();
+    return steps.stream()
+        .flatMap(step -> getSupportedAnnotationTypeElements(step).stream())
+        .collect(collectingAndThen(toList(), ImmutableSet::copyOf));
+  }
+
+  private ImmutableSet<TypeElement> getSupportedAnnotationTypeElements(Step step) {
+    return step.annotations().stream()
+        .map(elements::getTypeElement)
+        .filter(Objects::nonNull)
+        .collect(collectingAndThen(toList(), ImmutableSet::copyOf));
   }
 
   /**
-   * Returns the set of supported annotation types as a collected from registered {@linkplain
-   * ProcessingStep processing steps}.
+   * Returns the set of supported annotation types as collected from registered {@linkplain Step
+   * processing steps}.
    */
   @Override
   public final ImmutableSet<String> getSupportedAnnotationTypes() {
-    ImmutableSet.Builder<String> builder = ImmutableSet.builder();
-    for (Class<? extends Annotation> annotationClass : getSupportedAnnotationClasses()) {
-      builder.add(annotationClass.getCanonicalName());
-    }
-    return builder.build();
+    checkState(steps != null);
+    return steps.stream()
+        .flatMap(step -> step.annotations().stream())
+        .collect(collectingAndThen(toList(), ImmutableSet::copyOf));
   }
 
   @Override
@@ -189,17 +213,19 @@
   }
 
   /** Processes the valid elements, including those previously deferred by each step. */
-  private void process(ImmutableSetMultimap<Class<? extends Annotation>, Element> validElements) {
-    for (ProcessingStep step : steps) {
-      ImmutableSetMultimap<Class<? extends Annotation>, Element> stepElements =
-          new ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element>()
-              .putAll(indexByAnnotation(elementsDeferredBySteps.get(step), step.annotations()))
-              .putAll(filterKeys(validElements, Predicates.<Object>in(step.annotations())))
+  private void process(ImmutableSetMultimap<TypeElement, Element> validElements) {
+    for (Step step : steps) {
+      ImmutableSet<TypeElement> annotationTypes = getSupportedAnnotationTypeElements(step);
+      ImmutableSetMultimap<TypeElement, Element> stepElements =
+          new ImmutableSetMultimap.Builder<TypeElement, Element>()
+              .putAll(indexByAnnotation(elementsDeferredBySteps.get(step), annotationTypes))
+              .putAll(filterKeys(validElements, Predicates.in(annotationTypes)))
               .build();
       if (stepElements.isEmpty()) {
         elementsDeferredBySteps.removeAll(step);
       } else {
-        Set<? extends Element> rejectedElements = step.process(stepElements);
+        Set<? extends Element> rejectedElements =
+            step.process(toClassNameKeyedMultimap(stepElements));
         elementsDeferredBySteps.replaceValues(
             step, transform(rejectedElements, ElementName::forAnnotatedElement));
       }
@@ -233,43 +259,39 @@
    * Returns the valid annotated elements contained in all of the deferred elements. If none are
    * found for a deferred element, defers it again.
    */
-  private ImmutableSetMultimap<Class<? extends Annotation>, Element> validElements(
-      RoundEnvironment roundEnv) {
+  private ImmutableSetMultimap<TypeElement, Element> validElements(RoundEnvironment roundEnv) {
     ImmutableSet<ElementName> prevDeferredElementNames = ImmutableSet.copyOf(deferredElementNames);
     deferredElementNames.clear();
 
-    ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element>
-        deferredElementsByAnnotationBuilder = ImmutableSetMultimap.builder();
+    ImmutableSetMultimap.Builder<TypeElement, Element> deferredElementsByAnnotationBuilder =
+        ImmutableSetMultimap.builder();
     for (ElementName deferredElementName : prevDeferredElementNames) {
       Optional<? extends Element> deferredElement = deferredElementName.getElement(elements);
       if (deferredElement.isPresent()) {
         findAnnotatedElements(
             deferredElement.get(),
-            getSupportedAnnotationClasses(),
+            getSupportedAnnotationTypeElements(),
             deferredElementsByAnnotationBuilder);
       } else {
         deferredElementNames.add(deferredElementName);
       }
     }
 
-    ImmutableSetMultimap<Class<? extends Annotation>, Element> deferredElementsByAnnotation =
+    ImmutableSetMultimap<TypeElement, Element> deferredElementsByAnnotation =
         deferredElementsByAnnotationBuilder.build();
 
-    ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> validElements =
+    ImmutableSetMultimap.Builder<TypeElement, Element> validElements =
         ImmutableSetMultimap.builder();
 
     Set<ElementName> validElementNames = new LinkedHashSet<>();
 
     // Look at the elements we've found and the new elements from this round and validate them.
-    for (Class<? extends Annotation> annotationClass : getSupportedAnnotationClasses()) {
-      // This should just call roundEnv.getElementsAnnotatedWith(Class) directly, but there is a bug
-      // in some versions of eclipse that cause that method to crash.
-      TypeElement annotationType = elements.getTypeElement(annotationClass.getCanonicalName());
+    for (TypeElement annotationType : getSupportedAnnotationTypeElements()) {
       Set<? extends Element> roundElements =
           (annotationType == null)
-              ? ImmutableSet.<Element>of()
+              ? ImmutableSet.of()
               : roundEnv.getElementsAnnotatedWith(annotationType);
-      ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationClass);
+      ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationType);
       for (Element element : Sets.union(roundElements, prevRoundElements)) {
         ElementName elementName = ElementName.forAnnotatedElement(element);
         boolean isValidElement =
@@ -278,7 +300,7 @@
                     && validateElement(
                         element.getKind().equals(PACKAGE) ? element : getEnclosingType(element)));
         if (isValidElement) {
-          validElements.put(annotationClass, element);
+          validElements.put(annotationType, element);
           validElementNames.add(elementName);
         } else {
           deferredElementNames.add(elementName);
@@ -289,15 +311,14 @@
     return validElements.build();
   }
 
-  private ImmutableSetMultimap<Class<? extends Annotation>, Element> indexByAnnotation(
-      Set<ElementName> annotatedElements,
-      Set<? extends Class<? extends Annotation>> annotationClasses) {
-    ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> deferredElements =
+  private ImmutableSetMultimap<TypeElement, Element> indexByAnnotation(
+      Set<ElementName> annotatedElements, ImmutableSet<TypeElement> annotationTypes) {
+    ImmutableSetMultimap.Builder<TypeElement, Element> deferredElements =
         ImmutableSetMultimap.builder();
     for (ElementName elementName : annotatedElements) {
       Optional<? extends Element> element = elementName.getElement(elements);
       if (element.isPresent()) {
-        findAnnotatedElements(element.get(), annotationClasses, deferredElements);
+        findAnnotatedElements(element.get(), annotationTypes, deferredElements);
       }
     }
     return deferredElements.build();
@@ -305,8 +326,8 @@
 
   /**
    * Adds {@code element} and its enclosed elements to {@code annotatedElements} if they are
-   * annotated with any annotations in {@code annotationClasses}. Does not traverse to member types
-   * of {@code element}, so that if {@code Outer} is passed in the example below, looking for
+   * annotated with any annotations in {@code annotationTypes}. Does not traverse to member types of
+   * {@code element}, so that if {@code Outer} is passed in the example below, looking for
    * {@code @X}, then {@code Outer}, {@code Outer.foo}, and {@code Outer.foo()} will be added to the
    * multimap, but neither {@code Inner} nor its members will.
    *
@@ -323,27 +344,33 @@
    */
   private static void findAnnotatedElements(
       Element element,
-      Set<? extends Class<? extends Annotation>> annotationClasses,
-      ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> annotatedElements) {
+      ImmutableSet<TypeElement> annotationTypes,
+      ImmutableSetMultimap.Builder<TypeElement, Element> annotatedElements) {
     for (Element enclosedElement : element.getEnclosedElements()) {
       if (!enclosedElement.getKind().isClass() && !enclosedElement.getKind().isInterface()) {
-        findAnnotatedElements(enclosedElement, annotationClasses, annotatedElements);
+        findAnnotatedElements(enclosedElement, annotationTypes, annotatedElements);
       }
     }
 
     // element.getEnclosedElements() does NOT return parameter elements
     if (element instanceof ExecutableElement) {
       for (Element parameterElement : asExecutable(element).getParameters()) {
-        findAnnotatedElements(parameterElement, annotationClasses, annotatedElements);
+        findAnnotatedElements(parameterElement, annotationTypes, annotatedElements);
       }
     }
-    for (Class<? extends Annotation> annotationClass : annotationClasses) {
-      if (isAnnotationPresent(element, annotationClass)) {
-        annotatedElements.put(annotationClass, element);
+    for (TypeElement annotationType : annotationTypes) {
+      if (isAnnotationPresent(element, annotationType)) {
+        annotatedElements.put(annotationType, element);
       }
     }
   }
 
+  private static boolean isAnnotationPresent(Element element, TypeElement annotationType) {
+    return element.getAnnotationMirrors().stream()
+        .anyMatch(
+            mirror -> MoreTypes.asTypeElement(mirror.getAnnotationType()).equals(annotationType));
+  }
+
   /**
    * Returns the nearest enclosing {@link TypeElement} to the current element, throwing an {@link
    * IllegalArgumentException} if the provided {@link Element} is a {@link PackageElement} or is
@@ -371,12 +398,61 @@
         null);
   }
 
+  private static ImmutableSetMultimap<String, Element> toClassNameKeyedMultimap(
+      SetMultimap<TypeElement, Element> elements) {
+    ImmutableSetMultimap.Builder<String, Element> builder = ImmutableSetMultimap.builder();
+    elements
+        .asMap()
+        .forEach(
+            (annotation, element) ->
+                builder.putAll(annotation.getQualifiedName().toString(), element));
+    return builder.build();
+  }
+
+  /**
+   * Wraps the passed {@link ProcessingStep} in a {@link Step}. This is a convenience method to
+   * allow incremental migration to a String-based API. This method can be used to return a not yet
+   * converted {@link ProcessingStep} from {@link BasicAnnotationProcessor#steps()}.
+   */
+  protected static Step asStep(ProcessingStep processingStep) {
+    return new ProcessingStepAsStep(processingStep);
+  }
+
   /**
    * The unit of processing logic that runs under the guarantee that all elements are complete and
    * well-formed. A step may reject elements that are not ready for processing but may be at a later
    * round.
    */
+  public interface Step {
+
+    /**
+     * The set of fully-qualified annotation type names processed by this step.
+     *
+     * <p>Warning: If the returned names are not names of annotations, they'll be ignored.
+     */
+    Set<String> annotations();
+
+    /**
+     * The implementation of processing logic for the step. It is guaranteed that the keys in {@code
+     * elementsByAnnotation} will be a subset of the set returned by {@link #annotations()}.
+     *
+     * @return the elements (a subset of the values of {@code elementsByAnnotation}) that this step
+     *     is unable to process, possibly until a later processing round. These elements will be
+     *     passed back to this step at the next round of processing.
+     */
+    Set<? extends Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation);
+  }
+
+  /**
+   * The unit of processing logic that runs under the guarantee that all elements are complete and
+   * well-formed. A step may reject elements that are not ready for processing but may be at a later
+   * round.
+   *
+   * @deprecated Implement {@link Step} instead. See {@link BasicAnnotationProcessor#steps()}.
+   */
+  @Deprecated
   public interface ProcessingStep {
+
     /** The set of annotation types processed by this step. */
     Set<? extends Class<? extends Annotation>> annotations();
 
@@ -392,6 +468,46 @@
         SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation);
   }
 
+  private static class ProcessingStepAsStep implements Step {
+
+    private final ProcessingStep processingStep;
+    private final ImmutableMap<String, Class<? extends Annotation>> annotationsByName;
+
+    ProcessingStepAsStep(ProcessingStep processingStep) {
+      this.processingStep = processingStep;
+      this.annotationsByName =
+          processingStep.annotations().stream()
+              .collect(
+                  collectingAndThen(
+                      toMap(
+                          Class::getCanonicalName, (Class<? extends Annotation> aClass) -> aClass),
+                      ImmutableMap::copyOf));
+    }
+
+    @Override
+    public Set<String> annotations() {
+      return annotationsByName.keySet();
+    }
+
+    @Override
+    public Set<? extends Element> process(
+        ImmutableSetMultimap<String, Element> elementsByAnnotation) {
+      return processingStep.process(toClassKeyedMultimap(elementsByAnnotation));
+    }
+
+    private ImmutableSetMultimap<Class<? extends Annotation>, Element> toClassKeyedMultimap(
+        SetMultimap<String, Element> elements) {
+      ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> builder =
+          ImmutableSetMultimap.builder();
+      elements
+          .asMap()
+          .forEach(
+              (annotation, annotatedElements) ->
+                  builder.putAll(annotationsByName.get(annotation), annotatedElements));
+      return builder.build();
+    }
+  }
+
   /**
    * A package or type name.
    *
diff --git a/common/src/main/java/com/google/auto/common/SuperficialValidation.java b/common/src/main/java/com/google/auto/common/SuperficialValidation.java
index dddb1be..5ef4dbf 100644
--- a/common/src/main/java/com/google/auto/common/SuperficialValidation.java
+++ b/common/src/main/java/com/google/auto/common/SuperficialValidation.java
@@ -17,6 +17,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.stream.StreamSupport;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.AnnotationValueVisitor;
@@ -46,13 +47,12 @@
  * @author Gregory Kick
  */
 public final class SuperficialValidation {
+  /**
+   * Returns true if all of the given elements return true from {@link #validateElement(Element)}.
+   */
   public static boolean validateElements(Iterable<? extends Element> elements) {
-    for (Element element : elements) {
-      if (!validateElement(element)) {
-        return false;
-      }
-    }
-    return true;
+    return StreamSupport.stream(elements.spliterator(), false)
+        .allMatch(SuperficialValidation::validateElement);
   }
 
   private static final ElementVisitor<Boolean, Void> ELEMENT_VALIDATING_VISITOR =
@@ -94,6 +94,12 @@
         }
       };
 
+  /**
+   * Returns true if all types referenced by the given element are defined. The exact meaning of
+   * this depends on the kind of element. For packages, it means that all annotations on the package
+   * are fully defined. For other element kinds, it means that types referenced by the element,
+   * anything it contains, and any of its annotations element are all defined.
+   */
   public static boolean validateElement(Element element) {
     return element.accept(ELEMENT_VALIDATING_VISITOR, null);
   }
@@ -163,7 +169,13 @@
         }
       };
 
-  private static boolean validateType(TypeMirror type) {
+  /**
+   * Returns true if the given type is fully defined. This means that the type itself is defined, as
+   * are any types it references, such as any type arguments or type bounds. For an {@link
+   * ExecutableType}, the parameter and return types must be fully defined, as must types declared
+   * in a {@code throws} clause or in the bounds of any type parameters.
+   */
+  public static boolean validateType(TypeMirror type) {
     return type.accept(TYPE_VALIDATING_VISITOR, null);
   }
 
@@ -182,17 +194,14 @@
         && validateAnnotationValues(annotationMirror.getElementValues());
   }
 
-  @SuppressWarnings("unused")
   private static boolean validateAnnotationValues(
       Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap) {
-    for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> valueEntry :
-        valueMap.entrySet()) {
-      TypeMirror expectedType = valueEntry.getKey().getReturnType();
-      if (!validateAnnotationValue(valueEntry.getValue(), expectedType)) {
-        return false;
-      }
-    }
-    return true;
+    return valueMap.entrySet().stream()
+        .allMatch(
+            valueEntry -> {
+              TypeMirror expectedType = valueEntry.getKey().getReturnType();
+              return validateAnnotationValue(valueEntry.getValue(), expectedType);
+            });
   }
 
   private static final AnnotationValueVisitor<Boolean, TypeMirror> VALUE_VALIDATING_VISITOR =
@@ -216,17 +225,8 @@
           if (!expectedType.getKind().equals(TypeKind.ARRAY)) {
             return false;
           }
-          try {
-            expectedType = MoreTypes.asArray(expectedType).getComponentType();
-          } catch (IllegalArgumentException e) {
-            return false; // Not an array expected, ergo invalid.
-          }
-          for (AnnotationValue value : values) {
-            if (!value.accept(this, expectedType)) {
-              return false;
-            }
-          }
-          return true;
+          TypeMirror componentType = MoreTypes.asArray(expectedType).getComponentType();
+          return values.stream().allMatch(value -> value.accept(this, componentType));
         }
 
         @Override
@@ -280,4 +280,6 @@
       AnnotationValue annotationValue, TypeMirror expectedType) {
     return annotationValue.accept(VALUE_VALIDATING_VISITOR, expectedType);
   }
+
+  private SuperficialValidation() {}
 }
diff --git a/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java
index 59a135c..f753450 100644
--- a/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java
+++ b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java
@@ -18,28 +18,35 @@
 import static com.google.common.collect.Multimaps.transformValues;
 import static com.google.common.truth.Truth.assertAbout;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
 import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
 import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
 import static javax.tools.Diagnostic.Kind.ERROR;
 import static javax.tools.StandardLocation.SOURCE_OUTPUT;
 
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.BasicAnnotationProcessor.Step;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.SetMultimap;
 import com.google.common.truth.Correspondence;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.CompilationRule;
 import com.google.testing.compile.JavaFileObjects;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
 import javax.annotation.processing.Filer;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
 import javax.tools.JavaFileObject;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -47,30 +54,36 @@
 @RunWith(JUnit4.class)
 public class BasicAnnotationProcessorTest {
 
+  private abstract static class BaseAnnotationProcessor extends BasicAnnotationProcessor {
+
+    static final String ENCLOSING_CLASS_NAME =
+        BasicAnnotationProcessorTest.class.getCanonicalName();
+
+    @Override
+    public final SourceVersion getSupportedSourceVersion() {
+      return SourceVersion.latestSupported();
+    }
+  }
+
   @Retention(RetentionPolicy.SOURCE)
   public @interface RequiresGeneratedCode {}
 
   /**
    * Rejects elements unless the class generated by {@link GeneratesCode}'s processor is present.
    */
-  private static final class RequiresGeneratedCodeProcessor extends BasicAnnotationProcessor {
+  private static class RequiresGeneratedCodeProcessor extends BaseAnnotationProcessor {
 
     int rejectedRounds;
-    final ImmutableList.Builder<ImmutableSetMultimap<Class<? extends Annotation>, Element>>
-        processArguments = ImmutableList.builder();
+    final ImmutableList.Builder<ImmutableSetMultimap<String, Element>> processArguments =
+        ImmutableList.builder();
 
     @Override
-    public SourceVersion getSupportedSourceVersion() {
-      return SourceVersion.latestSupported();
-    }
-
-    @Override
-    protected Iterable<? extends ProcessingStep> initSteps() {
+    protected Iterable<? extends Step> steps() {
       return ImmutableSet.of(
-          new ProcessingStep() {
+          new Step() {
             @Override
-            public Set<Element> process(
-                SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+            public ImmutableSet<? extends Element> process(
+                ImmutableSetMultimap<String, Element> elementsByAnnotation) {
               processArguments.add(ImmutableSetMultimap.copyOf(elementsByAnnotation));
               TypeElement requiredClass =
                   processingEnv.getElementUtils().getTypeElement("test.SomeGeneratedClass");
@@ -83,25 +96,25 @@
             }
 
             @Override
-            public Set<? extends Class<? extends Annotation>> annotations() {
-              return ImmutableSet.of(RequiresGeneratedCode.class);
+            public ImmutableSet<String> annotations() {
+              return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".RequiresGeneratedCode");
             }
           },
-          new ProcessingStep() {
+          new Step() {
             @Override
-            public Set<? extends Class<? extends Annotation>> annotations() {
-              return ImmutableSet.of(AnAnnotation.class);
+            public ImmutableSet<? extends Element> process(
+                ImmutableSetMultimap<String, Element> elementsByAnnotation) {
+              return ImmutableSet.of();
             }
 
             @Override
-            public Set<? extends Element> process(
-                SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
-              return ImmutableSet.of();
+            public ImmutableSet<String> annotations() {
+              return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".AnAnnotation");
             }
           });
     }
 
-    ImmutableList<ImmutableSetMultimap<Class<? extends Annotation>, Element>> processArguments() {
+    ImmutableList<ImmutableSetMultimap<String, Element>> processArguments() {
       return processArguments.build();
     }
   }
@@ -110,27 +123,21 @@
   public @interface GeneratesCode {}
 
   /** Generates a class called {@code test.SomeGeneratedClass}. */
-  public class GeneratesCodeProcessor extends BasicAnnotationProcessor {
-
+  public static class GeneratesCodeProcessor extends BaseAnnotationProcessor {
     @Override
-    public SourceVersion getSupportedSourceVersion() {
-      return SourceVersion.latestSupported();
-    }
-
-    @Override
-    protected Iterable<? extends ProcessingStep> initSteps() {
+    protected Iterable<? extends Step> steps() {
       return ImmutableSet.of(
-          new ProcessingStep() {
+          new Step() {
             @Override
-            public Set<Element> process(
-                SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+            public ImmutableSet<? extends Element> process(
+                ImmutableSetMultimap<String, Element> elementsByAnnotation) {
               generateClass(processingEnv.getFiler(), "SomeGeneratedClass");
               return ImmutableSet.of();
             }
 
             @Override
-            public Set<? extends Class<? extends Annotation>> annotations() {
-              return ImmutableSet.of(GeneratesCode.class);
+            public ImmutableSet<String> annotations() {
+              return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".GeneratesCode");
             }
           });
     }
@@ -139,20 +146,15 @@
   public @interface AnAnnotation {}
 
   /** When annotating a type {@code Foo}, generates a class called {@code FooXYZ}. */
-  public class AnAnnotationProcessor extends BasicAnnotationProcessor {
+  public static class AnAnnotationProcessor extends BaseAnnotationProcessor {
 
     @Override
-    public SourceVersion getSupportedSourceVersion() {
-      return SourceVersion.latestSupported();
-    }
-
-    @Override
-    protected Iterable<? extends ProcessingStep> initSteps() {
+    protected Iterable<? extends Step> steps() {
       return ImmutableSet.of(
-          new ProcessingStep() {
+          new Step() {
             @Override
-            public Set<Element> process(
-                SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+            public ImmutableSet<Element> process(
+                ImmutableSetMultimap<String, Element> elementsByAnnotation) {
               for (Element element : elementsByAnnotation.values()) {
                 generateClass(processingEnv.getFiler(), element.getSimpleName() + "XYZ");
               }
@@ -160,8 +162,8 @@
             }
 
             @Override
-            public Set<? extends Class<? extends Annotation>> annotations() {
-              return ImmutableSet.of(AnAnnotation.class);
+            public ImmutableSet<String> annotations() {
+              return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".AnAnnotation");
             }
           });
     }
@@ -171,19 +173,15 @@
   public @interface CauseError {}
 
   /** Report an error for any class annotated. */
-  public static class CauseErrorProcessor extends BasicAnnotationProcessor {
-    @Override
-    public SourceVersion getSupportedSourceVersion() {
-      return SourceVersion.latestSupported();
-    }
+  public static class CauseErrorProcessor extends BaseAnnotationProcessor {
 
     @Override
-    protected Iterable<? extends ProcessingStep> initSteps() {
+    protected Iterable<? extends Step> steps() {
       return ImmutableSet.of(
-          new ProcessingStep() {
+          new Step() {
             @Override
-            public Set<Element> process(
-                SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+            public ImmutableSet<Element> process(
+                ImmutableSetMultimap<String, Element> elementsByAnnotation) {
               for (Element e : elementsByAnnotation.values()) {
                 processingEnv.getMessager().printMessage(ERROR, "purposeful error", e);
               }
@@ -191,26 +189,91 @@
             }
 
             @Override
-            public Set<? extends Class<? extends Annotation>> annotations() {
-              return ImmutableSet.of(CauseError.class);
+            public ImmutableSet<String> annotations() {
+              return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".CauseError");
             }
           });
     }
   }
 
-  @Test public void properlyDefersProcessing_typeElement() {
-    JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA",
-        "package test;",
-        "",
-        "@" + RequiresGeneratedCode.class.getCanonicalName(),
-        "public class ClassA {",
-        "  SomeGeneratedClass sgc;",
-        "}");
-    JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB",
-        "package test;",
-        "",
-        "@" + GeneratesCode.class.getCanonicalName(),
-        "public class ClassB {}");
+  public static class MissingAnnotationProcessor extends BaseAnnotationProcessor {
+
+    private ImmutableSetMultimap<String, Element> elementsByAnnotation;
+
+    @Override
+    protected Iterable<? extends Step> steps() {
+      return ImmutableSet.of(
+          new Step() {
+            @Override
+            public ImmutableSet<Element> process(
+                ImmutableSetMultimap<String, Element> elementsByAnnotation) {
+              MissingAnnotationProcessor.this.elementsByAnnotation = elementsByAnnotation;
+              for (Element element : elementsByAnnotation.values()) {
+                generateClass(processingEnv.getFiler(), element.getSimpleName() + "XYZ");
+              }
+              return ImmutableSet.of();
+            }
+
+            @Override
+            public ImmutableSet<String> annotations() {
+              return ImmutableSet.of(
+                  "test.SomeNonExistentClass", ENCLOSING_CLASS_NAME + ".AnAnnotation");
+            }
+          });
+    }
+
+    ImmutableSetMultimap<String, Element> getElementsByAnnotation() {
+      return elementsByAnnotation;
+    }
+  }
+
+  @SuppressWarnings("deprecation") // Deprecated ProcessingStep is being explicitly tested.
+  static final class MultiAnnotationProcessingStep implements ProcessingStep {
+
+    private SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation;
+
+    @Override
+    public ImmutableSet<? extends Class<? extends Annotation>> annotations() {
+      return ImmutableSet.of(AnAnnotation.class, ReferencesAClass.class);
+    }
+
+    @Override
+    public ImmutableSet<? extends Element> process(
+        SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+      this.elementsByAnnotation = elementsByAnnotation;
+      return ImmutableSet.of();
+    }
+
+    SetMultimap<Class<? extends Annotation>, Element> getElementsByAnnotation() {
+      return elementsByAnnotation;
+    }
+  }
+
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface ReferencesAClass {
+    Class<?> value();
+  }
+
+  @Rule public CompilationRule compilation = new CompilationRule();
+
+  @Test
+  public void properlyDefersProcessing_typeElement() {
+    JavaFileObject classAFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.ClassA",
+            "package test;",
+            "",
+            "@" + RequiresGeneratedCode.class.getCanonicalName(),
+            "public class ClassA {",
+            "  SomeGeneratedClass sgc;",
+            "}");
+    JavaFileObject classBFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.ClassB",
+            "package test;",
+            "",
+            "@" + GeneratesCode.class.getCanonicalName(),
+            "public class ClassB {}");
     RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor =
         new RequiresGeneratedCodeProcessor();
     assertAbout(javaSources())
@@ -244,22 +307,22 @@
         .generatesFileNamed(SOURCE_OUTPUT, "test", "ValidInRound2XYZ.java");
   }
 
-  @Retention(RetentionPolicy.SOURCE)
-  public @interface ReferencesAClass {
-    Class<?> value();
-  }
-
-  @Test public void properlyDefersProcessing_packageElement() {
-    JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA",
-        "package test;",
-        "",
-        "@" + GeneratesCode.class.getCanonicalName(),
-        "public class ClassA {",
-        "}");
-    JavaFileObject packageFileObject = JavaFileObjects.forSourceLines("test.package-info",
-        "@" + RequiresGeneratedCode.class.getCanonicalName(),
-        "@" + ReferencesAClass.class.getCanonicalName() + "(SomeGeneratedClass.class)",
-        "package test;");
+  @Test
+  public void properlyDefersProcessing_packageElement() {
+    JavaFileObject classAFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.ClassA",
+            "package test;",
+            "",
+            "@" + GeneratesCode.class.getCanonicalName(),
+            "public class ClassA {",
+            "}");
+    JavaFileObject packageFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.package-info",
+            "@" + RequiresGeneratedCode.class.getCanonicalName(),
+            "@" + ReferencesAClass.class.getCanonicalName() + "(SomeGeneratedClass.class)",
+            "package test;");
     RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor =
         new RequiresGeneratedCodeProcessor();
     assertAbout(javaSources())
@@ -272,21 +335,28 @@
     assertThat(requiresGeneratedCodeProcessor.rejectedRounds).isEqualTo(0);
   }
 
-  @Test public void properlyDefersProcessing_argumentElement() {
-    JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA",
-        "package test;",
-        "",
-        "public class ClassA {",
-        "  SomeGeneratedClass sgc;",
-        "  public void myMethod(@" + RequiresGeneratedCode.class.getCanonicalName() + " int myInt)",
-        "  {}",
-        "}");
-    JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB",
-        "package test;",
-        "",
-        "public class ClassB {",
-        "  public void myMethod(@" + GeneratesCode.class.getCanonicalName() + " int myInt) {}",
-        "}");
+  @Test
+  public void properlyDefersProcessing_argumentElement() {
+    JavaFileObject classAFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.ClassA",
+            "package test;",
+            "",
+            "public class ClassA {",
+            "  SomeGeneratedClass sgc;",
+            "  public void myMethod(@"
+                + RequiresGeneratedCode.class.getCanonicalName()
+                + " int myInt)",
+            "  {}",
+            "}");
+    JavaFileObject classBFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.ClassB",
+            "package test;",
+            "",
+            "public class ClassB {",
+            "  public void myMethod(@" + GeneratesCode.class.getCanonicalName() + " int myInt) {}",
+            "}");
     RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor =
         new RequiresGeneratedCodeProcessor();
     assertAbout(javaSources())
@@ -311,11 +381,13 @@
             "  @" + AnAnnotation.class.getCanonicalName(),
             "  public void method() {}",
             "}");
-    JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB",
-        "package test;",
-        "",
-        "@" + GeneratesCode.class.getCanonicalName(),
-        "public class ClassB {}");
+    JavaFileObject classBFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.ClassB",
+            "package test;",
+            "",
+            "@" + GeneratesCode.class.getCanonicalName(),
+            "public class ClassB {}");
     RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor =
         new RequiresGeneratedCodeProcessor();
     assertAbout(javaSources())
@@ -332,8 +404,8 @@
     assertThat(requiresGeneratedCodeProcessor.processArguments())
         .comparingElementsUsing(setMultimapValuesByString())
         .containsExactly(
-            ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA"),
-            ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA"))
+            ImmutableSetMultimap.of(RequiresGeneratedCode.class.getCanonicalName(), "test.ClassA"),
+            ImmutableSetMultimap.of(RequiresGeneratedCode.class.getCanonicalName(), "test.ClassA"))
         .inOrder();
   }
 
@@ -345,20 +417,60 @@
         "is equivalent comparing multimap values by `toString()` to");
   }
 
-  @Test public void reportsMissingType() {
-    JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA",
-        "package test;",
-        "",
-        "@" + RequiresGeneratedCode.class.getCanonicalName(),
-        "public class ClassA {",
-        "  SomeGeneratedClass bar;",
-        "}");
+  @Test
+  public void properlySkipsMissingAnnotations_generatesClass() {
+    JavaFileObject source =
+        JavaFileObjects.forSourceLines(
+            "test.ValidInRound2",
+            "package test;",
+            "",
+            "@" + AnAnnotation.class.getCanonicalName(),
+            "public class ValidInRound2 {",
+            "  ValidInRound1XYZ vir1xyz;",
+            "  @" + AnAnnotation.class.getCanonicalName(),
+            "  static class ValidInRound1 {}",
+            "}");
+    Compilation compilation =
+        javac().withProcessors(new MissingAnnotationProcessor()).compile(source);
+    assertThat(compilation).succeeded();
+    assertThat(compilation).generatedSourceFile("test.ValidInRound2XYZ");
+  }
+
+  @Test
+  public void properlySkipsMissingAnnotations_passesValidAnnotationsToProcess() {
+    JavaFileObject source =
+        JavaFileObjects.forSourceLines(
+            "test.ClassA",
+            "package test;",
+            "",
+            "@" + AnAnnotation.class.getCanonicalName(),
+            "public class ClassA {",
+            "}");
+    MissingAnnotationProcessor missingAnnotationProcessor = new MissingAnnotationProcessor();
+    assertThat(javac().withProcessors(missingAnnotationProcessor).compile(source)).succeeded();
+    assertThat(missingAnnotationProcessor.getElementsByAnnotation().keySet())
+        .containsExactly(AnAnnotation.class.getCanonicalName());
+    assertThat(missingAnnotationProcessor.getElementsByAnnotation().values()).hasSize(1);
+  }
+
+  @Test
+  public void reportsMissingType() {
+    JavaFileObject classAFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.ClassA",
+            "package test;",
+            "",
+            "@" + RequiresGeneratedCode.class.getCanonicalName(),
+            "public class ClassA {",
+            "  SomeGeneratedClass bar;",
+            "}");
     assertAbout(javaSources())
         .that(ImmutableList.of(classAFileObject))
         .processedWith(new RequiresGeneratedCodeProcessor())
         .failsToCompile()
         .withErrorContaining(RequiresGeneratedCodeProcessor.class.getCanonicalName())
-        .in(classAFileObject).onLine(4);
+        .in(classAFileObject)
+        .onLine(4);
   }
 
   @Test
@@ -378,6 +490,45 @@
         .withErrorContaining("purposeful");
   }
 
+  @Test
+  public void processingStepAsStepAnnotationsNamesMatchClasses() {
+    Step step = BasicAnnotationProcessor.asStep(new MultiAnnotationProcessingStep());
+
+    assertThat(step.annotations())
+        .containsExactly(
+            AnAnnotation.class.getCanonicalName(), ReferencesAClass.class.getCanonicalName());
+  }
+
+  /**
+   * Tests that a {@link ProcessingStep} passed to {@link
+   * BasicAnnotationProcessor#asStep(ProcessingStep)} still gets passed the correct arguments to
+   * {@link Step#process(ImmutableSetMultimap)}.
+   */
+  @Test
+  public void processingStepAsStepProcessElementsMatchClasses() {
+    Elements elements = compilation.getElements();
+    String anAnnotationName = AnAnnotation.class.getCanonicalName();
+    String referencesAClassName = ReferencesAClass.class.getCanonicalName();
+    TypeElement anAnnotationElement = elements.getTypeElement(anAnnotationName);
+    TypeElement referencesAClassElement = elements.getTypeElement(referencesAClassName);
+    MultiAnnotationProcessingStep processingStep = new MultiAnnotationProcessingStep();
+
+    BasicAnnotationProcessor.asStep(processingStep)
+        .process(
+            ImmutableSetMultimap.of(
+                anAnnotationName,
+                anAnnotationElement,
+                referencesAClassName,
+                referencesAClassElement));
+
+    assertThat(processingStep.getElementsByAnnotation())
+        .containsExactly(
+            AnAnnotation.class,
+            anAnnotationElement,
+            ReferencesAClass.class,
+            referencesAClassElement);
+  }
+
   private static void generateClass(Filer filer, String generatedClassName) {
     PrintWriter writer = null;
     try {
diff --git a/factory/Android.bp b/factory/Android.bp
index bf4899b..7e75542 100644
--- a/factory/Android.bp
+++ b/factory/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_auto_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_auto_license"],
+}
+
 java_library {
     name: "auto_factory_annotations",
     host_supported: true,
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java
index be9478f..4c4a38a 100644
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java
@@ -15,6 +15,7 @@
  */
 package com.google.auto.factory;
 
+import com.google.auto.factory.otherpackage.OtherPackage;
 import dagger.Module;
 import dagger.Provides;
 
@@ -45,4 +46,14 @@
   Number provideNumber() {
     return 3;
   }
+
+  @Provides
+  ReferencePackage provideReferencePackage(ReferencePackageFactory factory) {
+    return factory.create(17);
+  }
+
+  @Provides
+  OtherPackage provideOtherPackage() {
+    return new OtherPackage(null, 23);
+  }
 }
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java
index 7d0a162..b5bbe3d 100644
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java
@@ -15,6 +15,7 @@
  */
 package com.google.auto.factory;
 
+import com.google.auto.factory.otherpackage.OtherPackageFactory;
 import dagger.Component;
 
 /** A component to materialize the factory using Dagger 2 */
@@ -23,4 +24,8 @@
   FooFactory factory();
 
   GenericFooFactory<Number> generatedFactory();
+
+  ReferencePackageFactory referencePackageFactory();
+
+  OtherPackageFactory otherPackageFactory();
 }
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java
new file mode 100644
index 0000000..22aff65
--- /dev/null
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.auto.factory;
+
+import com.google.auto.factory.otherpackage.OtherPackage;
+import com.google.auto.factory.otherpackage.OtherPackageFactory;
+import javax.inject.Inject;
+
+@AutoFactory
+public class ReferencePackage {
+  private final OtherPackageFactory otherPackageFactory;
+  private final int random;
+
+  @Inject
+  public ReferencePackage(
+      @Provided OtherPackageFactory otherPackageFactory,
+      int random) {
+    this.otherPackageFactory = otherPackageFactory;
+    this.random = random;
+  }
+
+  public OtherPackage otherPackage() {
+    return otherPackageFactory.create(random);
+  }
+}
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java
new file mode 100644
index 0000000..b9925a1
--- /dev/null
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.auto.factory.otherpackage;
+
+import com.google.auto.factory.AutoFactory;
+import com.google.auto.factory.Provided;
+import com.google.auto.factory.ReferencePackageFactory;
+
+@AutoFactory
+public class OtherPackage {
+  private final ReferencePackageFactory referencePackageFactory;
+  private final int random;
+
+  public OtherPackage(@Provided ReferencePackageFactory referencePackageFactory, int random) {
+    this.referencePackageFactory = referencePackageFactory;
+    this.random = random;
+  }
+
+  public ReferencePackageFactory referencePackageFactory() {
+    return referencePackageFactory;
+  }
+
+  public int random() {
+    return random;
+  }
+}
diff --git a/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java b/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java
index ebd8336..e141211 100644
--- a/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java
+++ b/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java
@@ -4,6 +4,7 @@
 
 import com.google.auto.factory.GenericFoo.DepE;
 import com.google.auto.factory.GenericFoo.IntAndStringAccessor;
+import com.google.auto.factory.otherpackage.OtherPackage;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Guice;
 import com.google.inject.Key;
@@ -64,6 +65,16 @@
     assertThat(four.getDepE()).isEqualTo(DepE.VALUE_2);
   }
 
+  @Test
+  public void daggerInjectedPackageSpanningFactory() {
+    FactoryComponent component = DaggerFactoryComponent.create();
+    ReferencePackageFactory referencePackageFactory = component.referencePackageFactory();
+    ReferencePackage referencePackage = referencePackageFactory.create(5);
+    OtherPackage otherPackage = referencePackage.otherPackage();
+    assertThat(otherPackage.referencePackageFactory()).isNotSameInstanceAs(referencePackageFactory);
+    assertThat(otherPackage.random()).isEqualTo(5);
+  }
+
   @Test public void guiceInjectedFactory() {
     FooFactory fooFactory = Guice.createInjector(new GuiceModule()).getInstance(FooFactory.class);
     Foo one = fooFactory.create("A");
@@ -109,4 +120,15 @@
     assertThat(four.passThrough(5L)).isEqualTo(5L);
     assertThat(four.getDepE()).isEqualTo(DepE.VALUE_2);
   }
+
+  @Test
+  public void guiceInjectedPackageSpanningFactory() {
+    ReferencePackageFactory referencePackageFactory =
+        Guice.createInjector(new GuiceModule())
+            .getInstance(ReferencePackageFactory.class);
+    ReferencePackage referencePackage = referencePackageFactory.create(5);
+    OtherPackage otherPackage = referencePackage.otherPackage();
+    assertThat(otherPackage.referencePackageFactory()).isNotSameInstanceAs(referencePackageFactory);
+    assertThat(otherPackage.random()).isEqualTo(5);
+  }
 }
diff --git a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java
index ad4ccb8..e2da6ea 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java
@@ -62,21 +62,17 @@
   abstract AnnotationMirror mirror();
   abstract ImmutableMap<String, AnnotationValue> valuesMap();
 
-  String getFactoryName() {
-    CharSequence packageName = getPackage(targetType()).getQualifiedName();
-    StringBuilder builder = new StringBuilder(packageName);
-    if (packageName.length() > 0) {
-      builder.append('.');
-    }
+  PackageAndClass getFactoryName() {
+    String packageName = getPackage(targetType()).getQualifiedName().toString();
     if (className().isPresent()) {
-      builder.append(className().get());
-    } else {
-      for (String enclosingSimpleName : targetEnclosingSimpleNames()) {
-        builder.append(enclosingSimpleName).append('_');
-      }
-      builder.append(targetType().getSimpleName()).append("Factory");
+      return PackageAndClass.of(packageName, className().get());
     }
-    return builder.toString();
+    StringBuilder builder = new StringBuilder();
+    for (String enclosingSimpleName : targetEnclosingSimpleNames()) {
+      builder.append(enclosingSimpleName).append('_');
+    }
+    builder.append(targetType().getSimpleName()).append("Factory");
+    return PackageAndClass.of(packageName, builder.toString());
   }
 
   private ImmutableList<String> targetEnclosingSimpleNames() {
diff --git a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
index cf3d5eb..5cc1d94 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
@@ -27,13 +27,10 @@
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
-import com.google.googlejavaformat.java.filer.FormattingFiler;
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Map.Entry;
 import java.util.Set;
 import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.Messager;
@@ -68,7 +65,6 @@
   private Messager messager;
   private Elements elements;
   private Types types;
-  private FactoryWriter factoryWriter;
 
   @Override
   public synchronized void init(ProcessingEnvironment processingEnv) {
@@ -76,11 +72,6 @@
     elements = processingEnv.getElementUtils();
     types = processingEnv.getTypeUtils();
     messager = processingEnv.getMessager();
-    factoryWriter =
-        new FactoryWriter(
-            new FormattingFiler(processingEnv.getFiler()),
-            elements,
-            processingEnv.getSourceVersion());
     providedChecker = new ProvidedChecker(messager);
     declarationFactory = new AutoFactoryDeclaration.Factory(elements, messager);
     factoryDescriptorGenerator =
@@ -103,14 +94,15 @@
       providedChecker.checkProvidedParameter(element);
     }
 
-    ImmutableListMultimap.Builder<String, FactoryMethodDescriptor> indexedMethods =
+    ImmutableListMultimap.Builder<PackageAndClass, FactoryMethodDescriptor> indexedMethodsBuilder =
         ImmutableListMultimap.builder();
-    ImmutableSetMultimap.Builder<String, ImplementationMethodDescriptor>
+    ImmutableSetMultimap.Builder<PackageAndClass, ImplementationMethodDescriptor>
         implementationMethodDescriptorsBuilder = ImmutableSetMultimap.builder();
+    // Iterate over the classes and methods that are annotated with @AutoFactory.
     for (Element element : roundEnv.getElementsAnnotatedWith(AutoFactory.class)) {
       Optional<AutoFactoryDeclaration> declaration = declarationFactory.createIfValid(element);
       if (declaration.isPresent()) {
-        String factoryName = declaration.get().getFactoryName();
+        PackageAndClass factoryName = declaration.get().getFactoryName();
         TypeElement extendingType = declaration.get().extendingType();
         implementationMethodDescriptorsBuilder.putAll(
             factoryName, implementationMethods(extendingType, element));
@@ -123,62 +115,61 @@
       ImmutableSet<FactoryMethodDescriptor> descriptors =
           factoryDescriptorGenerator.generateDescriptor(element);
       for (FactoryMethodDescriptor descriptor : descriptors) {
-        indexedMethods.put(descriptor.factoryName(), descriptor);
+        indexedMethodsBuilder.put(descriptor.factoryName(), descriptor);
       }
     }
 
-    ImmutableSetMultimap<String, ImplementationMethodDescriptor>
+    ImmutableSetMultimap<PackageAndClass, ImplementationMethodDescriptor>
         implementationMethodDescriptors = implementationMethodDescriptorsBuilder.build();
+    ImmutableListMultimap<PackageAndClass, FactoryMethodDescriptor> indexedMethods =
+        indexedMethodsBuilder.build();
+    ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated =
+        simpleNamesToNames(indexedMethods.keySet());
+    FactoryWriter factoryWriter = new FactoryWriter(processingEnv, factoriesBeingCreated);
 
-    for (Entry<String, Collection<FactoryMethodDescriptor>> entry
-        : indexedMethods.build().asMap().entrySet()) {
-      ImmutableSet.Builder<TypeMirror> extending = ImmutableSet.builder();
-      ImmutableSortedSet.Builder<TypeMirror> implementing =
-          ImmutableSortedSet.orderedBy(
-              new Comparator<TypeMirror>() {
-                @Override
-                public int compare(TypeMirror first, TypeMirror second) {
-                  String firstName = MoreTypes.asTypeElement(first).getQualifiedName().toString();
-                  String secondName = MoreTypes.asTypeElement(second).getQualifiedName().toString();
-                  return firstName.compareTo(secondName);
-                }
-              });
-      boolean publicType = false;
-      Boolean allowSubclasses = null;
-      boolean skipCreation = false;
-      for (FactoryMethodDescriptor methodDescriptor : entry.getValue()) {
-        extending.add(methodDescriptor.declaration().extendingType().asType());
-        for (TypeElement implementingType : methodDescriptor.declaration().implementingTypes()) {
-          implementing.add(implementingType.asType());
-        }
-        publicType |= methodDescriptor.publicMethod();
-        if (allowSubclasses == null) {
-          allowSubclasses = methodDescriptor.declaration().allowSubclasses();
-        } else if (!allowSubclasses.equals(methodDescriptor.declaration().allowSubclasses())) {
-          skipCreation = true;
-          messager.printMessage(Kind.ERROR,
-              "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.",
-              methodDescriptor.declaration().target(),
-              methodDescriptor.declaration().mirror(),
-              methodDescriptor.declaration().valuesMap().get("allowSubclasses"));
-        }
-      }
-      if (!skipCreation) {
-        try {
-          factoryWriter.writeFactory(
-              FactoryDescriptor.create(
-                  entry.getKey(),
-                  Iterables.getOnlyElement(extending.build()),
-                  implementing.build(),
-                  publicType,
-                  ImmutableSet.copyOf(entry.getValue()),
-                  implementationMethodDescriptors.get(entry.getKey()),
-                  allowSubclasses));
-        } catch (IOException e) {
-          messager.printMessage(Kind.ERROR, "failed: " + e);
-        }
-      }
-    }
+    indexedMethods.asMap().forEach(
+        (factoryName, methodDescriptors) -> {
+          // The sets of classes that are mentioned in the `extending` and `implementing` elements,
+          // respectively, of the @AutoFactory annotations for this factory.
+          ImmutableSet.Builder<TypeMirror> extending = newTypeSetBuilder();
+          ImmutableSortedSet.Builder<TypeMirror> implementing = newTypeSetBuilder();
+          boolean publicType = false;
+          Boolean allowSubclasses = null;
+          boolean skipCreation = false;
+          for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) {
+            extending.add(methodDescriptor.declaration().extendingType().asType());
+            for (TypeElement implementingType :
+                methodDescriptor.declaration().implementingTypes()) {
+              implementing.add(implementingType.asType());
+            }
+            publicType |= methodDescriptor.publicMethod();
+            if (allowSubclasses == null) {
+              allowSubclasses = methodDescriptor.declaration().allowSubclasses();
+            } else if (!allowSubclasses.equals(methodDescriptor.declaration().allowSubclasses())) {
+              skipCreation = true;
+              messager.printMessage(Kind.ERROR,
+                  "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.",
+                  methodDescriptor.declaration().target(),
+                  methodDescriptor.declaration().mirror(),
+                  methodDescriptor.declaration().valuesMap().get("allowSubclasses"));
+            }
+          }
+          if (!skipCreation) {
+            try {
+              factoryWriter.writeFactory(
+                  FactoryDescriptor.create(
+                      factoryName,
+                      Iterables.getOnlyElement(extending.build()),
+                      implementing.build(),
+                      publicType,
+                      ImmutableSet.copyOf(methodDescriptors),
+                      implementationMethodDescriptors.get(factoryName),
+                      allowSubclasses));
+            } catch (IOException e) {
+              messager.printMessage(Kind.ERROR, "failed: " + e);
+            }
+          }
+        });
   }
 
   private ImmutableSet<ImplementationMethodDescriptor> implementationMethods(
@@ -216,8 +207,24 @@
     return Iterables.getOnlyElement(types).asType();
   }
 
+  private static ImmutableSetMultimap<String, PackageAndClass> simpleNamesToNames(
+      ImmutableSet<PackageAndClass> names) {
+    // .collect(toImmutableSetMultimap(...)) would make this much simpler but ran into problems in
+    // Google's internal build system because of multiple Guava versions.
+    ImmutableSetMultimap.Builder<String, PackageAndClass> builder = ImmutableSetMultimap.builder();
+    for (PackageAndClass name : names) {
+      builder.put(name.className(), name);
+    }
+    return builder.build();
+  }
+
+  private static ImmutableSortedSet.Builder<TypeMirror> newTypeSetBuilder() {
+    return ImmutableSortedSet.orderedBy(
+        Comparator.comparing(t -> MoreTypes.asTypeElement(t).getQualifiedName().toString()));
+  }
+
   @Override
-  public Set<String> getSupportedAnnotationTypes() {
+  public ImmutableSet<String> getSupportedAnnotationTypes() {
     return ImmutableSet.of(AutoFactory.class.getName(), Provided.class.getName());
   }
 
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java
index 68ae678..5ed2307 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java
@@ -46,7 +46,7 @@
         }
       };
 
-  abstract String name();
+  abstract PackageAndClass name();
   abstract TypeMirror extendingType();
   abstract ImmutableSet<TypeMirror> implementingTypes();
   abstract boolean publicType();
@@ -76,7 +76,7 @@
   }
 
   static FactoryDescriptor create(
-      String name,
+      PackageAndClass name,
       TypeMirror extendingType,
       ImmutableSet<TypeMirror> implementingTypes,
       boolean publicType,
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java
index c3a0159..43e5097 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java
@@ -41,7 +41,7 @@
   abstract Builder toBuilder();
   abstract boolean isVarArgs();
 
-  final String factoryName() {
+  final PackageAndClass factoryName() {
     return declaration().getFactoryName();
   }
 
@@ -54,7 +54,7 @@
   }
 
   @AutoValue.Builder
-  static abstract class Builder {
+  abstract static class Builder {
     abstract Builder declaration(AutoFactoryDeclaration declaration);
     abstract Builder name(String name);
     abstract Builder returnType(TypeMirror returnType);
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
index 3bb68e1..53b99cb 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
@@ -29,6 +29,7 @@
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.squareup.javapoet.AnnotationSpec;
@@ -44,10 +45,12 @@
 import java.io.IOException;
 import java.util.Iterator;
 import javax.annotation.processing.Filer;
+import javax.annotation.processing.ProcessingEnvironment;
 import javax.inject.Inject;
 import javax.inject.Provider;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.type.TypeVariable;
 import javax.lang.model.util.Elements;
@@ -57,18 +60,22 @@
   private final Filer filer;
   private final Elements elements;
   private final SourceVersion sourceVersion;
+  private final ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated;
 
-  FactoryWriter(Filer filer, Elements elements, SourceVersion sourceVersion) {
-    this.filer = filer;
-    this.elements = elements;
-    this.sourceVersion = sourceVersion;
+  FactoryWriter(
+      ProcessingEnvironment processingEnv,
+      ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated) {
+    this.filer = processingEnv.getFiler();
+    this.elements = processingEnv.getElementUtils();
+    this.sourceVersion = processingEnv.getSourceVersion();
+    this.factoriesBeingCreated = factoriesBeingCreated;
   }
 
   private static final Joiner ARGUMENT_JOINER = Joiner.on(", ");
 
-  void writeFactory(final FactoryDescriptor descriptor)
+  void writeFactory(FactoryDescriptor descriptor)
       throws IOException {
-    String factoryName = getSimpleName(descriptor.name()).toString();
+    String factoryName = descriptor.name().className();
     TypeSpec.Builder factory =
         classBuilder(factoryName)
             .addOriginatingElement(descriptor.declaration().targetType());
@@ -98,7 +105,7 @@
     addImplementationMethods(factory, descriptor);
     addCheckNotNullMethod(factory, descriptor);
 
-    JavaFile.builder(getPackage(descriptor.name()), factory.build())
+    JavaFile.builder(descriptor.name().packageName(), factory.build())
         .skipJavaLangImports(true)
         .build()
         .writeTo(filer);
@@ -109,7 +116,7 @@
     factory.addTypeVariables(typeVariableNames);
   }
 
-  private static void addConstructorAndProviderFields(
+  private void addConstructorAndProviderFields(
       TypeSpec.Builder factory, FactoryDescriptor descriptor) {
     MethodSpec.Builder constructor = constructorBuilder().addAnnotation(Inject.class);
     if (descriptor.publicType()) {
@@ -118,7 +125,7 @@
     Iterator<ProviderField> providerFields = descriptor.providers().values().iterator();
     for (int argumentIndex = 1; providerFields.hasNext(); argumentIndex++) {
       ProviderField provider = providerFields.next();
-      TypeName typeName = TypeName.get(provider.key().type().get()).box();
+      TypeName typeName = resolveTypeName(provider.key().type().get()).box();
       TypeName providerType = ParameterizedTypeName.get(ClassName.get(Provider.class), typeName);
       factory.addField(providerType, provider.name(), PRIVATE, FINAL);
       if (provider.key().qualifier().isPresent()) {
@@ -132,7 +139,7 @@
     factory.addMethod(constructor.build());
   }
 
-  private static void addFactoryMethods(
+  private void addFactoryMethods(
       TypeSpec.Builder factory,
       FactoryDescriptor descriptor,
       ImmutableSet<TypeVariableName> factoryTypeVariables) {
@@ -183,7 +190,7 @@
     }
   }
 
-  private static void addImplementationMethods(
+  private void addImplementationMethods(
       TypeSpec.Builder factory, FactoryDescriptor descriptor) {
     for (ImplementationMethodDescriptor methodDescriptor :
         descriptor.implementationMethodDescriptors()) {
@@ -215,11 +222,11 @@
    * {@link ParameterSpec}s to match {@code parameters}. Note that the type of the {@link
    * ParameterSpec}s match {@link Parameter#type()} and not {@link Key#type()}.
    */
-  private static Iterable<ParameterSpec> parameters(Iterable<Parameter> parameters) {
+  private ImmutableList<ParameterSpec> parameters(Iterable<Parameter> parameters) {
     ImmutableList.Builder<ParameterSpec> builder = ImmutableList.builder();
     for (Parameter parameter : parameters) {
       ParameterSpec.Builder parameterBuilder =
-          ParameterSpec.builder(TypeName.get(parameter.type().get()), parameter.name());
+          ParameterSpec.builder(resolveTypeName(parameter.type().get()), parameter.name());
       for (AnnotationMirror annotation :
           Iterables.concat(parameter.nullable().asSet(), parameter.key().qualifier().asSet())) {
         parameterBuilder.addAnnotation(AnnotationSpec.get(annotation));
@@ -266,23 +273,34 @@
     return false;
   }
 
-  private static CharSequence getSimpleName(CharSequence fullyQualifiedName) {
-    int lastDot = lastIndexOf(fullyQualifiedName, '.');
-    return fullyQualifiedName.subSequence(lastDot + 1, fullyQualifiedName.length());
-  }
-
-  private static String getPackage(CharSequence fullyQualifiedName) {
-    int lastDot = lastIndexOf(fullyQualifiedName, '.');
-    return lastDot == -1 ? "" : fullyQualifiedName.subSequence(0, lastDot).toString();
-  }
-
-  private static int lastIndexOf(CharSequence charSequence, char c) {
-    for (int i = charSequence.length() - 1; i >= 0; i--) {
-      if (charSequence.charAt(i) == c) {
-        return i;
-      }
+  /**
+   * Returns an appropriate {@code TypeName} for the given type. If the type is an
+   * {@code ErrorType}, and if it is a simple-name reference to one of the {@code *Factory}
+   * classes that we are going to generate, then we return its fully-qualified name. In every other
+   * case we just return {@code TypeName.get(type)}. Specifically, if it is an {@code ErrorType}
+   * referencing some other type, or referencing one of the classes we are going to generate but
+   * using its fully-qualified name, then we leave it as-is. JavaPoet treats {@code TypeName.get(t)}
+   * the same for {@code ErrorType} as for {@code DeclaredType}, which means that if this is a name
+   * that will eventually be generated then the code we write that references the type will in fact
+   * compile.
+   *
+   * <p>A simpler alternative would be to defer processing to a later round if we find an
+   * {@code @AutoFactory} class that references undefined types, under the assumption that something
+   * else will generate those types in the meanwhile. However, this would fail if for example
+   * {@code @AutoFactory class Foo} has a constructor parameter of type {@code BarFactory} and
+   * {@code @AutoFactory class Bar} has a constructor parameter of type {@code FooFactory}. We did
+   * in fact find instances of this in Google's source base.
+   */
+  private TypeName resolveTypeName(TypeMirror type) {
+    if (type.getKind() != TypeKind.ERROR) {
+      return TypeName.get(type);
     }
-    return -1;
+    ImmutableSet<PackageAndClass> factoryNames = factoriesBeingCreated.get(type.toString());
+    if (factoryNames.size() == 1) {
+      PackageAndClass packageAndClass = Iterables.getOnlyElement(factoryNames);
+      return ClassName.get(packageAndClass.packageName(), packageAndClass.className());
+    }
+    return TypeName.get(type);
   }
 
   private static ImmutableSet<TypeVariableName> getFactoryTypeVariables(
diff --git a/factory/src/main/java/com/google/auto/factory/processor/PackageAndClass.java b/factory/src/main/java/com/google/auto/factory/processor/PackageAndClass.java
new file mode 100644
index 0000000..857fba1
--- /dev/null
+++ b/factory/src/main/java/com/google/auto/factory/processor/PackageAndClass.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.auto.factory.processor;
+
+import com.google.auto.value.AutoValue;
+
+/** A Java class name, separated into its package part and its class part. */
+@AutoValue
+abstract class PackageAndClass {
+  /**
+   * The package part of this class name. For {@code java.util.Map.Entry}, it would be {@code
+   * java.util}.
+   */
+  abstract String packageName();
+
+  /**
+   * The class part of this class name. For {@code java.util.Map.Entry}, it would be {@code
+   * Map.Entry}.
+   */
+  abstract String className();
+
+  static PackageAndClass of(String packageName, String className) {
+    return new AutoValue_PackageAndClass(packageName, className);
+  }
+}
diff --git a/service/Android.bp b/service/Android.bp
index 033fd84..318c47b 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_auto_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_auto_license"],
+}
+
 java_library {
     name: "auto_service_annotations",
     host_supported: true,
diff --git a/value/Android.bp b/value/Android.bp
index 2e1d819..73a7215 100644
--- a/value/Android.bp
+++ b/value/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_auto_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_auto_license"],
+}
+
 filegroup {
     name: "auto_value_plugin_resources",
     path: "src/main/java",
@@ -35,7 +44,14 @@
     host_supported: true,
     srcs: ["src/main/java/com/google/auto/value/*.java"],
     sdk_version: "core_current",
+    // AutoValue is a pure java library without any dependency on android framework, however due to
+    // a dependency from an apex it is required to have a min_sdk_version
+    min_sdk_version: "19",
     visibility: ["//visibility:public"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.extservices",
+    ],
 }
 
 java_plugin {
diff --git a/value/annotations/pom.xml b/value/annotations/pom.xml
index d0d4a6b..d6c03de 100644
--- a/value/annotations/pom.xml
+++ b/value/annotations/pom.xml
@@ -21,12 +21,12 @@
   <parent>
     <groupId>com.google.auto.value</groupId>
     <artifactId>auto-value-parent</artifactId>
-    <version>HEAD-SNAPSHOT</version>
+    <version>1.7.4</version>
   </parent>
 
   <groupId>com.google.auto.value</groupId>
   <artifactId>auto-value-annotations</artifactId>
-  <version>HEAD-SNAPSHOT</version>
+  <version>1.7.4</version>
   <name>AutoValue Annotations</name>
   <description>
     Immutable value-type code generation for Java 1.6+.
diff --git a/value/pom.xml b/value/pom.xml
index 9f8be5a..dcf2cb1 100644
--- a/value/pom.xml
+++ b/value/pom.xml
@@ -26,7 +26,7 @@
 
   <groupId>com.google.auto.value</groupId>
   <artifactId>auto-value-parent</artifactId>
-  <version>HEAD-SNAPSHOT</version>
+  <version>1.7.4</version>
   <name>AutoValue Parent</name>
   <description>
     Immutable value-type code generation for Java 7+.
diff --git a/value/processor/pom.xml b/value/processor/pom.xml
index 0aa9ec8..5968352 100644
--- a/value/processor/pom.xml
+++ b/value/processor/pom.xml
@@ -21,12 +21,12 @@
   <parent>
     <groupId>com.google.auto.value</groupId>
     <artifactId>auto-value-parent</artifactId>
-    <version>HEAD-SNAPSHOT</version>
+    <version>1.7.4</version>
   </parent>
 
   <groupId>com.google.auto.value</groupId>
   <artifactId>auto-value</artifactId>
-  <version>HEAD-SNAPSHOT</version>
+  <version>1.7.4</version>
   <name>AutoValue Processor</name>
   <description>
     Immutable value-type code generation for Java 1.6+.
diff --git a/value/src/it/functional/pom.xml b/value/src/it/functional/pom.xml
index 4157da9..750b9c4 100644
--- a/value/src/it/functional/pom.xml
+++ b/value/src/it/functional/pom.xml
@@ -22,14 +22,14 @@
   <parent>
     <groupId>com.google.auto.value</groupId>
     <artifactId>auto-value-parent</artifactId>
-    <version>HEAD-SNAPSHOT</version>
+    <version>1.7.4</version>
     <relativePath>../../../pom.xml</relativePath>
   </parent>
   <url>https://github.com/google/auto/tree/master/value</url>
 
   <groupId>com.google.auto.value.it.functional</groupId>
   <artifactId>functional</artifactId>
-  <version>HEAD-SNAPSHOT</version>
+  <version>1.7.4</version>
   <name>Auto-Value Functional Integration Test</name>
   <properties>
     <exclude.tests>this-matches-nothing</exclude.tests>
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java
index 27356c5..10812f8 100644
--- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java
@@ -19,7 +19,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 import static com.google.common.truth.Truth8.assertThat;
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assume.assumeTrue;
 
 import com.google.common.collect.ImmutableList;
@@ -375,12 +375,9 @@
 
   @Test
   public void testNotNullablePrimitiveArrays() {
-    try {
-      PrimitiveArrays.create(null, new int[0]);
-      fail("Construction with null value for non-@Nullable array should have failed");
-    } catch (NullPointerException e) {
-      assertThat(e.getMessage()).contains("booleans");
-    }
+    NullPointerException e =
+        assertThrows(NullPointerException.class, () -> PrimitiveArrays.create(null, new int[0]));
+    assertThat(e).hasMessageThat().contains("booleans");
   }
 
   @AutoValue
@@ -421,12 +418,10 @@
     assertThat(instance3.notNullable()).isEqualTo("hello");
     assertThat(instance3.nullable()).isEqualTo("world");
 
-    try {
-      NullablePropertyWithBuilder.builder().build();
-      fail("Expected IllegalStateException for unset non-@Nullable property");
-    } catch (IllegalStateException e) {
-      assertThat(e.getMessage()).contains("notNullable");
-    }
+    IllegalStateException e =
+        assertThrows(
+            IllegalStateException.class, () -> NullablePropertyWithBuilder.builder().build());
+    assertThat(e).hasMessageThat().contains("notNullable");
   }
 
   @AutoValue
@@ -470,11 +465,39 @@
     assertThat(instance3.notOptional()).isEqualTo("hello");
     assertThat(instance3.optional()).hasValue("world");
 
-    try {
-      OptionalPropertyWithNullableBuilder.builder().build();
-      fail("Expected IllegalStateException for unset non-Optional property");
-    } catch (IllegalStateException expected) {
+    assertThrows(
+        IllegalStateException.class, () -> OptionalPropertyWithNullableBuilder.builder().build());
+  }
+
+  @AutoValue
+  public abstract static class NullableOptionalPropertyWithNullableBuilder {
+    public abstract @Nullable Optional<String> optional();
+
+    public static Builder builder() {
+      return new AutoValue_AutoValueJava8Test_NullableOptionalPropertyWithNullableBuilder.Builder();
     }
+
+    @AutoValue.Builder
+    public interface Builder {
+      Builder optional(@Nullable String s);
+
+      NullableOptionalPropertyWithNullableBuilder build();
+    }
+  }
+
+  @Test
+  public void testNullableOptional() {
+    NullableOptionalPropertyWithNullableBuilder instance1 =
+        NullableOptionalPropertyWithNullableBuilder.builder().build();
+    assertThat(instance1.optional()).isNull();
+
+    NullableOptionalPropertyWithNullableBuilder instance2 =
+        NullableOptionalPropertyWithNullableBuilder.builder().optional(null).build();
+    assertThat(instance2.optional()).isEmpty();
+
+    NullableOptionalPropertyWithNullableBuilder instance3 =
+        NullableOptionalPropertyWithNullableBuilder.builder().optional("haruspex").build();
+    assertThat(instance3.optional()).hasValue("haruspex");
   }
 
   @AutoValue
@@ -522,18 +545,10 @@
 
     BuilderWithUnprefixedGetters.Builder<String> builder = BuilderWithUnprefixedGetters.builder();
     assertThat(builder.t()).isNull();
-    try {
-      builder.list();
-      fail("Attempt to retrieve unset list property should have failed");
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set");
-    }
-    try {
-      builder.ints();
-      fail("Attempt to retrieve unset ints property should have failed");
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessageThat().isEqualTo("Property \"ints\" has not been set");
-    }
+    IllegalStateException e1 = assertThrows(IllegalStateException.class, () -> builder.list());
+    assertThat(e1).hasMessageThat().isEqualTo("Property \"list\" has not been set");
+    IllegalStateException e2 = assertThrows(IllegalStateException.class, () -> builder.ints());
+    assertThat(e2).hasMessageThat().isEqualTo("Property \"ints\" has not been set");
 
     builder.setList(names);
     assertThat(builder.list()).isSameInstanceAs(names);
@@ -596,12 +611,8 @@
 
     BuilderWithPrefixedGetters.Builder<String> builder = BuilderWithPrefixedGetters.builder();
     assertThat(builder.getInts()).isNull();
-    try {
-      builder.getList();
-      fail("Attempt to retrieve unset list property should have failed");
-    } catch (IllegalStateException e) {
-      assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set");
-    }
+    IllegalStateException e = assertThrows(IllegalStateException.class, () -> builder.getList());
+    assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set");
 
     builder.setList(names);
     assertThat(builder.getList()).isSameInstanceAs(names);
diff --git a/value/src/it/gwtserializer/pom.xml b/value/src/it/gwtserializer/pom.xml
index cd56484..eea8952 100644
--- a/value/src/it/gwtserializer/pom.xml
+++ b/value/src/it/gwtserializer/pom.xml
@@ -22,14 +22,14 @@
   <parent>
     <groupId>com.google.auto.value</groupId>
     <artifactId>auto-value-parent</artifactId>
-    <version>HEAD-SNAPSHOT</version>
+    <version>1.7.4</version>
     <relativePath>../../../pom.xml</relativePath>
   </parent>
   <url>https://github.com/google/auto/tree/master/value</url>
 
   <groupId>com.google.auto.value.it.gwtserializer</groupId>
   <artifactId>gwtserializer</artifactId>
-  <version>HEAD-SNAPSHOT</version>
+  <version>1.7.4</version>
   <name>Auto-Value GWT-RPC Serialization Integration Test</name>
   <dependencyManagement>
     <dependencies>
diff --git a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java
index db2f221..0ca46bd 100644
--- a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java
+++ b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java
@@ -100,8 +100,11 @@
   private static final String AUTO_VALUE_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoValue";
   private static final String COPY_ANNOTATIONS_NAME = AUTO_VALUE_NAME + ".CopyAnnotations";
 
+  // Maven is configured to shade (rewrite) com.google packages to prevent dependency conflicts.
+  // Split up the package here with a call to concat to prevent Maven from finding and rewriting it,
+  // so that this will be able to find the LazyInit annotation if it's on the classpath.
   private static final ClassName LAZY_INIT =
-      ClassName.get("com.google.errorprone.annotations.concurrent", "LazyInit");
+      ClassName.get("com".concat(".google.errorprone.annotations.concurrent"), "LazyInit");
 
   private static final AnnotationSpec SUPPRESS_WARNINGS =
       AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "Immutable").build();
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java
index d05ed43..d73c1c2 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java
@@ -72,7 +72,9 @@
   void processType(TypeElement autoOneOfType) {
     if (autoOneOfType.getKind() != ElementKind.CLASS) {
       errorReporter()
-          .abortWithError(autoOneOfType, "@" + AUTO_ONE_OF_NAME + " only applies to classes");
+          .abortWithError(
+              autoOneOfType,
+              "[AutoOneOfNotClass] @" + AUTO_ONE_OF_NAME + " only applies to classes");
     }
     checkModifiersIfNested(autoOneOfType);
     DeclaredType kindMirror = mirrorForKindType(autoOneOfType);
@@ -129,7 +131,7 @@
       errorReporter()
           .abortWithError(
               autoOneOfType,
-              "annotation processor for @AutoOneOf was invoked with a type"
+              "[AutoOneOfCompilerBug] annotation processor for @AutoOneOf was invoked with a type"
                   + " that does not have that annotation; this is probably a compiler bug");
     }
     AnnotationValue kindValue =
@@ -184,7 +186,8 @@
             errorReporter()
                 .reportError(
                     kindElement,
-                    "Enum has no constant with name corresponding to property '%s'",
+                    "[AutoOneOfNoEnumConstant] Enum has no constant with name corresponding to"
+                        + " property '%s'",
                     property);
           }
         });
@@ -195,7 +198,8 @@
             errorReporter()
                 .reportError(
                     constant,
-                    "Name of enum constant '%s' does not correspond to any property name",
+                    "[AutoOneOfBadEnumConstant] Name of enum constant '%s' does not correspond to"
+                        + " any property name",
                     constant.getSimpleName());
           }
         });
@@ -221,7 +225,7 @@
         errorReporter()
             .reportError(
                 autoOneOfType,
-                "%s must have a no-arg abstract method returning %s",
+                "[AutoOneOfNoKindGetter] %s must have a no-arg abstract method returning %s",
                 autoOneOfType,
                 kindMirror);
         break;
@@ -230,7 +234,10 @@
       default:
         for (ExecutableElement getter : kindGetters) {
           errorReporter()
-              .reportError(getter, "More than one abstract method returns %s", kindMirror);
+              .reportError(
+                  getter,
+                  "[AutoOneOfTwoKindGetters] More than one abstract method returns %s",
+                  kindMirror);
         }
     }
     throw new AbortProcessingException();
@@ -254,7 +261,8 @@
         // implement this alien method.
         errorReporter()
             .reportWarning(
-                method, "Abstract methods in @AutoOneOf classes must have no parameters");
+                method,
+                "[AutoOneOfParams] Abstract methods in @AutoOneOf classes must have no parameters");
       }
     }
     errorReporter().abortIfAnyError();
@@ -278,7 +286,9 @@
   @Override
   Optional<String> nullableAnnotationForMethod(ExecutableElement propertyMethod) {
     if (nullableAnnotationFor(propertyMethod, propertyMethod.getReturnType()).isPresent()) {
-      errorReporter().reportError(propertyMethod, "@AutoOneOf properties cannot be @Nullable");
+      errorReporter()
+          .reportError(
+              propertyMethod, "[AutoOneOfNullable] @AutoOneOf properties cannot be @Nullable");
     }
     return Optional.empty();
   }
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java
index 4214c12..36a82bd 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java
@@ -71,7 +71,6 @@
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.TypeParameterElement;
 import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
 import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
@@ -301,7 +300,8 @@
       for (TypeElement type : deferredTypes) {
         errorReporter.reportError(
             type,
-            "Did not generate @%s class for %s because it references undefined types",
+            "[AutoValueUndefined] Did not generate @%s class for %s because it references"
+                + " undefined types",
             simpleAnnotationName,
             type.getQualifiedName());
       }
@@ -331,7 +331,10 @@
       } catch (RuntimeException e) {
         String trace = Throwables.getStackTraceAsString(e);
         errorReporter.reportError(
-            type, "@%s processor threw an exception: %s", simpleAnnotationName, trace);
+            type,
+            "[AutoValueException] @%s processor threw an exception: %s",
+            simpleAnnotationName,
+            trace);
         throw e;
       }
     }
@@ -378,8 +381,9 @@
     ImmutableSet.Builder<Property> props = ImmutableSet.builder();
     propertyMethodsAndTypes.forEach(
         (propertyMethod, returnType) -> {
-          String propertyType = TypeEncoder.encodeWithAnnotations(
-              returnType, getExcludedAnnotationTypes(propertyMethod));
+          String propertyType =
+              TypeEncoder.encodeWithAnnotations(
+                  returnType, getExcludedAnnotationTypes(propertyMethod));
           String propertyName = methodToPropertyName.get(propertyMethod);
           String identifier = methodToIdentifier.get(propertyMethod);
           ImmutableList<String> fieldAnnotations =
@@ -399,7 +403,9 @@
                   nullableAnnotation);
           props.add(p);
           if (p.isNullable() && returnType.getKind().isPrimitive()) {
-            errorReporter().reportError(propertyMethod, "Primitive types cannot be @Nullable");
+            errorReporter()
+                .reportError(
+                    propertyMethod, "[AutoValueNullPrimitive] Primitive types cannot be @Nullable");
           }
         });
     return props.build();
@@ -517,7 +523,10 @@
         // reportedDups prevents us from reporting more than one error for the same method.
         for (ExecutableElement context : contexts) {
           errorReporter.reportError(
-              context, "More than one @%s property called %s", simpleAnnotationName, name);
+              context,
+              "[AutoValueDupProperty] More than one @%s property called %s",
+              simpleAnnotationName,
+              name);
         }
       }
     }
@@ -622,16 +631,18 @@
     if (enclosingKind.isClass() || enclosingKind.isInterface()) {
       if (type.getModifiers().contains(Modifier.PRIVATE)) {
         errorReporter.abortWithError(
-            type, "@%s class must not be private", simpleAnnotationName);
+            type, "[AutoValuePrivate] @%s class must not be private", simpleAnnotationName);
       } else if (Visibility.effectiveVisibilityOfElement(type).equals(Visibility.PRIVATE)) {
         // The previous case, where the class itself is private, is much commoner so it deserves
         // its own error message, even though it would be caught by the test here too.
         errorReporter.abortWithError(
-            type, "@%s class must not be nested in a private class", simpleAnnotationName);
+            type,
+            "[AutoValueInPrivate] @%s class must not be nested in a private class",
+            simpleAnnotationName);
       }
       if (!type.getModifiers().contains(Modifier.STATIC)) {
         errorReporter.abortWithError(
-            type, "Nested @%s class must be static", simpleAnnotationName);
+            type, "[AutoValueInner] Nested @%s class must be static", simpleAnnotationName);
       }
     }
     // In principle type.getEnclosingElement() could be an ExecutableElement (for a class
@@ -766,13 +777,14 @@
   final void checkReturnType(TypeElement autoValueClass, ExecutableElement getter) {
     TypeMirror type = getter.getReturnType();
     if (type.getKind() == TypeKind.ARRAY) {
-      TypeMirror componentType = ((ArrayType) type).getComponentType();
-      if (componentType.getKind().isPrimitive()) {
+      TypeMirror componentType = MoreTypes.asArray(type).getComponentType();
+     if (componentType.getKind().isPrimitive()) {
         warnAboutPrimitiveArrays(autoValueClass, getter);
       } else {
         errorReporter.reportError(
             getter,
-            "An @%s class cannot define an array-valued property unless it is a primitive array",
+            "[AutoValueArray] An @%s class cannot define an array-valued property unless it is a"
+                + " primitive array",
             simpleAnnotationName);
       }
     }
@@ -799,10 +811,11 @@
       String context = sameClass ? "" : (" Method: " + getter.getEnclosingElement() + "." + getter);
       errorReporter.reportWarning(
           element,
-          "An @%s property that is a primitive array returns the original array, which can"
-              + " therefore be modified by the caller. If this is OK, you can suppress this warning"
-              + " with @SuppressWarnings(\"mutable\"). Otherwise, you should replace the property"
-              + " with an immutable type, perhaps a simple wrapper around the original array.%s",
+          "[AutoValueMutable] An @%s property that is a primitive array returns the original"
+              + " array, which can therefore be modified by the caller. If this is OK, you can"
+              + " suppress this warning with @SuppressWarnings(\"mutable\"). Otherwise, you should"
+              + " replace the property with an immutable type, perhaps a simple wrapper around the"
+              + " original array.%s",
           simpleAnnotationName,
           context);
     }
@@ -815,8 +828,8 @@
   private static class ContainsMutableVisitor extends SimpleAnnotationValueVisitor8<Boolean, Void> {
     @Override
     public Boolean visitArray(List<? extends AnnotationValue> list, Void p) {
-      return list.stream().map(av -> av.getValue()).anyMatch("mutable"::equals);
-    }
+      return list.stream().map(AnnotationValue::getValue).anyMatch("mutable"::equals);
+   }
   }
 
   /**
@@ -1102,7 +1115,10 @@
       // error later because user code will have a reference to the code we were supposed to
       // generate (new AutoValue_Foo() or whatever) and that reference will be undefined.
       errorReporter.reportWarning(
-          originatingType, "Could not write generated class %s: %s", className, e);
+          originatingType,
+          "[AutoValueCouldNotWrite] Could not write generated class %s: %s",
+          className,
+          e);
     }
   }
 }
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
index f3b396c..aafffd7 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
@@ -121,8 +121,8 @@
         errorReporter()
             .reportWarning(
                 null,
-                "An exception occurred while looking for AutoValue extensions."
-                    + " No extensions will function.%s\n%s",
+                "[AutoValueExtensionsException] An exception occurred while looking for AutoValue"
+                    + " extensions. No extensions will function.%s\n%s",
                 explain,
                 Throwables.getStackTraceAsString(e));
         extensions = ImmutableList.of();
@@ -170,19 +170,22 @@
       errorReporter()
           .abortWithError(
               type,
-              "annotation processor for @AutoValue was invoked with a type"
+              "[AutoValueCompilerBug] annotation processor for @AutoValue was invoked with a type"
                   + " that does not have that annotation; this is probably a compiler bug");
     }
     if (type.getKind() != ElementKind.CLASS) {
-      errorReporter().abortWithError(type, "@AutoValue only applies to classes");
+      errorReporter()
+          .abortWithError(type, "[AutoValueNotClass] @AutoValue only applies to classes");
     }
     if (ancestorIsAutoValue(type)) {
-      errorReporter().abortWithError(type, "One @AutoValue class may not extend another");
+      errorReporter()
+          .abortWithError(type, "[AutoValueExtend] One @AutoValue class may not extend another");
     }
     if (implementsAnnotation(type)) {
       errorReporter()
           .abortWithError(
-              type, "@AutoValue may not be used to implement an annotation"
+              type,
+              "[AutoValueImplAnnotation] @AutoValue may not be used to implement an annotation"
                   + " interface; try using @AutoAnnotation instead");
     }
     checkModifiersIfNested(type);
@@ -333,7 +336,8 @@
         errorReporter()
             .reportError(
                 type,
-                "More than one extension wants to generate the final class: %s",
+                "[AutoValueMultiFinal] More than one extension wants to generate the final class:"
+                    + " %s",
                 finalExtensions.stream().map(this::extensionName).collect(joining(", ")));
         break;
     }
@@ -355,7 +359,8 @@
           errorReporter()
               .reportError(
                   type,
-                  "Extension %s wants to consume a property that does not exist: %s",
+                  "[AutoValueConsumeNonexist] Extension %s wants to consume a property that does"
+                      + " not exist: %s",
                   extensionName(extension),
                   consumedProperty);
         } else {
@@ -367,8 +372,8 @@
           errorReporter()
               .reportError(
                   type,
-                  "Extension %s wants to consume a method that is not one of the abstract methods"
-                      + " in this class: %s",
+                  "[AutoValueConsumeNotAbstract] Extension %s wants to consume a method that is"
+                      + " not one of the abstract methods in this class: %s",
                   extensionName(extension),
                   consumedMethod);
         } else {
@@ -379,8 +384,8 @@
         errorReporter()
             .reportError(
                 repeat,
-                "Extension %s wants to consume a method that was already consumed by another"
-                    + " extension",
+                "[AutoValueMultiConsume] Extension %s wants to consume a method that was already"
+                    + " consumed by another extension",
                 extensionName(extension));
       }
       consumed.addAll(consumedHere);
@@ -404,10 +409,12 @@
         // another, and therefore leaves us with both an abstract method and the subclass method
         // that overrides it. This shows up in AutoValueTest.LukesBase for example.
         String extensionMessage = extensionsPresent ? ", and no extension consumed it" : "";
-        errorReporter().reportWarning(
-            method,
-            "Abstract method is neither a property getter nor a Builder converter%s",
-            extensionMessage);
+        errorReporter()
+            .reportWarning(
+                method,
+                "[AutoValueBuilderWhat] Abstract method is neither a property getter nor a Builder"
+                    + " converter%s",
+                extensionMessage);
       }
     }
     errorReporter().abortIfAnyError();
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
index 724a322..81751e3 100644
--- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
@@ -20,6 +20,7 @@
 
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
+import com.google.auto.value.processor.BuilderSpec.Copier;
 import com.google.auto.value.processor.BuilderSpec.PropertySetter;
 import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder;
 import com.google.common.base.Equivalence;
@@ -197,7 +198,7 @@
     } else {
       errorReporter.reportError(
           propertyNameToUnprefixedSetters.values().iterator().next().getSetter(),
-          "If any setter methods use the setFoo convention then all must");
+          "[AutoValueSetNotSet] If any setter methods use the setFoo convention then all must");
       return false;
     }
     getterToPropertyName.forEach(
@@ -219,10 +220,10 @@
             if (needToMakeBarBuilder && !canMakeBarBuilder) {
               errorReporter.reportError(
                   propertyBuilder.getPropertyBuilderMethod(),
-                  "Property builder method returns %1$s but there is no way to make that type"
-                      + " from %2$s: %2$s does not have a non-static toBuilder() method that"
-                      + " returns %1$s, and %1$s does not have a method addAll or"
-                      + " putAll that accepts an argument of type %2$s",
+                  "[AutoValueCantMakeBuilder] Property builder method returns %1$s but there is no"
+                      + " way to make that type from %2$s: %2$s does not have a non-static"
+                      + " toBuilder() method that returns %1$s, and %1$s does not have a method"
+                      + " addAll or putAll that accepts an argument of type %2$s",
                   propertyBuilder.getBuilderTypeMirror(),
                   propertyType);
             }
@@ -231,8 +232,13 @@
             String setterName = settersPrefixed ? prefixWithSet(property) : property;
             errorReporter.reportError(
                 builderType,
-                "Expected a method with this signature: %s%s %s(%s), or a %sBuilder() method",
-                builderType, typeParamsString(), setterName, propertyType, property);
+                "[AutoValueBuilderMissingMethod] Expected a method with this signature: %s%s"
+                    + " %s(%s), or a %sBuilder() method",
+                builderType,
+                typeParamsString(),
+                setterName,
+                propertyType,
+                property);
           }
         });
     return errorReporter.errorCount() == startErrorCount;
@@ -248,7 +254,8 @@
         classifyMethodOneArg(method);
         break;
       default:
-        errorReporter.reportError(method, "Builder methods must have 0 or 1 parameters");
+        errorReporter.reportError(
+            method, "[AutoValueBuilderArgs] Builder methods must have 0 or 1 parameters");
     }
   }
 
@@ -296,10 +303,11 @@
     } else {
       errorReporter.reportError(
           method,
-          "Method without arguments should be a build method returning %1$s%2$s,"
-              + " or a getter method with the same name and type as a getter method of %1$s,"
-              + " or fooBuilder() where foo() or getFoo() is a getter method of %1$s",
-          autoValueClass, typeParamsString());
+          "[AutoValueBuilderNoArg] Method without arguments should be a build method returning"
+              + " %1$s%2$s, or a getter method with the same name and type as a getter method of"
+              + " %1$s, or fooBuilder() where foo() or getFoo() is a getter method of %1$s",
+          autoValueClass,
+          typeParamsString());
     }
   }
 
@@ -334,9 +342,11 @@
     }
     errorReporter.reportError(
         builderGetter,
-        "Method matches a property of %1$s but has return type %2$s instead of %3$s "
-            + "or an Optional wrapping of %3$s",
-        autoValueClass, builderGetterType, originalGetterType);
+        "[AutoValueBuilderReturnType] Method matches a property of %1$s but has return type %2$s"
+            + " instead of %3$s or an Optional wrapping of %3$s",
+        autoValueClass,
+        builderGetterType,
+        originalGetterType);
   }
 
   /**
@@ -372,11 +382,13 @@
       // The second disjunct isn't needed but convinces control-flow checkers that
       // propertyNameToSetters can't be null when we call put on it below.
       errorReporter.reportError(
-          method, "Method does not correspond to a property of %s", autoValueClass);
+          method,
+          "[AutoValueBuilderWhatProp] Method does not correspond to a property of %s",
+          autoValueClass);
       checkForFailedJavaBean(method);
       return;
     }
-    Optional<Function<String, String>> function = getSetterFunction(valueGetter, method);
+    Optional<Copier> function = getSetterFunction(valueGetter, method);
     if (function.isPresent()) {
       DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType());
       ExecutableType methodMirror =
@@ -419,7 +431,7 @@
    * using a method like {@code ImmutableList.copyOf} or {@code Optional.of}, when the returned
    * function will be something like {@code s -> "Optional.of(" + s + ")"}.
    */
-  private Optional<Function<String, String>> getSetterFunction(
+  private Optional<Copier> getSetterFunction(
       ExecutableElement valueGetter, ExecutableElement setter) {
     VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters());
     boolean nullableParameter =
@@ -440,12 +452,14 @@
         if (!nullableProperty) {
           errorReporter.reportError(
               setter,
-              "Parameter of setter method is @Nullable but property method %s.%s() is not",
-              autoValueClass, valueGetter.getSimpleName());
+              "[AutoValueNullNotNull] Parameter of setter method is @Nullable but property method"
+                  + " %s.%s() is not",
+              autoValueClass,
+              valueGetter.getSimpleName());
           return Optional.empty();
         }
       }
-      return Optional.of(s -> s);
+      return Optional.of(Copier.IDENTITY);
     }
 
     // Parameter type is not equal to property type, but might be convertible with copyOf.
@@ -455,7 +469,7 @@
     }
     errorReporter.reportError(
         setter,
-        "Parameter type %s of setter method should be %s to match getter %s.%s",
+        "[AutoValueGetVsSet] Parameter type %s of setter method should be %s to match getter %s.%s",
         parameterType, targetType, autoValueClass, valueGetter.getSimpleName());
     return Optional.empty();
   }
@@ -465,14 +479,14 @@
    * to the getter's return type using one of the given methods, or {@code Optional.empty()} if the
    * conversion isn't possible. An error will have been reported in the latter case.
    */
-  private Optional<Function<String, String>> getConvertingSetterFunction(
+  private Optional<Copier> getConvertingSetterFunction(
       ImmutableList<ExecutableElement> copyOfMethods,
       ExecutableElement valueGetter,
       ExecutableElement setter,
       TypeMirror parameterType) {
     DeclaredType targetType = MoreTypes.asDeclared(getterToPropertyType.get(valueGetter));
     for (ExecutableElement copyOfMethod : copyOfMethods) {
-      Optional<Function<String, String>> function =
+      Optional<Copier> function =
           getConvertingSetterFunction(copyOfMethod, targetType, parameterType);
       if (function.isPresent()) {
         return function;
@@ -481,8 +495,8 @@
     String targetTypeSimpleName = targetType.asElement().getSimpleName().toString();
     errorReporter.reportError(
         setter,
-        "Parameter type %s of setter method should be %s to match getter %s.%s,"
-            + " or it should be a type that can be passed to %s.%s to produce %s",
+        "[AutoValueGetVsSetOrConvert] Parameter type %s of setter method should be %s to match"
+            + " getter %s.%s, or it should be a type that can be passed to %s.%s to produce %s",
         parameterType,
         targetType,
         autoValueClass,
@@ -516,7 +530,7 @@
    * @return a function that maps a string parameter to a method call using that parameter. For
    *     example it might map {@code foo} to {@code ImmutableList.copyOf(foo)}.
    */
-  private Optional<Function<String, String>> getConvertingSetterFunction(
+  private Optional<Copier> getConvertingSetterFunction(
       ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType) {
     // We have a parameter type, for example Set<? extends T>, and we want to know if it can be
     // passed to the given copyOf method, which might for example be one of these methods from
@@ -532,7 +546,15 @@
     if (TypeVariables.canAssignStaticMethodResult(
         copyOfMethod, parameterType, targetType, typeUtils)) {
       String method = TypeEncoder.encodeRaw(targetType) + "." + copyOfMethod.getSimpleName();
-      return Optional.of(s -> method + "(" + s + ")");
+      Function<String, String> callMethod = s -> method + "(" + s + ")";
+      // This is a big old hack. We guess that the method can accept a null parameter if it has
+      // "Nullable" in the name, which java.util.Optional.ofNullable and
+      // com.google.common.base.Optional.fromNullable do.
+      Copier copier =
+          method.contains("Nullable")
+              ? Copier.acceptingNull(callMethod)
+              : Copier.notAcceptingNull(callMethod);
+      return Optional.of(copier);
     }
     return Optional.empty();
   }
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java
index 613d545..7e5b17c 100644
--- a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java
@@ -86,14 +86,17 @@
       if (hasAnnotationMirror(containedClass, AUTO_VALUE_BUILDER_NAME)) {
         if (!CLASS_OR_INTERFACE.contains(containedClass.getKind())) {
           errorReporter.reportError(
-              containedClass, "@AutoValue.Builder can only apply to a class or an interface");
+              containedClass,
+              "[AutoValueBuilderClass] @AutoValue.Builder can only apply to a class or an"
+                  + " interface");
         } else if (!containedClass.getModifiers().contains(Modifier.STATIC)) {
           errorReporter.reportError(
-              containedClass, "@AutoValue.Builder cannot be applied to a non-static class");
+              containedClass,
+              "[AutoValueInnerBuilder] @AutoValue.Builder cannot be applied to a non-static class");
         } else if (builderTypeElement.isPresent()) {
           errorReporter.reportError(
               containedClass,
-              "%s already has a Builder: %s",
+              "[AutoValueTwoBuilders] %s already has a Builder: %s",
               autoValueClass,
               builderTypeElement.get());
         } else {
@@ -218,7 +221,7 @@
           if (!builderTypeParamNames.equals(typeArguments)) {
             errorReporter.reportError(
                 method,
-                "Builder converter method should return %s%s",
+                "[AutoValueBuilderConverterReturn] Builder converter method should return %s%s",
                 builderTypeElement,
                 TypeSimplifier.actualTypeParametersString(builderTypeElement));
           }
@@ -227,7 +230,8 @@
       ImmutableSet<ExecutableElement> builderMethods = methods.build();
       if (builderMethods.size() > 1) {
         errorReporter.reportError(
-            builderMethods.iterator().next(), "There can be at most one builder converter method");
+            builderMethods.iterator().next(),
+            "[AutoValueTwoBuilderConverters] There can be at most one builder converter method");
       }
       this.toBuilderMethods = builderMethods;
       return builderMethods;
@@ -265,7 +269,9 @@
           // For now we ignore methods with annotations, because for example we do want to allow
           // Jackson's @JsonCreator.
           errorReporter.reportWarning(
-              method, "Static builder() method should be in the containing class");
+              method,
+              "[AutoValueBuilderInBuilder] Static builder() method should be in the containing"
+                  + " class");
         }
       }
       this.classifier = optionalClassifier.get();
@@ -276,7 +282,8 @@
         for (Element buildMethod : errorElements) {
           errorReporter.reportError(
               buildMethod,
-              "Builder must have a single no-argument method returning %s%s",
+              "[AutoValueBuilderBuild] Builder must have a single no-argument method returning"
+                  + " %s%s",
               autoValueClass,
               typeParamsString());
         }
@@ -354,6 +361,30 @@
   }
 
   /**
+   * Specifies how to copy a parameter value into the target type. This might be the identity, or
+   * it might be something like {@code ImmutableList.of(...)} or {@code Optional.ofNullable(...)}.
+   */
+  static class Copier {
+    static final Copier IDENTITY = acceptingNull(x -> x);
+
+    private final Function<String, String> copy;
+    private final boolean acceptsNull;
+
+    private Copier(Function<String, String> copy, boolean acceptsNull) {
+      this.copy = copy;
+      this.acceptsNull = acceptsNull;
+    }
+
+    static Copier acceptingNull(Function<String, String> copy) {
+      return new Copier(copy, true);
+    }
+
+    static Copier notAcceptingNull(Function<String, String> copy) {
+      return new Copier(copy, false);
+    }
+  }
+
+  /**
    * Information about a property setter, referenced from the autovalue.vm template. A property
    * called foo (defined by a method {@code T foo()} or {@code T getFoo()}) can have a setter method
    * {@code foo(T)} or {@code setFoo(T)} that returns the builder type. Additionally, it can have a
@@ -369,12 +400,11 @@
     private final String parameterTypeString;
     private final boolean primitiveParameter;
     private final String nullableAnnotation;
-    private final Function<String, String> copyFunction;
+    private final Copier copier;
 
-    PropertySetter(
-        ExecutableElement setter, TypeMirror parameterType, Function<String, String> copyFunction) {
+    PropertySetter(ExecutableElement setter, TypeMirror parameterType, Copier copier) {
       this.setter = setter;
-      this.copyFunction = copyFunction;
+      this.copier = copier;
       this.access = SimpleMethod.access(setter);
       this.name = setter.getSimpleName().toString();
       primitiveParameter = parameterType.getKind().isPrimitive();
@@ -423,13 +453,10 @@
     }
 
     public String copy(AutoValueProcessor.Property property) {
-      String copy = copyFunction.apply(property.toString());
-
-      // Add a null guard only in cases where we are using copyOf and the property is @Nullable.
-      if (property.isNullable() && !copy.equals(property.toString())) {
+      String copy = copier.copy.apply(property.toString());
+      if (property.isNullable() && !copier.acceptsNull) {
         copy = String.format("(%s == null ? null : %s)", property, copy);
       }
-
       return copy;
     }
   }
@@ -450,7 +477,8 @@
     if (!sameTypeParameters(autoValueClass, builderTypeElement)) {
       errorReporter.reportError(
           builderTypeElement,
-          "Type parameters of %s must have same names and bounds as type parameters of %s",
+          "[AutoValueTypeParamMismatch] Type parameters of %s must have same names and bounds as"
+              + " type parameters of %s",
           builderTypeElement,
           autoValueClass);
       return Optional.empty();
diff --git a/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java b/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java
index dafb582..2565cdd 100644
--- a/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java
+++ b/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java
@@ -198,7 +198,8 @@
     if (barBuilderTypeMirror.getKind() != TypeKind.DECLARED) {
       errorReporter.reportError(
           method,
-          "Method looks like a property builder, but its return type is not a class or interface");
+          "[AutoValueOddBuilderMethod] Method looks like a property builder, but its return type"
+              + " is not a class or interface");
       return Optional.empty();
     }
     DeclaredType barBuilderDeclaredType = MoreTypes.asDeclared(barBuilderTypeMirror);
@@ -210,15 +211,15 @@
     if (barTypeMirror.getKind() != TypeKind.DECLARED) {
       errorReporter.reportError(
           method,
-          "Method looks like a property builder, but the type of property %s is not a class or"
-              + " interface",
+          "[AutoValueBadBuilderMethod] Method looks like a property builder, but the type of"
+              + " property %s is not a class or interface",
           property);
       return Optional.empty();
     }
     if (isNullable(barGetter)) {
       errorReporter.reportError(
           barGetter,
-          "Property %s has a property builder so it cannot be @Nullable",
+          "[AutoValueNullBuilder] Property %s has a property builder so it cannot be @Nullable",
           property);
     }
     TypeElement barTypeElement = MoreTypes.asTypeElement(barTypeMirror);
@@ -229,8 +230,8 @@
     if (build == null || build.getModifiers().contains(Modifier.STATIC)) {
       errorReporter.reportError(
           method,
-          "Method looks like a property builder, but it returns %s which does not have a"
-              + " non-static build() method",
+          "[AutoValueBuilderNotBuildable] Method looks like a property builder, but it returns %s"
+              + " which does not have a non-static build() method",
           barBuilderTypeElement);
       return Optional.empty();
     }
@@ -241,7 +242,8 @@
     if (!MoreTypes.equivalence().equivalent(barTypeMirror, buildType)) {
       errorReporter.reportError(
           method,
-          "Property builder for %s has type %s whose build() method returns %s instead of %s",
+          "[AutoValueBuilderWrongType] Property builder for %s has type %s whose build() method"
+              + " returns %s instead of %s",
           property,
           barBuilderTypeElement,
           buildType,
@@ -254,9 +256,9 @@
     if (!maybeBuilderMaker.isPresent()) {
       errorReporter.reportError(
           method,
-          "Method looks like a property builder, but its type %s does not have a public"
-              + " constructor and %s does not have a static builder() or newBuilder() method that"
-              + " returns %s",
+          "[AutoValueCantMakePropertyBuilder] Method looks like a property builder, but its type"
+              + " %s does not have a public constructor and %s does not have a static builder() or"
+              + " newBuilder() method that returns %s",
           barBuilderTypeElement,
           barTypeElement,
           barBuilderTypeElement);
diff --git a/value/src/main/java/com/google/auto/value/processor/autoannotation.vm b/value/src/main/java/com/google/auto/value/processor/autoannotation.vm
index 1d14d3f..a953eda 100644
--- a/value/src/main/java/com/google/auto/value/processor/autoannotation.vm
+++ b/value/src/main/java/com/google/auto/value/processor/autoannotation.vm
@@ -108,7 +108,7 @@
 
 ## annotationType method (defined by the Annotation interface)
 
-  @Override
+  @`java.lang.Override`
   public Class<? extends $annotationName> annotationType() {
     return ${annotationName}.class;
   }
@@ -117,7 +117,7 @@
 
 #foreach ($m in $members)
 
-  @Override
+  @`java.lang.Override`
   public ${m.type} ${m}() {
 
   #if ($m.kind == "ARRAY")
@@ -162,7 +162,7 @@
   #end
 #end
 
-  @Override
+  @`java.lang.Override`
   public String toString() {
     StringBuilder sb = new StringBuilder("@$annotationFullName(");
 
@@ -211,7 +211,7 @@
   #end
 #end
 
-  @Override
+  @`java.lang.Override`
   public boolean equals(Object o) {
     if (o == this) {
       return true;
@@ -269,7 +269,7 @@
 ## example.) We precompute the invariable part, as an optimization but also in order to avoid
 ## falling afoul of constant-overflow checks in the compiler.
 
-  @Override
+  @`java.lang.Override`
   public int hashCode() {
     return
     ## If the invariable part is 0, we avoid outputting `return 0 + ...` just because it generates
diff --git a/value/src/main/java/com/google/auto/value/processor/autooneof.vm b/value/src/main/java/com/google/auto/value/processor/autooneof.vm
index 04cc194..0249d9e 100644
--- a/value/src/main/java/com/google/auto/value/processor/autooneof.vm
+++ b/value/src/main/java/com/google/auto/value/processor/autooneof.vm
@@ -99,7 +99,7 @@
 
 #foreach ($p in $props)
 
-    @Override
+    @`java.lang.Override`
     $p.access $p.type ${p.getter}() {
       throw new UnsupportedOperationException(${kindGetter}().toString());
     }
@@ -128,7 +128,7 @@
 
     private Impl_$p() {}
 
-    @Override
+    @`java.lang.Override`
     public void ${p.getter}() {}
 
     #if ($serializable)
@@ -141,7 +141,7 @@
 
     #if ($toString)
 
-    @Override
+    @`java.lang.Override`
     public String toString() {
       return "${simpleClassName}{$p.name}";
     }
@@ -154,7 +154,7 @@
 
     #if ($equals)
 
-    @Override
+    @`java.lang.Override`
     public boolean equals($equalsParameterType x) {
       return x == this;
     }
@@ -163,7 +163,7 @@
 
     #if ($hashCode)
 
-    @Override
+    @`java.lang.Override`
     public int hashCode() {
       return System.identityHashCode(this);
     }
@@ -178,14 +178,14 @@
       this.$p = $p;
     }
 
-    @Override
+    @`java.lang.Override`
     public $p.type ${p.getter}() {
       return $p;
     }
 
     #if ($toString)
 
-    @Override
+    @`java.lang.Override`
     public String toString() {
       return "${simpleClassName}{$p.name=" ##
           + #if ($p.kind == "ARRAY") `java.util.Arrays`.toString(this.$p) #else this.$p #end
@@ -196,7 +196,7 @@
 
     #if ($equals)
 
-    @Override
+    @`java.lang.Override`
     public boolean equals($equalsParameterType x) {
       if (x instanceof $origClass) {
         $origClass$wildcardTypes that = ($origClass$wildcardTypes) x;
@@ -211,7 +211,7 @@
 
     #if ($hashCode)
 
-    @Override
+    @`java.lang.Override`
     public int hashCode() {
       return #hashCodeExpression($p);
     }
@@ -220,7 +220,7 @@
 
   #end
 
-    @Override
+    @`java.lang.Override`
     public $kindType ${kindGetter}() {
       return ${kindType}.$propertyToKind[$p.name];
     }
diff --git a/value/src/main/java/com/google/auto/value/processor/autovalue.vm b/value/src/main/java/com/google/auto/value/processor/autovalue.vm
index 80ef79a..6f50e93 100644
--- a/value/src/main/java/com/google/auto/value/processor/autovalue.vm
+++ b/value/src/main/java/com/google/auto/value/processor/autovalue.vm
@@ -101,7 +101,7 @@
   ${a}##
   #end
 
-  @Override
+  @`java.lang.Override`
   ${p.access}${p.type} ${p.getter}() {
     return $p;
   }
@@ -110,7 +110,7 @@
 
 #if ($toString)
 
-  @Override
+  @`java.lang.Override`
   public `java.lang.String` toString() {
     return "#if ($identifiers)$simpleClassName#end{"
 
@@ -129,7 +129,7 @@
 
 #if ($equals)
 
-  @Override
+  @`java.lang.Override`
   public boolean equals($equalsParameterType o) {
     if (o == this) {
       return true;
@@ -162,7 +162,7 @@
 
 #if ($hashCode)
 
-  @Override
+  @`java.lang.Override`
   public int hashCode() {
     int h$ = 1;
 
@@ -185,7 +185,7 @@
 
   #foreach ($m in $toBuilderMethods)
 
-  @Override
+  @`java.lang.Override`
   ${m.access}${builderTypeName}${builderActualTypes} ${m.name}() {
     return new Builder${builderActualTypes}(this);
   }
@@ -252,7 +252,7 @@
 
     #foreach ($setter in $builderSetters[$p.name])
 
-    @Override
+    @`java.lang.Override`
     ${setter.access}${builderTypeName}${builderActualTypes} ##
         ${setter.name}(${setter.nullableAnnotation}$setter.parameterType $p) {
 
@@ -290,7 +290,7 @@
 
     #if ($propertyBuilder)
 
-    @Override
+    @`java.lang.Override`
     ${propertyBuilder.access}$propertyBuilder.builderType ${p.name}Builder() {
       if (${propertyBuilder.name} == null) {
 
@@ -337,7 +337,7 @@
 
     #if ($builderGetters[$p.name])
 
-    @Override
+    @`java.lang.Override`
     ${p.nullableAnnotation}${builderGetters[$p.name].access}$builderGetters[$p.name].type ${p.getter}() {
       #if ($builderGetters[$p.name].optional)
 
@@ -377,7 +377,7 @@
     #end
   #end
 
-    @Override
+    @`java.lang.Override`
     ${buildMethod.get().access}${origClass}${actualTypes} ${buildMethod.get().name}() {
 
   #foreach ($p in $props)
diff --git a/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm b/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm
index 0450036..1265aae 100644
--- a/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm
+++ b/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm
@@ -93,26 +93,26 @@
   @SuppressWarnings("unused")
   private int dummy_$classHashString;
 
-  @Override
+  @`java.lang.Override`
   public void deserializeInstance(
       `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader,
       $subclass$actualTypes instance) {
     deserialize(streamReader, instance);
   }
 
-  @Override
+  @`java.lang.Override`
   public boolean hasCustomInstantiateInstance() {
     return true;
   }
 
-  @Override
+  @`java.lang.Override`
   public $subclass$actualTypes instantiateInstance(
       `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader)
       throws `com.google.gwt.user.client.rpc.SerializationException` {
     return instantiate(streamReader);
   }
 
-  @Override
+  @`java.lang.Override`
   public void serializeInstance(
       `com.google.gwt.user.client.rpc.SerializationStreamWriter` streamWriter,
       $subclass$actualTypes instance)
diff --git a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java
index 2d4aa7c..e46c9f5 100644
--- a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java
@@ -3189,6 +3189,12 @@
             "package foo.bar;",
             "",
             "public class Thread {}");
+    JavaFileObject override =
+        JavaFileObjects.forSourceLines(
+            "foo.bar.Override", //
+            "package foo.bar;",
+            "",
+            "public class Override {}");
     JavaFileObject test =
         JavaFileObjects.forSourceLines(
             "foo.bar.Test",
@@ -3215,7 +3221,7 @@
         javac()
             .withProcessors(new AutoValueProcessor())
             .withOptions("-Xlint:-processing", "-implicit:none")
-            .compile(object, string, integer, thread, test);
+            .compile(object, string, integer, thread, override, test);
     assertThat(compilation).succeededWithoutWarnings();
   }
 
diff --git a/value/userguide/howto.md b/value/userguide/howto.md
index 1dbc561..6f9142c 100644
--- a/value/userguide/howto.md
+++ b/value/userguide/howto.md
@@ -470,7 +470,7 @@
   }
 
   @AutoValue
-  abstract class StringValue extends StringOrInteger {
+  abstract static class StringValue extends StringOrInteger {
     abstract String string();
 
     @Override
@@ -480,7 +480,7 @@
   }
 
   @AutoValue
-  abstract class IntegerValue extends StringOrInteger {
+  abstract static class IntegerValue extends StringOrInteger {
     abstract int integer();
 
     @Override
diff --git a/value/userguide/index.md b/value/userguide/index.md
index 8cbe75f..2e05d54 100644
--- a/value/userguide/index.md
+++ b/value/userguide/index.md
@@ -92,6 +92,23 @@
 </dependencies>
 ```
 
+Some AutoValue annotations have CLASS retention. This is mostly of use for
+compile-time tools, such as AutoValue itself. If you are creating
+a library, the end user rarely needs to know the original AutoValue annotations.
+In that case, you can set the scope to `provided`, so that the user of your
+library does not have `auto-value-annotations` as a transitive dependency.
+
+```xml
+<dependencies>
+  <dependency>
+    <groupId>com.google.auto.value</groupId>
+    <artifactId>auto-value-annotations</artifactId>
+    <version>${auto-value.version}</version>
+    <scope>provided</scope>
+  </dependency>
+</dependencies>
+```
+
 For `auto-value` (the annotation processor), you can write this:
 
 ```xml