Small compilation error reporting improvements.

Changes most JDT compilation error reporting to include the causal chain
of failed types (some but not all places were already doing this).

Saves compilation error information in libraries to facilitate causal
chain reporting even across libraries.

Turns off required strictness in library compilation since it was only
turned on to guard against previously undiagnosable cross-module
compilation errors.

Change-Id: I34ce8f327734483bec1886b7d9611012a98a1d8f
Review-Link: https://gwt-review.googlesource.com/#/c/7865/
diff --git a/dev/core/src/com/google/gwt/dev/BuildTarget.java b/dev/core/src/com/google/gwt/dev/BuildTarget.java
index 5653fec..e698b21 100644
--- a/dev/core/src/com/google/gwt/dev/BuildTarget.java
+++ b/dev/core/src/com/google/gwt/dev/BuildTarget.java
@@ -58,7 +58,7 @@
 
   @VisibleForTesting
   public static String formatCompilingModuleMessage(String canonicalModuleName) {
-    return "Compiling module " + canonicalModuleName;
+    return "\n" + "Compiling module " + canonicalModuleName;
   }
 
   @VisibleForTesting
diff --git a/dev/core/src/com/google/gwt/dev/CompilerContext.java b/dev/core/src/com/google/gwt/dev/CompilerContext.java
index 98f4046..5155d9d 100644
--- a/dev/core/src/com/google/gwt/dev/CompilerContext.java
+++ b/dev/core/src/com/google/gwt/dev/CompilerContext.java
@@ -21,6 +21,9 @@
 import com.google.gwt.dev.cfg.LibraryWriter;
 import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.cfg.NullLibraryWriter;
+import com.google.gwt.dev.javac.CombinedCompilationErrorsIndex;
+import com.google.gwt.dev.javac.CompilationErrorsIndex;
+import com.google.gwt.dev.javac.CompilationErrorsIndexImpl;
 import com.google.gwt.dev.javac.MemoryUnitCache;
 import com.google.gwt.dev.javac.UnitCache;
 import com.google.gwt.dev.resource.ResourceOracle;
@@ -43,8 +46,11 @@
 
     private ResourceOracle buildResourceOracle;
     private boolean compileMonolithic = true;
+    private CompilationErrorsIndex globalCompilationErrorsIndex;
+    private CompilationErrorsIndex libraryCompilationErrorsIndex;
     private LibraryGroup libraryGroup = new ImmutableLibraryGroup();
     private LibraryWriter libraryWriter = new NullLibraryWriter();
+    private CompilationErrorsIndex localCompilationErrorsIndex;
     private ModuleDef module;
     private PrecompileTaskOptions options = new PrecompileTaskOptionsImpl();
     private ResourceOracle publicResourceOracle;
@@ -53,6 +59,7 @@
 
     public CompilerContext build() {
       initializeResourceOracles();
+      initializeCompilationErrorIndexes();
 
       CompilerContext compilerContext = new CompilerContext();
       compilerContext.buildResourceOracle = buildResourceOracle;
@@ -63,6 +70,8 @@
       compilerContext.options = options;
       compilerContext.publicResourceOracle = publicResourceOracle;
       compilerContext.sourceResourceOracle = sourceResourceOracle;
+      compilerContext.localCompilationErrorsIndex = localCompilationErrorsIndex;
+      compilerContext.globalCompilationErrorsIndex = globalCompilationErrorsIndex;
       compilerContext.unitCache = unitCache;
       return compilerContext;
     }
@@ -106,6 +115,14 @@
       return this;
     }
 
+    private void initializeCompilationErrorIndexes() {
+      localCompilationErrorsIndex = new CompilationErrorsIndexImpl();
+      libraryCompilationErrorsIndex = libraryGroup != null
+          ? libraryGroup.getCompilationErrorsIndex() : new CompilationErrorsIndexImpl();
+      globalCompilationErrorsIndex = new CombinedCompilationErrorsIndex(localCompilationErrorsIndex,
+          libraryCompilationErrorsIndex);
+    }
+
     /**
      * Initialize source, build, and public resource oracles using the most complete currently
      * available combination of moduleDef and libraryGroup.<br />
@@ -154,20 +171,31 @@
   private boolean compileMonolithic = true;
   private LibraryGroup libraryGroup = new ImmutableLibraryGroup();
   private LibraryWriter libraryWriter = new NullLibraryWriter();
+  private CompilationErrorsIndex localCompilationErrorsIndex = new CompilationErrorsIndexImpl();
+  private CompilationErrorsIndex globalCompilationErrorsIndex = new CombinedCompilationErrorsIndex(
+      localCompilationErrorsIndex, new CompilationErrorsIndexImpl());
   private ModuleDef module;
-  private TinyCompileSummary tinyCompileSummary = new TinyCompileSummary();
-
   // TODO(stalcup): split this into module parsing, precompilation, compilation, and linking option
   // sets.
   private PrecompileTaskOptions options = new PrecompileTaskOptionsImpl();
+
   private ResourceOracle publicResourceOracle;
   private ResourceOracle sourceResourceOracle;
+  private TinyCompileSummary tinyCompileSummary = new TinyCompileSummary();
   private UnitCache unitCache = new MemoryUnitCache();
 
   public ResourceOracle getBuildResourceOracle() {
     return buildResourceOracle;
   }
 
+  /**
+   * Returns the immutable compilation errors index that provides a combined view of compilation
+   * errors for both the current compile as well as previously compiled libraries.
+   */
+  public CompilationErrorsIndex getGlobalCompilationErrorsIndex() {
+    return globalCompilationErrorsIndex;
+  }
+
   public LibraryGroup getLibraryGroup() {
     return libraryGroup;
   }
@@ -176,6 +204,13 @@
     return libraryWriter;
   }
 
+  /**
+   * Returns the mutable index of compilation errors for the current compile.
+   */
+  public CompilationErrorsIndex getLocalCompilationErrorsIndex() {
+    return localCompilationErrorsIndex;
+  }
+
   public ModuleDef getModule() {
     return module;
   }
@@ -216,6 +251,10 @@
     return sourceResourceOracle;
   }
 
+  public TinyCompileSummary getTinyCompileSummary() {
+    return tinyCompileSummary;
+  }
+
   public UnitCache getUnitCache() {
     return unitCache;
   }
@@ -223,8 +262,4 @@
   public boolean shouldCompileMonolithic() {
     return compileMonolithic;
   }
-
-  public TinyCompileSummary getTinyCompileSummary() {
-    return tinyCompileSummary;
-  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/LibraryCompiler.java b/dev/core/src/com/google/gwt/dev/LibraryCompiler.java
index 5d2ee62..1b5b607 100644
--- a/dev/core/src/com/google/gwt/dev/LibraryCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/LibraryCompiler.java
@@ -199,6 +199,7 @@
 
       generatedArtifacts = precompilation.getGeneratedArtifacts();
       libraryWriter.addGeneratedArtifacts(generatedArtifacts);
+      libraryWriter.setCompilationErrorsIndex(compilerContext.getLocalCompilationErrorsIndex());
     } finally {
       // Even if a compile problem occurs, close the library cleanly so that it can be examined.
       libraryWriter.write();
@@ -301,8 +302,6 @@
   private void normalizeOptions(TreeLogger logger) throws UnableToCompleteException {
     Preconditions.checkArgument(compilerOptions.getModuleNames().size() == 1);
 
-    // Fail early on errors to avoid confusion later.
-    compilerOptions.setStrict(true);
     // Current optimization passes are not safe with only partial data.
     compilerOptions.setOptimizationLevel(OptionOptimize.OPTIMIZE_LEVEL_DRAFT);
     // Protects against rampant overlapping source inclusion.
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
index 559aa68..e9777a5 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
@@ -76,8 +76,8 @@
     String testType = query.getTestType();
     JClassType fromType = typeOracle.findType(testType);
     if (fromType == null) {
-      CompilationProblemReporter.logMissingTypeErrorWithHints(logger, testType,
-          query.getCompilationState());
+      CompilationProblemReporter.logErrorTrace(logger, TreeLogger.ERROR,
+          query.getCompilationState().getCompilerContext(), testType, true);
       throw new UnableToCompleteException();
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Libraries.java b/dev/core/src/com/google/gwt/dev/cfg/Libraries.java
index 64a8e96..7ab8582 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Libraries.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Libraries.java
@@ -43,16 +43,17 @@
   }
 
   public static final String BUILD_RESOURCE_PATHS_ENTRY_NAME = "buildResourcePaths.txt";
+  public static final String COMPILATION_ERRORS_INDEX_ENTRY_NAME = "compilationErrorsIndex.ser";
   public static final String DEPENDENCY_LIBRARY_NAMES_ENTRY_NAME = "dependencyLibraryNames.txt";
   public static final String DIRECTORY_BUILD_RESOURCES = "buildResources/";
   public static final String DIRECTORY_BYTECODE = "bytecode/";
   public static final String DIRECTORY_COMPILATION_UNITS = "compilationUnits/";
   public static final String DIRECTORY_GENERATED_ARTIFACTS = "generatedArtifacts/";
   public static final String DIRECTORY_PUBLIC_RESOURCES = "publicResources/";
+  public static final char ENCODE_PREFIX = '%';
   public static final String EXTENSION_CLASS_FILE = ".class";
   public static final String EXTENSION_COMPILATION_UNITS = ".compilationUnit";
   public static final String GENERATED_ARTIFACT_NAMES_ENTRY_NAME = "generatedArtifactNames.txt";
-  public static final char ENCODE_PREFIX = '%';
   public static final char KEY_VALUE_SEPARATOR = ':';
   public static final String LIBRARY_NAME_ENTRY_NAME = "libraryName.txt";
   public static final char LINE_SEPARATOR = '\n';
@@ -61,10 +62,10 @@
   public static final String NESTED_SOURCE_NAMES_BY_ENCLOSING_NAME_ENTRY_NAME =
       "nestedSourceNamesByEnclosingName.txt";
   public static final String PERMUTATION_RESULT_ENTRY_NAME = "permutationResult.ser";
-  public static final String PUBLIC_RESOURCE_PATHS_ENTRY_NAME = "publicResourcePaths.txt";
-  public static final String REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME = "reboundTypeSourceNames.txt";
   public static final String PROCESSED_REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME =
       "processedReboundTypeSourceNames.txt";
+  public static final String PUBLIC_RESOURCE_PATHS_ENTRY_NAME = "publicResourcePaths.txt";
+  public static final String REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME = "reboundTypeSourceNames.txt";
   public static final String REGULAR_CLASS_FILE_PATHS_ENTRY_NAME = "regularClassFilePaths.txt";
   public static final String REGULAR_COMPILATION_UNIT_TYPE_SOURCE_NAMES_ENTRY_NAME =
       "regularCompilationUnitTypeSourceNames.txt";
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Library.java b/dev/core/src/com/google/gwt/dev/cfg/Library.java
index 9fdb01b..4f4a991 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Library.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Library.java
@@ -14,6 +14,7 @@
 package com.google.gwt.dev.cfg;
 
 import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.javac.CompilationErrorsIndexImpl;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.jjs.PermutationResult;
 import com.google.gwt.dev.resource.Resource;
@@ -112,6 +113,11 @@
   Multimap<String, String> getProcessedReboundTypeSourceNamesByGenerator();
 
   /**
+   * Returns a source of compilation error information to support detailed logging.
+   */
+  CompilationErrorsIndexImpl getCompilationErrorsIndex();
+
+  /**
    * Returns a resource handle or null for the provided path.
    */
   Resource getPublicResourceByPath(String path);
diff --git a/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java b/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java
index 418e44c..04ab5d2 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/LibraryGroup.java
@@ -17,6 +17,8 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.linker.ArtifactSet;
 import com.google.gwt.dev.cfg.Libraries.IncompatibleLibraryVersionException;
+import com.google.gwt.dev.javac.CompilationErrorsIndex;
+import com.google.gwt.dev.javac.CompilationErrorsIndexImpl;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.PermutationResult;
@@ -126,6 +128,8 @@
     return fromLibraries(zipLibraries, true);
   }
 
+  private SetMultimap<String, String> compilationErrorsByTypeSourceName;
+  private CompilationErrorsIndexImpl compilationErrorsIndex;
   private Set<String> compilationUnitTypeSourceNames;
   private ArtifactSet generatedArtifacts;
   private List<Library> libraries = Lists.newArrayList();
@@ -139,6 +143,7 @@
   private SetMultimap<String, String> processedReboundTypeSourceNamesByGenerator =
       HashMultimap.create();
   private Set<String> reboundTypeSourceNames;
+  private SetMultimap<String, String> referencesByTypeSourceName;
   private List<Library> rootLibraries;
   private Set<String> superSourceCompilationUnitTypeSourceNames;
 
@@ -201,6 +206,16 @@
     return library.getClassFileStream(classFilePath);
   }
 
