Defer processing of an @AutoValue class with missing types, and attempt to handle it in a later annotation processing round, when those types may have come into existence.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=79467609
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 cf39ae7..020c598 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
@@ -24,7 +24,6 @@
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -117,25 +116,48 @@
     throw new AbortProcessingException();
   }
 
+  /**
+   * Qualified names of {@code @AutoValue} classes that we attempted to process but had to abandon
+   * because we needed other types that they referenced and those other types were missing.
+   */
+  private final List<String> deferredTypeNames = new ArrayList<String>();
+
   @Override
   public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-    if (annotations.size() == 1
-        && Iterables.getOnlyElement(annotations).getQualifiedName().toString().equals(
-            AutoValue.class.getName())) {
-      process(roundEnv);
+    List<TypeElement> deferredTypes = new ArrayList<TypeElement>();
+    for (String deferred : deferredTypeNames) {
+      deferredTypes.add(processingEnv.getElementUtils().getTypeElement(deferred));
     }
-    return false;  // never claim annotation, because who knows what other processors want?
-  }
-
-  private void process(RoundEnvironment roundEnv) {
+    if (roundEnv.processingOver()) {
+      // This means that the previous round didn't generate any new sources, so we can't have found
+      // any new instances of @AutoValue; and we can't have any new types that are the reason a type
+      // was in deferredTypes.
+      for (TypeElement type : deferredTypes) {
+        reportError("Did not generate @AutoValue class for " + type.getQualifiedName()
+            + " because it references undefined types", type);
+      }
+      return false;
+    }
     Collection<? extends Element> annotatedElements =
         roundEnv.getElementsAnnotatedWith(AutoValue.class);
-    Collection<? extends TypeElement> types = ElementFilter.typesIn(annotatedElements);
+    List<TypeElement> types = new ImmutableList.Builder<TypeElement>()
+        .addAll(deferredTypes)
+        .addAll(ElementFilter.typesIn(annotatedElements))
+        .build();
+    deferredTypeNames.clear();
     for (TypeElement type : types) {
       try {
         processType(type);
       } catch (AbortProcessingException e) {
-        // We abandoned this type, but continue with the next.
+        // We abandoned this type; continue with the next.
+      } catch (MissingTypeException e) {
+        // We abandoned this type, but only because we needed another type that it references and
+        // that other type was missing. It is possible that the missing type will be generated by
+        // further annotation processing, so we will try again on the next round (perhaps failing
+        // again and adding it back to the list). We save the name of the @AutoValue type rather
+        // than its TypeElement because it is not guaranteed that it will be represented by
+        // the same TypeElement on the next round.
+        deferredTypeNames.add(type.getQualifiedName().toString());
       } catch (RuntimeException e) {
         // Don't propagate this exception, which will confusingly crash the compiler.
         // Instead, report a compiler error with the stack trace.
@@ -143,6 +165,7 @@
         reportError("@AutoValue processor threw an exception: " + trace, type);
       }
     }
+    return false;  // never claim annotation, because who knows what other processors want?
   }
 
   private String generatedClassName(TypeElement type, String prefix) {
diff --git a/value/src/main/java/com/google/auto/value/processor/MissingTypeException.java b/value/src/main/java/com/google/auto/value/processor/MissingTypeException.java
new file mode 100644
index 0000000..1411d1c
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/MissingTypeException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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.value.processor;
+
+/**
+ * Exception thrown in the specific case where processing of a class was abandoned because it
+ * required types that the class references to be present and they were not. This case is handled
+ * specially because it is possible that those types might be generated later during annotation
+ * processing, so we should reattempt the processing of the class in a later annotation processing
+ * round.
+ *
+ * @author Éamonn McManus
+ */
+@SuppressWarnings("serial")
+class MissingTypeException extends RuntimeException {
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java b/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java
index 6b24627..0b75eb6 100644
--- a/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java
+++ b/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java
@@ -80,7 +80,7 @@
    *     because nested classes in that class or one of its ancestors are in scope in the generated
    *     subclass, so a reference to another class with the same name as one of them is ambiguous.
    *
-   * @throws AbortProcessingException if one of the input types contains an error (typically,
+   * @throws MissingTypeException if one of the input types contains an error (typically,
    *     is undefined). This may be something like {@code UndefinedClass}, or something more subtle
    *     like {@code Set<UndefinedClass<?>>}.
    */
@@ -357,7 +357,7 @@
     }
 
     @Override public Void visitError(ErrorType t, Void p) {
-      throw new AbortProcessingException();
+      throw new MissingTypeException();
     }
   }
 
@@ -390,7 +390,7 @@
     Set<String> simpleNames = new HashSet<String>();
     for (TypeMirror type : types) {
       if (type.getKind() == TypeKind.ERROR) {
-        throw new AbortProcessingException();
+        throw new MissingTypeException();
       }
       String simpleName = typeUtils.asElement(type).getSimpleName().toString();
       if (!simpleNames.add(simpleName)) {
diff --git a/value/src/test/java/com/google/auto/value/processor/CompilationTest.java b/value/src/test/java/com/google/auto/value/processor/CompilationTest.java
index ffcda31..a2eff0e 100644
--- a/value/src/test/java/com/google/auto/value/processor/CompilationTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/CompilationTest.java
@@ -16,20 +16,32 @@
 package com.google.auto.value.processor;
 
 import static com.google.common.truth.Truth.assert_;
+import static com.google.common.truth.Truth.assertAbout;
 import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.testing.compile.JavaFileObjects;
 
 import junit.framework.TestCase;
 
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.Set;
 
+import javax.annotation.processing.AbstractProcessor;
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
 import javax.tools.JavaFileObject;
 
 /**
@@ -560,4 +572,83 @@
         .withErrorContaining(exception.toString())
         .in(javaFileObject).onLine(6);
   }
+
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface Foo {}
+
+  /* Processor that generates an empty class BarFoo every time it sees a class Bar annotated with
+   * @Foo.
+   */
+  public static class FooProcessor extends AbstractProcessor {
+    @Override
+    public Set<String> getSupportedAnnotationTypes() {
+      return ImmutableSet.of(Foo.class.getCanonicalName());
+    }
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+      return SourceVersion.latestSupported();
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+      Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Foo.class);
+      for (TypeElement type : ElementFilter.typesIn(elements)) {
+        try {
+          generateFoo(type);
+        } catch (IOException e) {
+          throw new AssertionError(e);
+        }
+      }
+      return false;
+    }
+
+    private void generateFoo(TypeElement type) throws IOException {
+      String pkg = TypeSimplifier.packageNameOf(type);
+      String className = type.getSimpleName().toString();
+      String generatedClassName = className + "Foo";
+      JavaFileObject source =
+          processingEnv.getFiler().createSourceFile(pkg + "." + generatedClassName, type);
+      PrintWriter writer = new PrintWriter(source.openWriter());
+      writer.println("package " + pkg + ";");
+      writer.println("public class " + generatedClassName + " {}");
+      writer.close();
+    }
+  }
+
+  public void testReferencingGeneratedClass() {
+    // Test that ensures that a type that does not exist can be the type of an @AutoValue property
+    // as long as it later does come into existence. The BarFoo type referenced here does not exist
+    // when the AutoValueProcessor runs on the first round, but the FooProcessor then generates it.
+    // That generation provokes a further round of annotation processing and AutoValueProcessor
+    // should succeed then.
+    JavaFileObject bazFileObject = JavaFileObjects.forSourceLines(
+        "foo.bar.Baz",
+        "package foo.bar;",
+        "",
+        "import com.google.auto.value.AutoValue;",
+        "",
+        "@AutoValue",
+        "public abstract class Baz {",
+        "  public abstract BarFoo barFoo();",
+        "",
+        "  public static Baz create(BarFoo barFoo) {",
+        "    return new AutoValue_Baz(barFoo);",
+        "  }",
+        "}");
+    JavaFileObject barFileObject = JavaFileObjects.forSourceLines(
+        "foo.bar.Bar",
+        "package foo.bar;",
+        "",
+        "import com.google.auto.value.AutoValue;",
+        "",
+        "@" + Foo.class.getCanonicalName(),
+        "public abstract class Bar {",
+        "  public abstract BarFoo barFoo();",
+        "}");
+    assertAbout(javaSources())
+        .that(ImmutableList.of(bazFileObject, barFileObject))
+        .processedWith(new AutoValueProcessor(), new FooProcessor())
+        .compilesWithoutError();
+  }
 }
diff --git a/value/src/test/java/com/google/auto/value/processor/TypeSimplifierTest.java b/value/src/test/java/com/google/auto/value/processor/TypeSimplifierTest.java
index 0255e39..062b941 100644
--- a/value/src/test/java/com/google/auto/value/processor/TypeSimplifierTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/TypeSimplifierTest.java
@@ -659,7 +659,7 @@
         try {
           new TypeSimplifier(typeUtil, "foo.bar", ImmutableSet.of(typeWithError), javaLangObject);
           fail("Expected exception for type: " + typeWithError);
-        } catch (AbortProcessingException expected) {
+        } catch (MissingTypeException expected) {
         }
       }
     }