+  public CompilationErrorsIndex getCompilationErrorsIndex() {
+    if (compilationErrorsIndex == null) {
+      compilationErrorsIndex = new CompilationErrorsIndexImpl();
+      for (Library library : libraries) {
+        compilationErrorsIndex.merge(library.getCompilationErrorsIndex());
+      }
+    }
+    return compilationErrorsIndex;
+  }
+
   /**
    * Returns the compilation unit for the given compilation unit type name if present or null.
    */
@@ -355,6 +370,19 @@
         Maps.filterKeys(librariesByName, Predicates.in(libraryNames)).values());
   }
 
+  /**
+   * Throws an exception if the referenced compilation unit (which is being provided by the
+   * referenced new library) is already being provided by some older library.
+   */
+  private void assertUniquelyProvided(Library newLibrary, String compilationUnitTypeSourceName) {
+    if (librariesByCompilationUnitTypeSourceName.containsKey(compilationUnitTypeSourceName)) {
+      Library oldLibrary =
+          librariesByCompilationUnitTypeSourceName.get(compilationUnitTypeSourceName);
+      throw new CollidingCompilationUnitException(formatDuplicateCompilationUnitMessage(
+          compilationUnitTypeSourceName, oldLibrary.getLibraryName(), newLibrary.getLibraryName()));
+    }
+  }
+
   private void buildLibraryIndexes(boolean verifyLibraryReferences) {
     // Start processing with a consistent library order to ensure consistently ordered output.
     Collections.sort(libraries, LIBRARY_NAME_COMPARATOR);
@@ -417,19 +445,6 @@
     libraries = librariesInLinkOrder;
   }
 
-  /**
-   * Throws an exception if the referenced compilation unit (which is being provided by the
-   * referenced new library) is already being provided by some older library.
-   */
-  private void assertUniquelyProvided(Library newLibrary, String compilationUnitTypeSourceName) {
-    if (librariesByCompilationUnitTypeSourceName.containsKey(compilationUnitTypeSourceName)) {
-      Library oldLibrary =
-          librariesByCompilationUnitTypeSourceName.get(compilationUnitTypeSourceName);
-      throw new CollidingCompilationUnitException(formatDuplicateCompilationUnitMessage(
-          compilationUnitTypeSourceName, oldLibrary.getLibraryName(), newLibrary.getLibraryName()));
-    }
-  }
-
   private Map<String, Library> getLibrariesByBuildResourcePath() {
     if (librariesByBuildResourcePath == null) {
       librariesByBuildResourcePath = Maps.newLinkedHashMap();
diff --git a/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java b/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java
index 119a32b..bc2e082 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/LibraryWriter.java
@@ -14,6 +14,7 @@
 package com.google.gwt.dev.cfg;
 
 import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.javac.CompilationErrorsIndex;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.jjs.PermutationResult;
 import com.google.gwt.dev.resource.Resource;
@@ -92,6 +93,11 @@
   void markReboundTypesProcessed(Set<String> reboundTypeSourceNames);
 
   /**
+   * Records a source of compilation error information to support detailed logging.
+   */
+  void setCompilationErrorsIndex(CompilationErrorsIndex compilationErrorsIndex);
+
+  /**
    * Records the library name.<br />
    *
    * Library names are the way that libraries reference one another as dependencies and should be
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index 8f7d743..cec5251 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -967,8 +967,8 @@
     boolean seedTypesMissing = false;
     TypeOracle typeOracle = compilationState.getTypeOracle();
     if (typeOracle.findType("java.lang.Object") == null) {
-      CompilationProblemReporter.logMissingTypeErrorWithHints(logger, "java.lang.Object",
-          compilationState);
+      CompilationProblemReporter.logErrorTrace(logger, TreeLogger.ERROR,
+          compilationState.getCompilerContext(), "java.lang.Object", true);
       seedTypesMissing = true;
     } else {
       TreeLogger branch = logger.branch(TreeLogger.TRACE, "Finding entry point classes", null);
@@ -976,8 +976,8 @@
       for (int i = 0; i < typeNames.length; i++) {
         String typeName = typeNames[i];
         if (typeOracle.findType(typeName) == null) {
-          CompilationProblemReporter.logMissingTypeErrorWithHints(branch, typeName,
-              compilationState);
+          CompilationProblemReporter.logErrorTrace(branch, TreeLogger.ERROR,
+              compilationState.getCompilerContext(), typeName, true);
           seedTypesMissing = true;
         }
       }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java b/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java
index 01a0f41..591a3db 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/NullLibraryWriter.java
@@ -14,6 +14,7 @@
 package com.google.gwt.dev.cfg;
 
 import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.javac.CompilationErrorsIndex;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.jjs.PermutationResult;
 import com.google.gwt.dev.resource.Resource;
@@ -79,6 +80,10 @@
   }
 
   @Override
+  public void setCompilationErrorsIndex(CompilationErrorsIndex compilationErrorsIndex) {
+  }
+
+  @Override
   public void setLibraryName(String libraryName) {
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java b/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java
index 7461c16..ef6d5a4 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ZipLibrary.java
@@ -16,6 +16,7 @@
 import com.google.gwt.core.ext.linker.ArtifactSet;
 import com.google.gwt.core.ext.linker.impl.StandardGeneratedResource;
 import com.google.gwt.dev.cfg.Libraries.IncompatibleLibraryVersionException;
+import com.google.gwt.dev.javac.CompilationErrorsIndexImpl;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.jjs.CompilerIoException;
 import com.google.gwt.dev.jjs.PermutationResult;
@@ -130,6 +131,33 @@
       }
     }
 
+    private CompilationErrorsIndexImpl readCompilationErrorsIndex() {
+      ZipEntry compilationErrorsIndexEntry =
+          zipFile.getEntry(Libraries.COMPILATION_ERRORS_INDEX_ENTRY_NAME);
+      if (compilationErrorsIndexEntry == null) {
+        return null;
+      }
+
+      InputStream compilationErrorsIndexInputStream = getInputStream(compilationErrorsIndexEntry);
+
+      CompilationErrorsIndexImpl newCompilationErrorsIndex;
+      try {
+        ObjectInputStream objectInputStream =
+            new ObjectInputStream(compilationErrorsIndexInputStream);
+        newCompilationErrorsIndex = (CompilationErrorsIndexImpl) objectInputStream.readObject();
+        objectInputStream.close();
+      } catch (IOException e) {
+        throw new CompilerIoException(
+            "Failed to read the compilation errors index for deserialization.", e);
+      } catch (ClassNotFoundException e) {
+        throw new CompilerIoException(
+            "Failed to deserialize the compilation errors index because a "
+            + "referenced type could not be found.", e);
+      }
+
+      return newCompilationErrorsIndex;
+    }
+
     private CompilationUnit readCompilationUnitByTypeSourceName(String typeSourceName) {
       ZipEntry compilationUnitEntry =
           zipFile.getEntry(Libraries.computeCompilationUnitEntryName(typeSourceName));
@@ -275,6 +303,7 @@
   private Set<String> buildResourcePaths;
   private Map<String, Resource> buildResourcesByPath = Maps.newHashMap();
   private Set<String> classFilePaths;
+  private CompilationErrorsIndexImpl compilationErrorsIndex;
   private Multimap<String, String> compilationUnitNamesByNestedBinaryName = HashMultimap.create();
   private Multimap<String, String> compilationUnitNamesByNestedSourceName = HashMultimap.create();
   private Map<String, CompilationUnit> compilationUnitsByTypeBinaryName = Maps.newHashMap();
@@ -329,6 +358,19 @@
   }
 
   @Override
+  public CompilationErrorsIndexImpl getCompilationErrorsIndex() {
+    if (compilationErrorsIndex == null) {
+      compilationErrorsIndex = zipLibraryReader.readCompilationErrorsIndex();
+      // There may have been no compilation error index data to read but libraries must still return
+      // an empty index instead of null.
+      if (compilationErrorsIndex == null) {
+        compilationErrorsIndex = new CompilationErrorsIndexImpl();
+      }
+    }
+    return compilationErrorsIndex;
+  }
+
+  @Override
   public CompilationUnit getCompilationUnitByTypeBinaryName(String typeBinaryName) {
     // If the type cache doesn't contain the type yet.
     if (!compilationUnitsByTypeBinaryName.containsKey(typeBinaryName)) {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java b/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java
index bcbef44..fb63083 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ZipLibraryWriter.java
@@ -17,6 +17,7 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.linker.ArtifactSet;
 import com.google.gwt.core.ext.linker.GeneratedResource;
+import com.google.gwt.dev.javac.CompilationErrorsIndex;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.javac.CompiledClass;
 import com.google.gwt.dev.jjs.CompilerIoException;
@@ -74,6 +75,18 @@
       zipFile = new File(zipFileName);
     }
 
+    public void writeCompilationErrorsIndex() {
+      startEntry(Libraries.COMPILATION_ERRORS_INDEX_ENTRY_NAME);
+      try {
+        ObjectOutputStream out = new ObjectOutputStream(zipOutputStream);
+        out.writeObject(compilationErrorsIndex);
+      } catch (IOException e) {
+        throw new CompilerIoException(
+            "Failed to serialize the compilation errors index in new library " + zipFile.getPath()
+            + ".", e);
+      }
+    }
+
     public void writeProcessedReboundTypeSourceNamesForGenerators() {
       writeStringMultimap(Libraries.PROCESSED_REBOUND_TYPE_SOURCE_NAMES_ENTRY_NAME,
           processedReboundTypeSourceNamesByGenerator);
@@ -167,6 +180,7 @@
         writeClassFilePaths();
         writeCompilationUnitTypeSourceNames();
         writeNestedNamesByCompilationUnitName();
+        writeCompilationErrorsIndex();
 
         // Resources
         writeBuildResources();
@@ -360,6 +374,7 @@
   }
 
   private Map<String, Resource> buildResourcesByPath = Maps.newHashMap();
+  private CompilationErrorsIndex compilationErrorsIndex;
   private Map<String, CompilationUnit> compilationUnitsByTypeSourceName = Maps.newHashMap();
   private Set<String> dependencyLibraryNames = Sets.newHashSet();
   private ArtifactSet generatedArtifacts = new ArtifactSet();
@@ -372,9 +387,9 @@
   private SetMultimap<String, String> processedReboundTypeSourceNamesByGenerator =
       HashMultimap.create();
   private Map<String, Resource> publicResourcesByPath = Maps.newHashMap();
+  private Set<String> reboundTypeSourceNames = Sets.newHashSet();
   private Set<String> regularClassFilePaths = Sets.newHashSet();
   private Set<String> regularCompilationUnitTypeSourceNames = Sets.newLinkedHashSet();
-  private Set<String> reboundTypeSourceNames = Sets.newHashSet();
   private Set<String> superSourceClassFilePaths = Sets.newHashSet();
   private Set<String> superSourceCompilationUnitTypeSourceNames = Sets.newLinkedHashSet();
   private ZipWriter zipWriter;
@@ -465,14 +480,19 @@
   }
 
   @Override
+  public void markReboundTypeProcessed(String processedReboundTypeSourceName,
+      String generatorName) {
+    processedReboundTypeSourceNamesByGenerator.put(generatorName, processedReboundTypeSourceName);
+  }
+
+  @Override
   public void markReboundTypesProcessed(Set<String> reboundTypeSourceNames) {
     this.reboundTypeSourceNames = reboundTypeSourceNames;
   }
 
   @Override
-  public void markReboundTypeProcessed(String processedReboundTypeSourceName,
-      String generatorName) {
-    processedReboundTypeSourceNamesByGenerator.put(generatorName, processedReboundTypeSourceName);
+  public void setCompilationErrorsIndex(CompilationErrorsIndex compilationErrorsIndex) {
+    this.compilationErrorsIndex = compilationErrorsIndex;
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/javac/CombinedCompilationErrorsIndex.java b/dev/core/src/com/google/gwt/dev/javac/CombinedCompilationErrorsIndex.java
new file mode 100644
index 0000000..38626be
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/CombinedCompilationErrorsIndex.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 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.gwt.dev.javac;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A CompilationErrorsIndex facade that exposes the contents of two contained
+ * CompilationErrorsIndices.
+ */
+public class CombinedCompilationErrorsIndex implements CompilationErrorsIndex {
+
+  private CompilationErrorsIndex libraryCompilationErrorsIndex;
+  private CompilationErrorsIndex localCompilationErrorsIndexes;
+
+  public CombinedCompilationErrorsIndex(CompilationErrorsIndex localCompilationErrorsIndexes,
+      CompilationErrorsIndex libraryCompilationErrorsIndex) {
+    this.localCompilationErrorsIndexes = localCompilationErrorsIndexes;
+    this.libraryCompilationErrorsIndex = libraryCompilationErrorsIndex;
+  }
+
+  @Override
+  public void add(String typeSourceName, String fileName, List<String> typeReferences,
+      List<String> compilationErrors) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Set<String> getCompileErrors(String typeSourceName) {
+    if (localCompilationErrorsIndexes.hasCompileErrors(typeSourceName)) {
+      return localCompilationErrorsIndexes.getCompileErrors(typeSourceName);
+    }
+    if (libraryCompilationErrorsIndex.hasCompileErrors(typeSourceName)) {
+      return libraryCompilationErrorsIndex.getCompileErrors(typeSourceName);
+    }
+    return null;
+  }
+
+  @Override
+  public String getFileName(String typeSourceName) {
+    String localFileName = localCompilationErrorsIndexes.getFileName(typeSourceName);
+    if (localFileName != null) {
+      return localFileName;
+    }
+    String libraryFileName = libraryCompilationErrorsIndex.getFileName(typeSourceName);
+    if (libraryFileName != null) {
+      return libraryFileName;
+    }
+    return null;
+  }
+
+  @Override
+  public Set<String> getTypeReferences(String typeSourceName) {
+    if (localCompilationErrorsIndexes.hasTypeReferences(typeSourceName)) {
+      return localCompilationErrorsIndexes.getTypeReferences(typeSourceName);
+    }
+    if (libraryCompilationErrorsIndex.hasTypeReferences(typeSourceName)) {
+      return libraryCompilationErrorsIndex.getTypeReferences(typeSourceName);
+    }
+    return null;
+  }
+
+  @Override
+  public boolean hasCompileErrors(String typeSourceName) {
+    return localCompilationErrorsIndexes.hasCompileErrors(typeSourceName)
+        || libraryCompilationErrorsIndex.hasCompileErrors(typeSourceName);
+  }
+
+  @Override
+  public boolean hasTypeReferences(String typeSourceName) {
+    return localCompilationErrorsIndexes.hasTypeReferences(typeSourceName)
+        || libraryCompilationErrorsIndex.hasTypeReferences(typeSourceName);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationErrorsIndex.java b/dev/core/src/com/google/gwt/dev/javac/CompilationErrorsIndex.java
new file mode 100644
index 0000000..4893e21
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationErrorsIndex.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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.gwt.dev.javac;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Provides fast access to compilation error information to support detailed logging.
+ */
+public interface CompilationErrorsIndex {
+
+  /**
+   * Records the filename, names of referenced types, and known compilation errors for a given type.
+   */
+  void add(String typeSourceName, String fileName, List<String> typeReferences,
+      List<String> compilationErrors);
+
+  /**
+   * Returns the compile error strings previously recorded for a given type.
+   */
+  Set<String> getCompileErrors(String typeSourceName);
+
+  /**
+   * Returns the file name previously recorded for a given type.
+   */
+  String getFileName(String typeSourceName);
+
+  /**
+   * Returns the type reference strings previously recorded for a given type.
+   */
+  Set<String> getTypeReferences(String typeSourceName);
+
+  /**
+   * Returns whether a given type has any recorded compile errors.
+   */
+  boolean hasCompileErrors(String typeSourceName);
+
+  /**
+   * Returns whether a given type has any recorded type references.
+   */
+  boolean hasTypeReferences(String typeSourceName);
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationErrorsIndexImpl.java b/dev/core/src/com/google/gwt/dev/javac/CompilationErrorsIndexImpl.java
new file mode 100644
index 0000000..b8ddbb6
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationErrorsIndexImpl.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 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.gwt.dev.javac;
+
+import com.google.gwt.thirdparty.guava.common.base.Objects;
+import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
+import com.google.gwt.thirdparty.guava.common.collect.Maps;
+import com.google.gwt.thirdparty.guava.common.collect.SetMultimap;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A mutable and serializable CompilationErrorsIndex.
+ * <p>
+ * JDT compilation errors for a current compile can be accumulated here, saved into libraries, and
+ * reloaded later from libraries to enable accurate and detailed compilation error cause traces.
+ */
+public class CompilationErrorsIndexImpl implements CompilationErrorsIndex, Serializable {
+
+  private static SetMultimap<String, String> readStringListMap(ObjectInputStream objectInputStream)
+      throws IOException {
+    HashMultimap<String, String> stringListMap = HashMultimap.<String, String> create();
+    int keyCount = objectInputStream.readInt();
+    for (int i = 0; i < keyCount; i++) {
+      String key = objectInputStream.readUTF();
+      int listLength = objectInputStream.readInt();
+      for (int j = 0; j < listLength; j++) {
+        stringListMap.put(key, objectInputStream.readUTF());
+      }
+    }
+    return stringListMap;
+  }
+
+  private static Map<String, String> readStringMap(ObjectInputStream objectInputStream)
+      throws IOException {
+    Map<String, String> stringMap = Maps.newHashMap();
+    int fileNameMappingCount = objectInputStream.readInt();
+    for (int i = 0; i < fileNameMappingCount; i++) {
+      String typeSourceName = objectInputStream.readUTF();
+      String fileName = objectInputStream.readUTF();
+      stringMap.put(typeSourceName, fileName);
+    }
+    return stringMap;
+  }
+
+  private static void writeStringListMap(ObjectOutputStream objectOutputStream,
+      Map<String, Collection<String>> stringMap) throws IOException {
+    objectOutputStream.writeInt(stringMap.size());
+    for (Entry<String, Collection<String>> entry : stringMap.entrySet()) {
+      objectOutputStream.writeUTF(entry.getKey());
+      Collection<String> strings = entry.getValue();
+      objectOutputStream.writeInt(strings.size());
+      for (String string : strings) {
+        objectOutputStream.writeUTF(string);
+      }
+    }
+  }
+
+  private static void writeStringMap(ObjectOutputStream objectOutputStream,
+      Map<String, String> stringMap) throws IOException {
+    objectOutputStream.writeInt(stringMap.size());
+    for (Entry<String, String> entry : stringMap.entrySet()) {
+      objectOutputStream.writeUTF(entry.getKey());
+      objectOutputStream.writeUTF(entry.getValue());
+    }
+  }
+
+  private SetMultimap<String, String> compilationErrorsByTypeSourceName =
+      HashMultimap.<String, String> create();
+  private Map<String, String> fileNamesByTypeSourceName = Maps.newHashMap();
+  private SetMultimap<String, String> referencesByTypeSourceName =
+      HashMultimap.<String, String> create();
+
+  @Override
+  public void add(String typeSourceName, String fileName, List<String> typeReferences,
+      List<String> compilationErrors) {
+    fileNamesByTypeSourceName.put(typeSourceName, fileName);
+    compilationErrorsByTypeSourceName.putAll(typeSourceName, compilationErrors);
+    referencesByTypeSourceName.putAll(typeSourceName, typeReferences);
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (object instanceof CompilationErrorsIndexImpl) {
+      CompilationErrorsIndexImpl that = (CompilationErrorsIndexImpl) object;
+      return
+          Objects.equal(this.fileNamesByTypeSourceName, that.fileNamesByTypeSourceName) && Objects
+              .equal(this.compilationErrorsByTypeSourceName, that.compilationErrorsByTypeSourceName)
+          && Objects.equal(this.referencesByTypeSourceName, that.referencesByTypeSourceName);
+    }
+    return false;
+  }
+
+  @Override
+  public Set<String> getCompileErrors(String typeSourceName) {
+    return compilationErrorsByTypeSourceName.get(typeSourceName);
+  }
+
+  @Override
+  public String getFileName(String typeSourceName) {
+    return fileNamesByTypeSourceName.get(typeSourceName);
+  }
+
+  @Override
+  public Set<String> getTypeReferences(String typeSourceName) {
+    return referencesByTypeSourceName.get(typeSourceName);
+  }
+
+  @Override
+  public boolean hasCompileErrors(String typeSourceName) {
+    return compilationErrorsByTypeSourceName.containsKey(typeSourceName);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(fileNamesByTypeSourceName, compilationErrorsByTypeSourceName,
+        referencesByTypeSourceName);
+  }
+
+  @Override
+  public boolean hasTypeReferences(String typeSourceName) {
+    return referencesByTypeSourceName.containsKey(typeSourceName);
+  }
+
+  public void merge(CompilationErrorsIndexImpl that) {
+    for (String typeSourceName : that.fileNamesByTypeSourceName.keySet()) {
+      if (!this.fileNamesByTypeSourceName.containsKey(typeSourceName)) {
+        this.fileNamesByTypeSourceName.put(typeSourceName,
+            that.fileNamesByTypeSourceName.get(typeSourceName));
+        this.compilationErrorsByTypeSourceName.putAll(typeSourceName,
+            that.compilationErrorsByTypeSourceName.get(typeSourceName));
+        this.referencesByTypeSourceName.putAll(typeSourceName,
+            that.referencesByTypeSourceName.get(typeSourceName));
+      }
+    }
+  }
+
+  private void readObject(ObjectInputStream objectInputStream) throws IOException {
+    fileNamesByTypeSourceName = readStringMap(objectInputStream);
+    compilationErrorsByTypeSourceName = readStringListMap(objectInputStream);
+    referencesByTypeSourceName = readStringListMap(objectInputStream);
+  }
+
+  private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
+    writeStringMap(objectOutputStream, fileNamesByTypeSourceName);
+    writeStringListMap(objectOutputStream, compilationErrorsByTypeSourceName.asMap());
+    writeStringListMap(objectOutputStream, referencesByTypeSourceName.asMap());
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationProblemReporter.java b/dev/core/src/com/google/gwt/dev/javac/CompilationProblemReporter.java
index cce22a7..741d2af 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationProblemReporter.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationProblemReporter.java
@@ -19,12 +19,14 @@
 import com.google.gwt.core.ext.TreeLogger.HelpInfo;
 import com.google.gwt.core.ext.TreeLogger.Type;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.CompilerContext;
 import com.google.gwt.dev.javac.CompilationUnitBuilder.GeneratedCompilationUnit;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.InternalCompilerException.NodeInfo;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.util.Messages;
 import com.google.gwt.dev.util.Util;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
 
 import org.eclipse.jdt.core.compiler.CategorizedProblem;
 
@@ -34,7 +36,6 @@
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
 
@@ -45,6 +46,21 @@
 public class CompilationProblemReporter {
 
   /**
+   * Traverses a set of compilation units to record enough information to enable accurate and
+   * detailed compilation error cause traces.
+   */
+  public static void indexErrors(CompilationErrorsIndex compilationErrorsIndex,
+      List<CompilationUnit> units) {
+    for (CompilationUnit unit : units) {
+      if (unit.isError()) {
+        Dependencies dependencies = unit.getDependencies();
+        compilationErrorsIndex.add(unit.getTypeName(), unit.getResourceLocation(),
+            dependencies.getApiRefs(), CompilationProblemReporter.formatErrors(unit));
+      }
+    }
+  }
+
+  /**
    * Used as a convenience to catch all exceptions thrown by the compiler. For
    * instances of {@link InternalCompilerException}, extra diagnostics are
    * printed.
@@ -98,62 +114,68 @@
   }
 
   /**
-   * Provides a meaningful error message when a type is missing from the
-   * {@link com.google.gwt.core.ext.typeinfo.TypeOracle} or
-   * {@link com.google.gwt.dev.shell.CompilingClassLoader}.
-   *
-   * @param logger logger for logging errors to the console
-   * @param missingType The qualified source name of the type to report
+   * Provides meaningful error messages and hints for types that failed to compile or are otherwise
+   * missing.
    */
-  public static void logMissingTypeErrorWithHints(TreeLogger logger, String missingType,
-      CompilationState compilationState) {
-    logDependentErrors(logger, missingType, compilationState);
-    logger = logger.branch(TreeLogger.ERROR, "Unable to find type '" + missingType + "'", null);
+  public static int logErrorTrace(TreeLogger logger, Type logLevel,
+      CompilerContext compilerContext, List<CompilationUnit> units, boolean hint) {
+    int errorCount = 0;
+    for (CompilationUnit unit : units) {
+      if (unit.isError()) {
+        logErrorTrace(logger, logLevel, compilerContext, unit.getTypeName(), hint);
+        errorCount++;
 
-    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        if (logger.isLoggable(TreeLogger.INFO) && unit instanceof GeneratedCompilationUnit) {
+          CompilationProblemReporter.maybeDumpSource(logger,
+              ((GeneratedCompilationUnit) unit).getSource(), unit.getTypeName());
+        }
+      }
+    }
+    return errorCount++;
+  }
 
-    URL sourceURL = Util.findSourceInClassPath(cl, missingType);
-    if (sourceURL != null) {
-      Messages.HINT_PRIOR_COMPILER_ERRORS.log(logger, null);
-      if (missingType.indexOf(".client.") != -1) {
-        Messages.HINT_CHECK_MODULE_INHERITANCE.log(logger, null);
-      } else {
-        Messages.HINT_CHECK_MODULE_NONCLIENT_SOURCE_DECL.log(logger, null);
-      }
-    } else if (!missingType.equals("java.lang.Object")) {
-      boolean strictSourceResources =
-          compilationState.getCompilerContext().getOptions().enforceStrictSourceResources();
-      if (strictSourceResources) {
-        Messages.HINT_STRICT_SOURCE_ENTRIES.log(logger, null);
-      }
-      Messages.HINT_CHECK_TYPENAME.log(logger, missingType, null);
-      Messages.HINT_CHECK_CLASSPATH_SOURCE_ENTRIES.log(logger, null);
+  /**
+   * Provides a meaning error message and hints for a type that failed to compile or is otherwise
+   * missing.
+   */
+  public static void logErrorTrace(TreeLogger logger, Type logLevel,
+      CompilerContext compilerContext, String typeSourceName, boolean hint) {
+    TreeLogger branch = logger.branch(TreeLogger.INFO,
+        "Tracing compile failure path for type '" + typeSourceName + "'");
+    if (logErrorChain(branch, logLevel, typeSourceName,
+        compilerContext.getGlobalCompilationErrorsIndex())) {
+      return;
     }
 
-    /*
-     * For missing JRE emulated classes (e.g. Object), or the main GWT
-     * libraries, there are special warnings.
-     */
-    if (missingType.indexOf("java.lang.") == 0 || missingType.indexOf("com.google.gwt.core.") == 0) {
-      Messages.HINT_CHECK_INHERIT_CORE.log(logger, null);
-    } else if (missingType.indexOf("com.google.gwt.user.") == 0) {
-      Messages.HINT_CHECK_INHERIT_USER.log(logger, null);
+    if (hint) {
+      logHints(logger, compilerContext, typeSourceName);
     }
   }
 
+  public static int logWarnings(TreeLogger logger, Type logLevelForWarnings,
+      List<CompilationUnit> units) {
+    int warningCount = 0;
+    for (CompilationUnit unit : units) {
+      if (CompilationProblemReporter.logWarnings(logger, logLevelForWarnings, unit)) {
+        warningCount++;
+      }
+    }
+    return warningCount++;
+  }
+
   /**
    * Logs errors to the console.
    *
    * @param logger logger for reporting errors to the console
    * @param unit Compilation unit that may have errors
-   * @param suppressErrors Controls he log level for logging errors. If
-   *          <code>false</code> is passed, compilation errors are logged at
-   *          TreeLogger.ERROR and warnings logged at TreeLogger.WARN. If
-   *          <code>true</code> is passed, compilation errors are logged at
+   * @param suppressErrors Controls he log level for logging errors. If <code>false</code> is passed,
+   *          compilation errors are logged at TreeLogger.ERROR and warnings logged at
+   *          TreeLogger.WARN. If <code>true</code> is passed, compilation errors are logged at
    *          TreeLogger.TRACE and TreeLogger.DEBUG.
    * @return <code>true</code> if an error was logged.
    */
-  public static boolean reportErrors(TreeLogger logger, CompilationUnit unit, boolean suppressErrors) {
+  public static boolean reportErrors(TreeLogger logger, CompilationUnit unit,
+      boolean suppressErrors) {
     CategorizedProblem[] problems = unit.getProblems();
     if (problems == null || problems.length == 0) {
       return false;
@@ -184,15 +206,6 @@
         // Ignore all other problems.
         continue;
       }
-      // Append 'Line #: msg' to the error message.
-      StringBuffer msgBuf = new StringBuffer();
-      int line = problem.getSourceLineNumber();
-      if (line > 0) {
-        msgBuf.append("Line ");
-        msgBuf.append(line);
-        msgBuf.append(": ");
-      }
-      msgBuf.append(problem.getMessage());
 
       HelpInfo helpInfo = null;
       if (problem instanceof GWTProblem) {
@@ -204,7 +217,7 @@
         String branchString = isError ? "Errors" : "Warnings";
         branch = logger.branch(branchType, branchString + " in '" + fileName + "'", null);
       }
-      branch.log(logLevel, msgBuf.toString(), null, helpInfo);
+      branch.log(logLevel, toMessageWithLineNumber(problem), null, helpInfo);
     }
 
     if (branch != null && branch.isLoggable(TreeLogger.INFO)) {
@@ -217,23 +230,54 @@
     return branch != null;
   }
 
-  private static void addUnitToVisit(Map<String, CompiledClass> classMap, String typeName,
-      Queue<CompilationUnit> toVisit, Set<CompilationUnit> visited) {
-    CompiledClass found = classMap.get(typeName);
-    if (found != null) {
-      CompilationUnit unit = found.getUnit();
-      if (!visited.contains(unit)) {
-        toVisit.add(unit);
-        visited.add(unit);
+  private static void addUnitToVisit(CompilationErrorsIndex compilationErrorsIndex,
+      String typeSourceName, Queue<String> toVisit, Set<String> visited) {
+    if (compilationErrorsIndex.hasCompileErrors(typeSourceName)) {
+      if (!visited.contains(typeSourceName)) {
+        toVisit.add(typeSourceName);
+        visited.add(typeSourceName);
       }
     }
   }
 
-  private static void logDependentErrors(TreeLogger logger, String missingType,
-      CompilationState compilationState) {
-    final Set<CompilationUnit> visited = new HashSet<CompilationUnit>();
-    final Queue<CompilationUnit> toVisit = new LinkedList<CompilationUnit>();
-    Map<String, CompiledClass> classMap = compilationState.getClassFileMapBySource();
+  /**
+   * Returns readable compilation error messages for a compilation unit.
+   * <p>
+   * Should only be run on CompilationUnits that actually have problems.
+   */
+  private static List<String> formatErrors(CompilationUnit unit) {
+    CategorizedProblem[] problems = unit.getProblems();
+    assert problems != null && problems.length > 0;
+
+    List<String> errorMessages = Lists.newArrayList();
+    for (CategorizedProblem problem : problems) {
+      if (!problem.isError()) {
+        continue;
+      }
+
+      errorMessages.add(toMessageWithLineNumber(problem));
+    }
+
+    return errorMessages;
+  }
+
+  private static boolean hasWarnings(CompilationUnit unit) {
+    CategorizedProblem[] problems = unit.getProblems();
+    if (problems == null || problems.length == 0) {
+      return false;
+    }
+    for (CategorizedProblem problem : problems) {
+      if (problem.isWarning() && problem instanceof GWTProblem) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private static boolean logErrorChain(TreeLogger logger, Type logLevel,
+      String typeSourceName, CompilationErrorsIndex compilationErrorsIndex) {
+    final Set<String> visited = new HashSet<String>();
+    final Queue<String> toVisit = new LinkedList<String>();
 
     /*
      * Traverses CompilationUnits enqueued in toVisit(), calling {@link
@@ -241,17 +285,85 @@
      * CompilationUnit is visited only once, and only if it is reachable via the
      * {@link Dependencies} graph.
      */
-    addUnitToVisit(classMap, missingType, toVisit, visited);
+    addUnitToVisit(compilationErrorsIndex, typeSourceName, toVisit, visited);
 
     while (!toVisit.isEmpty()) {
-      CompilationUnit unit = toVisit.remove();
-      CompilationProblemReporter.reportErrors(logger, unit, false);
+      String dependentTypeSourceName = toVisit.remove();
 
-      for (String apiRef : unit.getDependencies().getApiRefs()) {
-        addUnitToVisit(classMap, apiRef, toVisit, visited);
+      Set<String> compileErrors = compilationErrorsIndex.getCompileErrors(dependentTypeSourceName);
+      TreeLogger branch = logger.branch(logLevel,
+          "Errors in '" + compilationErrorsIndex.getFileName(dependentTypeSourceName) + "'");
+      for (String compileError : compileErrors) {
+        branch.log(logLevel, compileError);
+      }
+
+      Set<String> typeReferences =
+          compilationErrorsIndex.getTypeReferences(dependentTypeSourceName);
+      if (typeReferences != null) {
+        for (String typeReference : typeReferences) {
+          addUnitToVisit(compilationErrorsIndex, typeReference, toVisit, visited);
+        }
       }
     }
     logger.log(TreeLogger.DEBUG, "Checked " + visited.size() + " dependencies for errors.");
+    return visited.size() > 1;
+  }
+
+  private static void logHints(TreeLogger logger, CompilerContext compilerContext,
+      String typeSourceName) {
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+    URL sourceURL = Util.findSourceInClassPath(cl, typeSourceName);
+    if (sourceURL != null) {
+      if (typeSourceName.indexOf(".client.") != -1) {
+        Messages.HINT_CHECK_MODULE_INHERITANCE.log(logger, null);
+      } else {
+        Messages.HINT_CHECK_MODULE_NONCLIENT_SOURCE_DECL.log(logger, null);
+      }
+    } else if (!typeSourceName.equals("java.lang.Object")) {
+      boolean strictSourceResources =
+          compilerContext.getOptions().enforceStrictSourceResources();
+      if (strictSourceResources) {
+        Messages.HINT_STRICT_SOURCE_ENTRIES.log(logger, null);
+      }
+      Messages.HINT_CHECK_TYPENAME.log(logger, typeSourceName, null);
+      Messages.HINT_CHECK_CLASSPATH_SOURCE_ENTRIES.log(logger, null);
+    }
+
+    /*
+     * For missing JRE emulated classes (e.g. Object), or the main GWT libraries, there are special
+     * warnings.
+     */
+    if (typeSourceName.indexOf("java.lang.") == 0
+        || typeSourceName.indexOf("com.google.gwt.core.") == 0) {
+      Messages.HINT_CHECK_INHERIT_CORE.log(logger, null);
+    } else if (typeSourceName.indexOf("com.google.gwt.user.") == 0) {
+      Messages.HINT_CHECK_INHERIT_USER.log(logger, null);
+    }
+  }
+
+  private static boolean logWarnings(TreeLogger logger, TreeLogger.Type logLevel,
+      CompilationUnit unit) {
+    if (!hasWarnings(unit)) {
+      return false;
+    }
+
+    TreeLogger branch =
+        logger.branch(logLevel, "Warnings in '" + unit.getResourceLocation() + "'", null);
+    for (CategorizedProblem problem : unit.getProblems()) {
+      if (!problem.isWarning() || !(problem instanceof GWTProblem)) {
+        continue;
+      }
+
+      branch.log(logLevel, toMessageWithLineNumber(problem), null,
+          ((GWTProblem) problem).getHelpInfo());
+    }
+
+    if (branch.isLoggable(TreeLogger.INFO) && unit instanceof GeneratedCompilationUnit) {
+      CompilationProblemReporter.maybeDumpSource(branch,
+          ((GeneratedCompilationUnit) unit).getSource(), unit.getTypeName());
+    }
+    return true;
   }
 
   /**
@@ -277,4 +389,9 @@
     }
     logger.log(TreeLogger.INFO, "Unable to dump source to disk", caught);
   }
+
+  private static String toMessageWithLineNumber(CategorizedProblem problem) {
+    int lineNumber = problem.getSourceLineNumber();
+    return (lineNumber > 0 ? "Line " + lineNumber + ": " : "") + problem.getMessage();
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationState.java b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
index 4fcefec..d97ba9d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationState.java
@@ -253,8 +253,8 @@
     // Performed after compilation unit invalidator because only valid units should be saved in the
     // library.
     if (saveInLibrary) {
-      CompilationUnitInvalidator.retainValidUnits(logger, units,
-          compileMoreLater.getValidClasses());
+      CompilationUnitInvalidator.retainValidUnits(logger, units, compileMoreLater.getValidClasses(),
+          compilerContext.getLocalCompilationErrorsIndex());
       for (CompilationUnit compilationUnit : units) {
         compilerContext.getLibraryWriter().addCompilationUnit(compilationUnit);
       }
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
index 15f33e0..9ab1d3d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.javac;
 
 import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.CompilerContext;
 import com.google.gwt.dev.javac.JdtCompiler.AdditionalTypeProviderDelegate;
@@ -147,9 +148,9 @@
 
             // Only run this pass if JDT was able to compile the unit with no errors, otherwise
             // the JDT AST traversal might throw exceptions.
-            apiRefs = compiler.collectApiRefs(cud);
             methodArgs = MethodParamCollector.collect(cud, builder.getSourceMapPath());
           }
+          apiRefs = compiler.collectApiRefs(cud);
 
           final Interner<String> interner = StringInterner.get();
           String packageName = interner.intern(Shared.getPackageName(builder.getTypeName()));
@@ -383,21 +384,34 @@
       // stale cache files.
       unitCache.cleanup(logger);
 
-      // Sort, then report all errors (re-report for cached units).
-      Collections.sort(resultUnits, CompilationUnit.COMPARATOR);
-      logger = logger.branch(TreeLogger.DEBUG, "Validating units:");
-      int errorCount = 0;
-      for (CompilationUnit unit : resultUnits) {
-        if (CompilationProblemReporter.reportErrors(logger, unit, suppressErrors)) {
-          errorCount++;
-        }
+      // Report warnings.
+      Type logLevelForWarnings = suppressErrors ? TreeLogger.DEBUG : TreeLogger.WARN;
+      int warningCount =
+          CompilationProblemReporter.logWarnings(logger, logLevelForWarnings, resultUnits);
+      if (warningCount > 0 && !logger.isLoggable(logLevelForWarnings)) {
+        logger.log(TreeLogger.INFO, "Ignored " + warningCount + " unit"
+            + (warningCount > 1 ? "s" : "") + " with compilation errors in first pass.\n"
+            + "Compile with -strict or with -logLevel set to DEBUG or WARN to see all errors.");
       }
-      if (suppressErrors && errorCount > 0 && !logger.isLoggable(TreeLogger.TRACE)
+
+      // Index errors so that error chains can be reported.
+      CompilationProblemReporter.indexErrors(compilerContext.getLocalCompilationErrorsIndex(),
+          resultUnits);
+
+      // Report error chains and hints.
+      Type logLevelForErrors = suppressErrors ? TreeLogger.TRACE : TreeLogger.ERROR;
+      int errorCount = CompilationProblemReporter.logErrorTrace(logger, logLevelForErrors,
+          compilerContext, resultUnits, false);
+      if (errorCount > 0 && !logger.isLoggable(logLevelForErrors)
           && logger.isLoggable(TreeLogger.INFO)) {
         logger.log(TreeLogger.INFO, "Ignored " + errorCount + " unit" + (errorCount > 1 ? "s" : "")
             + " with compilation errors in first pass.\n"
             + "Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.");
       }
+
+      // Sort units to ensure stable output.
+      Collections.sort(resultUnits, CompilationUnit.COMPARATOR);
+
       return resultUnits;
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
index bc7505a..3e636c2 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitInvalidator.java
@@ -17,13 +17,13 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
 import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
 import com.google.gwt.thirdparty.guava.common.collect.Multimap;
 
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 
 /**
@@ -43,8 +43,8 @@
    * {@code units} or {@code validClasses}.
    * </ul>
    */
-  public static void retainValidUnits(TreeLogger logger,
-      Collection<CompilationUnit> units, Map<String, CompiledClass> validClasses) {
+  public static void retainValidUnits(TreeLogger logger, Collection<CompilationUnit> units,
+      Map<String, CompiledClass> validClasses, CompilationErrorsIndex compilationErrorsIndex) {
     logger = logger.branch(TreeLogger.TRACE, "Removing invalidated units");
 
     // Build a map of api-refs -> dependent units.
@@ -119,6 +119,11 @@
               "Compilation unit '" + brokenUnit
               + "' is removed due to invalid reference(s):");
           branch.log(TreeLogger.DEBUG, brokenEntry.getKey());
+          // Record inferred errors resulting from references to broken types so that accurate
+          // errors chains can be reported.
+          compilationErrorsIndex.add(brokenUnit.getTypeName(), brokenUnit.getResourceLocation(),
+              brokenUnit.getDependencies().getApiRefs(),
+              ImmutableList.of(brokenEntry.getKey() + " cannot be resolved to a type"));
         }
       }
 
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
index 8cbaf32..50c82dd 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -63,6 +63,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
 import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
 import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
@@ -866,6 +867,12 @@
       }
 
       @Override
+      protected void onMissingTypeRef(MissingTypeBinding referencedType,
+          CompilationUnitDeclaration unitOfReferrer, Expression expression) {
+        addReference(referencedType);
+      }
+
+      @Override
       protected void onBinaryTypeRef(BinaryTypeBinding referencedType,
           CompilationUnitDeclaration unitOfReferrer, Expression expression) {
         if (!String.valueOf(referencedType.getFileName()).endsWith(".java")) {
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
index a47dd18..c6b2464 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
@@ -32,6 +32,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
 import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
 import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
@@ -158,19 +159,34 @@
    */
   protected void onBinaryTypeRef(BinaryTypeBinding referencedType,
       CompilationUnitDeclaration unitOfReferrer, Expression expression) {
+    // To allow for optional subclass overriding.
+  }
+
+  /**
+   * @param referencedType
+   * @param unitOfReferrer
+   * @param expression
+   */
+  protected void onMissingTypeRef(MissingTypeBinding referencedType,
+      CompilationUnitDeclaration unitOfReferrer, Expression expression) {
+    // To allow for optional subclass overriding.
   }
 
   protected abstract void onTypeRef(SourceTypeBinding referencedType,
       CompilationUnitDeclaration unitOfReferrer);
 
   private void maybeDispatch(Expression expression, TypeBinding binding) {
-    assert (binding != null);
+    if (binding == null) {
+      return;
+    }
 
     if (binding instanceof SourceTypeBinding) {
       SourceTypeBinding type = (SourceTypeBinding) binding;
       onTypeRef(type, cud);
     } else if (binding instanceof ArrayBinding) {
       maybeDispatch(expression, ((ArrayBinding) binding).leafComponentType);
+    } else if (binding instanceof MissingTypeBinding) {
+      onMissingTypeRef((MissingTypeBinding) binding, cud, expression);
     } else if (binding instanceof BinaryTypeBinding) {
       onBinaryTypeRef((BinaryTypeBinding) binding, cud, expression);
     } else if (binding instanceof ParameterizedTypeBinding) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
index 8a02594..b302df7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
@@ -138,8 +138,12 @@
       this.compiledClassesByTypeName = compiledClassesByTypeName;
     }
 
+    protected abstract boolean hasCompileErrors(String typeName);
+
     protected abstract CompilationUnit getCompilationUnitFromLibrary(String typeName);
 
+    protected abstract void logErrorTrace(TreeLogger branch, Type logLevel, String sourceName);
+
     protected CompilationUnit getCompilationUnitFromSource(String typeName) {
       return compiledClassesByTypeName.get(typeName).getUnit();
     }
@@ -708,10 +712,15 @@
 
     // If this is a library compile.
     if (!compilerContext.shouldCompileMonolithic()) {
+      boolean errorStateBeforeForcedTraversal = errorsFound;
       // Trace execution from all types supplied as source and resolve references.
       Set<String> internalNames = ImmutableSet.copyOf(compiledClassesByInternalName.keySet());
       for (String internalName : internalNames) {
         JDeclaredType type = internalFindType(internalName, internalNameBasedTypeLocator);
+        if (type == null) {
+          continue;
+        }
+
         instantiate(type);
         for (JField field : type.getFields()) {
           flowInto(field);
@@ -720,6 +729,11 @@
           flowInto(method);
         }
       }
+      // If the compile isn't strict then errors in types that were only seen by the full traversal
+      // of all local types that occurs in incremental compiles, should be suppressed.
+      if (!compilerContext.getOptions().isStrict()) {
+        errorsFound = errorStateBeforeForcedTraversal;
+      }
     }
 
     /*
@@ -805,7 +819,8 @@
   private void assimilateLibraryUnit(CompilationUnit referencedCompilationUnit) {
     if (referencedCompilationUnit.isError()) {
       if (failedUnits.add(referencedCompilationUnit)) {
-        CompilationProblemReporter.reportErrors(logger, referencedCompilationUnit, false);
+        CompilationProblemReporter.logErrorTrace(logger, TreeLogger.ERROR,
+            compilerContext, referencedCompilationUnit.getTypeName(), true);
         errorsFound = true;
       }
       return;
@@ -829,7 +844,8 @@
   private void assimilateSourceUnit(CompilationUnit unit) {
     if (unit.isError()) {
       if (failedUnits.add(unit)) {
-        CompilationProblemReporter.reportErrors(logger, unit, false);
+        CompilationProblemReporter.logErrorTrace(logger, TreeLogger.ERROR,
+            compilerContext, unit.getTypeName(), true);
         errorsFound = true;
       }
       return;
@@ -1082,6 +1098,17 @@
       protected CompilationUnit getCompilationUnitFromLibrary(String sourceName) {
         return compilerContext.getLibraryGroup().getCompilationUnitByTypeSourceName(sourceName);
       }
+
+      @Override
+      protected boolean hasCompileErrors(String sourceName) {
+        return compilerContext.getGlobalCompilationErrorsIndex().hasCompileErrors(sourceName);
+      }
+
+      @Override
+      protected void logErrorTrace(TreeLogger branch, Type logLevel, String sourceName) {
+        CompilationProblemReporter.logErrorTrace(branch, logLevel, compilerContext, sourceName,
+            false);
+      }
     };
     binaryNameBasedTypeLocator = new NameBasedTypeLocator(null) {
       @Override
@@ -1102,6 +1129,17 @@
         return internalNameBasedTypeLocator.sourceCompilationUnitIsAvailable(
             BinaryName.toInternalName(binaryName));
       }
+
+      @Override
+      protected boolean hasCompileErrors(String binaryName) {
+        return sourceNameBasedTypeLocator.hasCompileErrors(BinaryName.toSourceName(binaryName));
+      }
+
+      @Override
+      protected void logErrorTrace(TreeLogger branch, Type logLevel, String binaryName) {
+        sourceNameBasedTypeLocator.logErrorTrace(branch, logLevel,
+            BinaryName.toSourceName(binaryName));
+      }
     };
     internalNameBasedTypeLocator = new NameBasedTypeLocator(compiledClassesByInternalName) {
       @Override
@@ -1123,6 +1161,17 @@
         return binaryNameBasedTypeLocator.resolvedTypeIsAvailable(
             InternalName.toBinaryName(internalName));
       }
+
+      @Override
+      protected boolean hasCompileErrors(String internalName) {
+        return sourceNameBasedTypeLocator.hasCompileErrors(InternalName.toSourceName(internalName));
+      }
+
+      @Override
+      protected void logErrorTrace(TreeLogger branch, Type logLevel, String internalName) {
+        sourceNameBasedTypeLocator.logErrorTrace(branch, logLevel,
+            BinaryName.toSourceName(internalName));
+      }
     };
   }
 
@@ -1296,8 +1345,16 @@
     }
 
     if (compilerContext.shouldCompileMonolithic()) {
-      logger.log(TreeLogger.ERROR, String.format("Could not find %s in types compiled from source. "
-          + "It was either unavailable or failed to compile.", typeName));
+      if (nameBasedTypeLocator.hasCompileErrors(typeName)) {
+        TreeLogger branch = logger.branch(TreeLogger.ERROR, String.format(
+            "Type %s could not be referenced because it previously failed to "
+            + "compile with errors:"));
+        nameBasedTypeLocator.logErrorTrace(branch, TreeLogger.ERROR, typeName);
+      } else {
+        logger.log(TreeLogger.ERROR, String.format(
+            "Could not find %s in types compiled from source. Is the source glob too strict?",
+            typeName));
+      }
       errorsFound = true;
       return null;
     }
@@ -1307,10 +1364,16 @@
       return nameBasedTypeLocator.getResolvedType(typeName);
     }
 
-    logger.log(TreeLogger.ERROR, String.format(
-        "Could not find %s in types compiled from source or in provided dependency libraries. "
-        + "Either the source file was unavailable, failed to compile or there is a missing "
-        + "dependency.", typeName));
+    if (nameBasedTypeLocator.hasCompileErrors(typeName)) {
+      TreeLogger branch = logger.branch(TreeLogger.ERROR, String.format(
+          "Type %s could not be referenced because it previously failed to compile with errors:"));
+      nameBasedTypeLocator.logErrorTrace(branch, TreeLogger.ERROR, typeName);
+    } else {
+      logger.log(TreeLogger.ERROR, String.format(
+          "Could not find %s in types compiled from source or in provided dependency libraries. "
+              + "Either the source file was unavailable or there is a missing dependency.",
+          typeName));
+    }
     errorsFound = true;
     return null;
   }
diff --git a/dev/core/test/com/google/gwt/dev/IncrementalBuilderTest.java b/dev/core/test/com/google/gwt/dev/IncrementalBuilderTest.java
index 61be4c1..507d7d9 100644
--- a/dev/core/test/com/google/gwt/dev/IncrementalBuilderTest.java
+++ b/dev/core/test/com/google/gwt/dev/IncrementalBuilderTest.java
@@ -33,6 +33,7 @@
 import java.net.URL;
 import java.util.Arrays;
 import java.util.List;
+import java.util.regex.Pattern;
 
 /**
  * Test for {@link IncrementalBuilder}.
@@ -219,6 +220,61 @@
         duplicateCompilationUnitError);
   }
 
+  public void testEntryPointWithFailedCrossModuleTypeReference() throws MalformedURLException {
+    String victimTypeName =
+        "com.google.gwt.dev.testdata.incrementalbuildsystem.EntryPointCompileFails";
+
+    UnitTestTreeLogger.Builder loggerBuilder = new UnitTestTreeLogger.Builder();
+    loggerBuilder.setLowestLogLevel(TreeLogger.INFO);
+    loggerBuilder.expectInfo("Tracing compile failure path for type '" + victimTypeName + "'",
+        null);
+    loggerBuilder.expectError(Pattern.compile("Errors in '.*EntryPointCompileFails\\.java'"), null);
+    loggerBuilder.expectError("Line 24: ImmediateCompileFails cannot be resolved to a type", null);
+    loggerBuilder.expectError(Pattern.compile("Errors in '.*ImmediateCompileFails\\.java'"), null);
+    loggerBuilder.expectError("Line 20: List cannot be resolved to a type", null);
+    UnitTestTreeLogger testLogger = loggerBuilder.createLogger();
+
+    IncrementalBuilder incrementalBuilder = createIncrementalBuilder(
+        "com.google.gwt.dev.testdata.incrementalbuildsystem.CrossModuleCompileFails",
+        createGwtClassPathResourceLoader());
+    incrementalBuilder.clean();
+
+    boolean buildSucceeded = incrementalBuilder.build(testLogger).isSuccess();
+    assertFalse(buildSucceeded);
+    testLogger.assertLogEntriesContainExpected();
+  }
+
+  public void testEntryPointWithFailedSameModuleTypeReference() throws MalformedURLException {
+    String victimTypeName =
+        "com.google.gwt.dev.testdata.incrementalbuildsystem.EntryPointCompileFails";
+    String causeTypeName =
+        "com.google.gwt.dev.testdata.incrementalbuildsystem.ImmediateCompileFails";
+
+    UnitTestTreeLogger.Builder loggerBuilder = new UnitTestTreeLogger.Builder();
+    loggerBuilder.setLowestLogLevel(TreeLogger.INFO);
+    loggerBuilder.expectInfo("Tracing compile failure path for type '" + victimTypeName + "'",
+        null);
+    loggerBuilder.expectError(Pattern.compile("Errors in '.*EntryPointCompileFails\\.java'"), null);
+    loggerBuilder.expectError(causeTypeName + " cannot be resolved to a type", null);
+    loggerBuilder.expectError(Pattern.compile("Errors in '.*ImmediateCompileFails\\.java'"), null);
+    loggerBuilder.expectError("Line 20: List cannot be resolved to a type", null);
+    UnitTestTreeLogger testLogger = loggerBuilder.createLogger();
+
+    IncrementalBuilder incrementalBuilder = createIncrementalBuilder(
+        "com.google.gwt.dev.testdata.incrementalbuildsystem.SameModuleCompileFails",
+        createGwtClassPathResourceLoader());
+    incrementalBuilder.clean();
+
+    boolean buildSucceeded = incrementalBuilder.build(testLogger).isSuccess();
+    assertFalse(buildSucceeded);
+    testLogger.assertLogEntriesContainExpected();
+  }
+
+  public void testIgnoresCompileFailuresInUnreachableTypes() throws MalformedURLException {
+    assertBuildResult(
+        "com.google.gwt.dev.testdata.incrementalbuildsystem.UnreachableTypeCompileFails", true);
+  }
+
   public void testUnableToFindModule() throws MalformedURLException {
     String unableToFindModuleMessage = ModuleDefLoader.formatUnableToFindModuleMessage(
         "com/google/gwt/dev/testdata/incrementalbuildsystem/NoSuchModule.gwt.xml");
diff --git a/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java b/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java
index dc150fe..7349f22 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/MockLibrary.java
@@ -14,6 +14,7 @@
 package com.google.gwt.dev.cfg;
 
 import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.javac.CompilationErrorsIndexImpl;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.javac.CompiledClass;
 import com.google.gwt.dev.jjs.PermutationResult;
@@ -59,6 +60,7 @@
   }
 
   private Set<String> buildResourcePaths = Sets.newHashSet();
+  private CompilationErrorsIndexImpl compilationErrorsIndex = new CompilationErrorsIndexImpl();
   private Multimap<String, String> compilationUnitNamesByNestedBinaryName = HashMultimap.create();
   private Multimap<String, String> compilationUnitNamesByNestedSourceName = HashMultimap.create();
   private Map<String, CompilationUnit> compilationUnitsByTypeName = Maps.newHashMap();
@@ -133,6 +135,11 @@
   }
 
   @Override
+  public CompilationErrorsIndexImpl getCompilationErrorsIndex() {
+    return compilationErrorsIndex;
+  }
+
+  @Override
   public CompilationUnit getCompilationUnitByTypeBinaryName(String typeBinaryName) {
     // Convert nested binary name to enclosing type source name.
     String typeSourceName =
@@ -193,6 +200,11 @@
   }
 
   @Override
+  public Set<String> getReboundTypeSourceNames() {
+    return reboundTypeNames;
+  }
+
+  @Override
   public Set<String> getRegularClassFilePaths() {
     return null;
   }
@@ -203,11 +215,6 @@
   }
 
   @Override
-  public Set<String> getReboundTypeSourceNames() {
-    return reboundTypeNames;
-  }
-
-  @Override
   public Set<String> getSuperSourceClassFilePaths() {
     return null;
   }
diff --git a/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java b/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java
index f998354..7bce545 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/MockLibraryWriter.java
@@ -14,6 +14,7 @@
 package com.google.gwt.dev.cfg;
 
 import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.javac.CompilationErrorsIndex;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.jjs.PermutationResult;
 import com.google.gwt.dev.resource.Resource;
@@ -102,6 +103,10 @@
   }
 
   @Override
+  public void setCompilationErrorsIndex(CompilationErrorsIndex compilationErrorsIndex) {
+  }
+
+  @Override
   public void setLibraryName(String libraryName) {
     this.libraryName = libraryName;
   }
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java
index 5de014f..d28ac61 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefLoaderTest.java
@@ -69,33 +69,28 @@
     assertHonorsStrictResources(false);
   }
 
-  public void testErrorReporting_badXml() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_badXml() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadModule",
         "Line 3, column 1 : Element type \"inherits\" must be followed by either "
             + "attribute specifications, \">\" or \"/>\".");
   }
 
-  public void testErrorReporting_badLinker() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_badLinker() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadLinker",
         "Line 2: Invalid linker name 'X'");
   }
 
-  public void testErrorReporting_badProperty() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_badProperty() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadProperty",
         "Line 2: Property 'X' not found");
   }
 
-  public void testErrorReporting_badPropertyValue() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_badPropertyValue() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.BadPropertyValue",
         "Line 3: Value 'z' in not a valid value for property 'X'");
   }
 
-  public void testErrorReporting_deepError() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_deepError() {
     UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder();
     builder.setLowestLogLevel(TreeLogger.DEBUG);
     builder.expectDebug(
@@ -121,34 +116,29 @@
     logger.assertLogEntriesContainExpected();
   }
 
-  public void testErrorReporting_inheritNotFound() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_inheritNotFound() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.InheritNotFound",
         "Unable to find 'com/google/gwt/dev/cfg/testdata/NonExistentModule.gwt.xml' on your "
             + "classpath; could be a typo, or maybe you forgot to include a classpath entry "
             + "for source?");
   }
 
-  public void testErrorReporting_invalidName() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_invalidName() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.InvalidName",
         "Line 2: Invalid property name '123:33'");
   }
 
-  public void testErrorReporting_multipleErrors() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_multipleErrors() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.MultipleErrors",
         "Line 1: Unexpected attribute 'blah' in element 'module'");
   }
 
-  public void testErrorReporting_unexpectedAttribute() throws UnableToCompleteException,
-      IOException, IncompatibleLibraryVersionException {
+  public void testErrorReporting_unexpectedAttribute() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.UnexpectedAttribute",
         "Line 2: Unexpected attribute 'blah' in element 'inherits'");
   }
 
-  public void testErrorReporting_unexpectedTag() throws UnableToCompleteException, IOException,
-      IncompatibleLibraryVersionException {
+  public void testErrorReporting_unexpectedTag() {
     assertErrorsWhenLoading("com.google.gwt.dev.cfg.testdata.errors.UnexpectedTag",
         "Line 2: Unexpected element 'inherited'");
   }
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java b/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java
index 3f919ca..b2792f1 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/ZipLibrariesTest.java
@@ -14,6 +14,7 @@
 package com.google.gwt.dev.cfg;
 
 import com.google.gwt.dev.cfg.Libraries.IncompatibleLibraryVersionException;
+import com.google.gwt.dev.javac.CompilationErrorsIndexImpl;
 import com.google.gwt.dev.javac.CompilationStateTestBase;
 import com.google.gwt.dev.javac.CompilationUnit;
 import com.google.gwt.dev.javac.JdtCompilerTest;
@@ -92,6 +93,9 @@
     rebuildCompilationState();
     List<CompilationUnit> compilationUnits =
         Lists.newArrayList(state.getCompilationUnitMap().values());
+    CompilationErrorsIndexImpl expectedCompilationErrorsIndex = new CompilationErrorsIndexImpl();
+    expectedCompilationErrorsIndex.add("com.google.Foo", "/project/src/com/google/Foo.java",
+        Lists.newArrayList("com.google.Bang"), Lists.newArrayList("Unresolved type 'Bang'"));
 
     // Put data in the library and save it.
     ZipLibraryWriter zipLibraryWriter = new ZipLibraryWriter(zipFile.getPath());
@@ -109,6 +113,7 @@
     for (CompilationUnit compilationUnit : compilationUnits) {
       zipLibraryWriter.addCompilationUnit(compilationUnit);
     }
+    zipLibraryWriter.setCompilationErrorsIndex(expectedCompilationErrorsIndex);
     zipLibraryWriter.write();
 
     // Read data back from disk.
@@ -147,6 +152,9 @@
     assertTrue(zipLibrary.getNestedBinaryNamesByCompilationUnitName().get(
         JdtCompilerTest.OUTER_INNER.getTypeName()).contains(
         JdtCompilerTest.OUTER_INNER.getTypeName() + "$Inner"));
+
+    // The reloaded compilation errors index contains the same data as before writing to disk.
+    assertEquals(expectedCompilationErrorsIndex, zipLibrary.getCompilationErrorsIndex());
   }
 
   public void testVersionNumberException() throws IOException {
diff --git a/dev/core/test/com/google/gwt/dev/javac/CompilationUnitInvalidatorTest.java b/dev/core/test/com/google/gwt/dev/javac/CompilationUnitInvalidatorTest.java
index b01ec86..cd43d4c 100644
--- a/dev/core/test/com/google/gwt/dev/javac/CompilationUnitInvalidatorTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/CompilationUnitInvalidatorTest.java
@@ -16,6 +16,8 @@
 package com.google.gwt.dev.javac;
 
 import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.CompilerContext;
+import com.google.gwt.dev.util.UnitTestTreeLogger;
 
 import junit.framework.TestCase;
 
@@ -82,8 +84,27 @@
     Map<String, CompiledClass> knownValidClasses =
         new HashMap<String, CompiledClass>(validClasses);
 
+    // Collect more compilation errors where extra units are thrown out by unit invalidation.
+    CompilerContext compilerContext = new CompilerContext.Builder().build();
+
     // Invoke the method under test
-    CompilationUnitInvalidator.retainValidUnits(TreeLogger.NULL, units, validClasses);
+    CompilationUnitInvalidator.retainValidUnits(TreeLogger.NULL, units, validClasses,
+        compilerContext.getLocalCompilationErrorsIndex());
+
+    // Check that the compilation errors index was correctly populated.
+    UnitTestTreeLogger.Builder loggerBuilder = new UnitTestTreeLogger.Builder();
+    loggerBuilder.setLowestLogLevel(TreeLogger.INFO);
+    loggerBuilder.expectInfo("Tracing compile failure path for type 'bad6'", null);
+    loggerBuilder.expectError("Errors in '/mock/bad6.java'", null);
+    loggerBuilder.expectError("bad5 cannot be resolved to a type", null);
+    loggerBuilder.expectError("Errors in '/mock/bad5.java'", null);
+    loggerBuilder.expectError("bad3 cannot be resolved to a type", null);
+    loggerBuilder.expectError("Errors in '/mock/bad3.java'", null);
+    loggerBuilder.expectError("bad1 cannot be resolved to a type", null);
+    UnitTestTreeLogger testLogger = loggerBuilder.createLogger();
+    CompilationProblemReporter.logErrorTrace(testLogger, TreeLogger.ERROR, compilerContext, "bad6",
+        false);
+    testLogger.assertCorrectLogEntries();
 
     // Check that validClasses is not mutated
     assertEquals(knownValidClasses, validClasses);
diff --git a/dev/core/test/com/google/gwt/dev/javac/TypeOracleTestingUtils.java b/dev/core/test/com/google/gwt/dev/javac/TypeOracleTestingUtils.java
index e3cfc2e..8465fff 100644
--- a/dev/core/test/com/google/gwt/dev/javac/TypeOracleTestingUtils.java
+++ b/dev/core/test/com/google/gwt/dev/javac/TypeOracleTestingUtils.java
@@ -110,7 +110,7 @@
       Collection<CompilationUnit> oldCompilationUnits =
           Lists.newArrayList(oldState.getCompilationUnits());
       CompilationUnitInvalidator.retainValidUnits(logger, oldCompilationUnits,
-          oldState.getValidClasses());
+          oldState.getValidClasses(), new CompilationErrorsIndexImpl());
       for (CompilationUnit compilationUnit : oldCompilationUnits) {
         oldLibrary.addCompilationUnit(compilationUnit);
       }
diff --git a/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java b/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java
index 483e02f..e2c5fc6 100644
--- a/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java
+++ b/dev/core/test/com/google/gwt/dev/util/UnitTestTreeLogger.java
@@ -22,6 +22,7 @@
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.regex.Pattern;
 
 /**
  * A {@link TreeLogger} implementation that can be used during JUnit tests to
@@ -49,6 +50,11 @@
       expected.add(new LogEntry(type, msg, caught));
     }
 
+    public void expect(TreeLogger.Type type, Pattern msgPattern,
+        Class<? extends Throwable> caught) {
+      expected.add(new LogEntry(type, msgPattern, caught));
+    }
+
     public void expectDebug(String msg, Class<? extends Throwable> caught) {
       expect(TreeLogger.DEBUG, msg, caught);
     }
@@ -57,6 +63,10 @@
       expect(TreeLogger.ERROR, msg, caught);
     }
 
+    public void expectError(Pattern msgPattern, Class<? extends Throwable> caught) {
+      expect(TreeLogger.ERROR, msgPattern, caught);
+    }
+
     public void expectInfo(String msg, Class<? extends Throwable> caught) {
       expect(TreeLogger.INFO, msg, caught);
     }
@@ -98,7 +108,8 @@
    */
   private static class LogEntry {
     private final Class<? extends Throwable> caught;
-    private final String msg;
+    private String msg;
+    private Pattern msgPattern;
     private final Type type;
 
     public LogEntry(TreeLogger.Type type, String msg, Class<? extends Throwable> caught) {
@@ -108,6 +119,13 @@
       this.caught = caught;
     }
 
+    public LogEntry(TreeLogger.Type type, Pattern msgPattern, Class<? extends Throwable> caught) {
+      assert (type != null);
+      this.type = type;
+      this.msgPattern = msgPattern;
+      this.caught = caught;
+    }
+
     public LogEntry(Type type, String msg, Throwable caught) {
       this(type, msg, (caught == null) ? null : caught.getClass());
     }
@@ -120,6 +138,10 @@
       return msg;
     }
 
+    public Pattern getMessagePattern() {
+      return msgPattern;
+    }
+
     public Type getType() {
       return type;
     }
@@ -129,7 +151,11 @@
       StringBuilder sb = new StringBuilder();
       sb.append(type.getLabel());
       sb.append(": ");
-      sb.append(getMessage());
+      if (getMessage() != null) {
+        sb.append(getMessage());
+      } else {
+        sb.append("like " + getMessagePattern().pattern());
+      }
       Class<? extends Throwable> t = getCaught();
       if (t != null) {
         sb.append("; ");
@@ -142,8 +168,14 @@
       if (!type.equals(other.type)) {
         return false;
       }
-      if (!msg.equals(other.msg)) {
-        return false;
+      if (msg != null) {
+        if (!msg.equals(other.msg)) {
+          return false;
+        }
+      } else {
+        if (!getMessagePattern().matcher(other.msg).matches()) {
+          return false;
+        }
       }
       if ((caught == null) != (other.caught == null)) {
         return false;
@@ -157,7 +189,13 @@
 
   private static void assertCorrectLogEntry(LogEntry expected, LogEntry actual) {
     Assert.assertEquals("Log types do not match", expected.getType(), actual.getType());
-    Assert.assertEquals("Log messages do not match", expected.getMessage(), actual.getMessage());
+    if (expected.getMessage() != null) {
+      Assert.assertEquals("Log messages do not match", expected.getMessage(), actual.getMessage());
+    } else {
+      Assert.assertTrue("Log message '" + actual.getMessage() + "' does not match pattern "
+          + expected.getMessagePattern().pattern(),
+          expected.getMessagePattern().matcher(actual.getMessage()).matches());
+    }
     if (expected.getCaught() == null) {
       Assert.assertNull("Actual log exception type should have been null", actual.getCaught());
     } else {
diff --git a/user/BUILD b/user/BUILD
index 1c603a1..e16989d 100644
--- a/user/BUILD
+++ b/user/BUILD
@@ -158,7 +158,7 @@
     srcs = [],
     added_root_globs = {
         "test": [
-            "test/com/google/gwt/dev/testdata/incrementalbuildsystem/*",
+            "test/com/google/gwt/dev/testdata/incrementalbuildsystem/**/*",
         ],
     },
     # We only need the Java classes so don't wait for the rest of :gwt-dev.
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java
index efe77b1..aa1d480 100644
--- a/user/src/com/google/gwt/junit/JUnitShell.java
+++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -762,7 +762,8 @@
       errMsg = "The test class '" + typeName + "' was not found in module '"
           + moduleName + "'; no compilation unit for that type was seen";
     } else {
-      CompilationProblemReporter.logMissingTypeErrorWithHints(logger, typeName, compilationState);
+      CompilationProblemReporter.logErrorTrace(logger, TreeLogger.ERROR,
+          compilationState.getCompilerContext(), typeName, true);
       errMsg = "The test class '" + typeName
           + "' had compile errors; check log for details";
     }
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CrossModuleCompileFails.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CrossModuleCompileFails.gwt.xml
new file mode 100644
index 0000000..94e9a3c
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/CrossModuleCompileFails.gwt.xml
@@ -0,0 +1,5 @@
+<module>
+  <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.UnreachableTypeCompileFails"/>
+  <source path="" includes="EntryPointCompileFails.java" />
+  <entry-point class="com.google.gwt.dev.testdata.incrementalbuildsystem.EntryPointCompileFails"/>
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/EntryPointCompileFails.java b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/EntryPointCompileFails.java
new file mode 100644
index 0000000..6b137a2
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/EntryPointCompileFails.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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.gwt.dev.testdata.incrementalbuildsystem;
+
+import com.google.gwt.core.client.EntryPoint;
+
+/**
+ * An entry point test class that should immediately fail to compile because it references a type
+ * that fails to compile.
+ */
+public class EntryPointCompileFails implements EntryPoint {
+
+  public ImmediateCompileFails immediateCompileFails;
+
+  @Override
+  public void onModuleLoad() {
+    immediateCompileFails = new ImmediateCompileFails();
+  }
+}
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ImmediateCompileFails.java b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ImmediateCompileFails.java
new file mode 100644
index 0000000..2e0d3a2
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/ImmediateCompileFails.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 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.gwt.dev.testdata.incrementalbuildsystem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A test class that should immediately fail to compile because of missing imports.
+ *
+ * The actual failing version is in /super
+ */
+public class ImmediateCompileFails {
+  List<String> strings = new ArrayList<String>();
+}
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/MultipleFailTop.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/MultipleFailTop.gwt.xml
index 6b6e6b2..8315b0b 100644
--- a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/MultipleFailTop.gwt.xml
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/MultipleFailTop.gwt.xml
@@ -1,4 +1,6 @@
 <module>
   <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.MultipleFailLeft" />
   <inherits name="com.google.gwt.dev.testdata.incrementalbuildsystem.MultipleFailRight" />
+  <source path="" includes="EntryPointCompileFails.java" />
+  <super-source path="super" />
 </module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SameModuleCompileFails.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SameModuleCompileFails.gwt.xml
new file mode 100644
index 0000000..bafe7d5
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/SameModuleCompileFails.gwt.xml
@@ -0,0 +1,5 @@
+<module>
+  <source path="" includes="EntryPointCompileFails.java" />
+  <super-source path="super" />
+  <entry-point class="com.google.gwt.dev.testdata.incrementalbuildsystem.EntryPointCompileFails"/>
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/UnreachableTypeCompileFails.gwt.xml b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/UnreachableTypeCompileFails.gwt.xml
new file mode 100644
index 0000000..df669a8
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/UnreachableTypeCompileFails.gwt.xml
@@ -0,0 +1,4 @@
+<module>
+  <source path="" includes="DoesNotExist.baja" />
+  <super-source path="super" />
+</module>
diff --git a/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/super/com/google/gwt/dev/testdata/incrementalbuildsystem/ImmediateCompileFails.java b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/super/com/google/gwt/dev/testdata/incrementalbuildsystem/ImmediateCompileFails.java
new file mode 100644
index 0000000..934eef8
--- /dev/null
+++ b/user/test/com/google/gwt/dev/testdata/incrementalbuildsystem/super/com/google/gwt/dev/testdata/incrementalbuildsystem/ImmediateCompileFails.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 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.gwt.dev.testdata.incrementalbuildsystem;
+
+/**
+ * A test class that should immediately fail to compile because of missing imports.
+ */
+public class ImmediateCompileFails {
+  List<String> strings = new ArrayList<String>();
+}