Give better error messages when there are global name collisions.

Change-Id: Ida19497d8158a80d4509b3ef65be9511e7bea521
diff --git a/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
index e8a0c03..4fb23b7 100644
--- a/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
+++ b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
@@ -158,7 +158,7 @@
   private final Set<String> deletedDiskSourcePaths = Sets.newHashSet();
   private final Set<String> deletedResourcePaths = Sets.newHashSet();
   private final Set<String> dualJsoImplInterfaceNames = Sets.newHashSet();
-  private final Set<String> exportedGlobalNames = Sets.newHashSet();
+  private final Map<String, String> descriptionByexportedGlobalNames = Maps.newHashMap();
   private final Multimap<String, String> exportedGlobalNamesByTypeName = HashMultimap.create();
   private final ArtifactSet generatedArtifacts = new ArtifactSet();
   private final Multimap<String, String> generatedCompilationUnitNamesByReboundTypeNames =
@@ -192,9 +192,10 @@
   private StringAnalyzableTypeEnvironment typeEnvironment = new StringAnalyzableTypeEnvironment();
   private final Multimap<String, String> typeNamesByReferencingTypeName = HashMultimap.create();
 
-  public boolean addExportedGlobalName(String exportedGlobalName, String inTypeName) {
+  public String addExportedGlobalName(
+      String exportedGlobalName, String description, String inTypeName) {
     exportedGlobalNamesByTypeName.put(inTypeName, exportedGlobalName);
-    return exportedGlobalNames.add(exportedGlobalName);
+    return descriptionByexportedGlobalNames.put(exportedGlobalName, description);
   }
 
   /**
@@ -462,6 +463,7 @@
     copyMap(that.compilationUnitTypeNameByNestedTypeName,
         this.compilationUnitTypeNameByNestedTypeName);
     copyMap(that.contentHashByGeneratedTypeName, this.contentHashByGeneratedTypeName);
+    copyMap(that.descriptionByexportedGlobalNames, this.descriptionByexportedGlobalNames);
     copyMap(that.jsByTypeName, this.jsByTypeName);
     copyMap(that.lastModifiedByDiskSourcePath, this.lastModifiedByDiskSourcePath);
     copyMap(that.lastModifiedByResourcePath, this.lastModifiedByResourcePath);
@@ -483,7 +485,6 @@
     copyCollection(that.deletedDiskSourcePaths, this.deletedDiskSourcePaths);
     copyCollection(that.deletedResourcePaths, this.deletedResourcePaths);
     copyCollection(that.dualJsoImplInterfaceNames, this.dualJsoImplInterfaceNames);
-    copyCollection(that.exportedGlobalNames, this.exportedGlobalNames);
     copyCollection(that.generatedArtifacts, this.generatedArtifacts);
     copyCollection(that.jsoStatusChangedTypeNames, this.jsoStatusChangedTypeNames);
     copyCollection(that.jsoTypeNames, this.jsoTypeNames);
@@ -679,7 +680,7 @@
   public void removeExportedNames(String inTypeName) {
     Collection<String> exportedGlobalNamesForType =
         exportedGlobalNamesByTypeName.removeAll(inTypeName);
-    exportedGlobalNames.removeAll(exportedGlobalNamesForType);
+    descriptionByexportedGlobalNames.keySet().removeAll(exportedGlobalNamesForType);
   }
 
   public void removeReferencesFrom(String fromTypeName) {
@@ -765,7 +766,8 @@
         && Objects.equal(this.deletedResourcePaths, that.deletedResourcePaths)
         && Objects.equal(this.dualJsoImplInterfaceNames, that.dualJsoImplInterfaceNames)
         && Objects.equal(this.generatedArtifacts, that.generatedArtifacts)
-        && Objects.equal(this.exportedGlobalNames, that.exportedGlobalNames)
+        && Objects.equal(this.descriptionByexportedGlobalNames,
+            that.descriptionByexportedGlobalNames)
         && Objects.equal(this.exportedGlobalNamesByTypeName, that.exportedGlobalNamesByTypeName)
         && Objects.equal(this.generatedCompilationUnitNamesByReboundTypeNames,
             that.generatedCompilationUnitNamesByReboundTypeNames)
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
index 428fa62..44c854b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
@@ -273,10 +273,13 @@
   }
 
   private void checkGlobalName(JMember member) {
-    if (!minimalRebuildCache.addExportedGlobalName(member.getQualifiedJsName(),
-        member.getEnclosingType().getName())) {
-      logError(member, "%s cannot be exported because the global name '%s' is already taken.",
-          getMemberDescription(member), member.getQualifiedJsName());
+   String currentGlobalNameDescription = minimalRebuildCache.addExportedGlobalName(
+       member.getQualifiedJsName(), JjsUtils.getReadableDescription(member),
+       member.getEnclosingType().getName());
+    if (currentGlobalNameDescription != null) {
+      logError(member,
+          "%s cannot be exported because the global name '%s' is already taken by '%s'.",
+          getMemberDescription(member), member.getQualifiedJsName(), currentGlobalNameDescription);
     }
   }
 
@@ -288,8 +291,8 @@
             getMemberDescription(member));
       } else {
         logError(
-            member, "%s cannot be assigned a different JavaScript name than the method it overrides.",
-            getMemberDescription(member));
+            member, "%s cannot be assigned a different JavaScript name than the method "
+                + "it overrides.", getMemberDescription(member));
       }
       return;
     }
@@ -328,7 +331,7 @@
   private void checkJsPropertyGetterConsistentWithSetter(
       JType type, JMethod setter, JMethod getter) {
     if (setter.getParams().size() == 1
-        &&  getter.getType() != setter.getParams().get(0).getType()) {
+        && getter.getType() != setter.getParams().get(0).getType()) {
       logError(setter,
           "The setter and getter for JsProperty '%s' in type '%s' must have consistent types.",
           setter.getJsName(), JjsUtils.getReadableDescription(type));
diff --git a/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java b/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java
index 52ff5ec..c0ab6b0 100644
--- a/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java
+++ b/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java
@@ -86,7 +86,7 @@
     startingCache.computeReachableTypeNames();
     startingCache.computeAndClearStaleTypesCache(TreeLogger.NULL,
         new JTypeOracle(null, startingCache));
-    startingCache.addExportedGlobalName("alert", "Window");
+    startingCache.addExportedGlobalName("alert", "Window", "void Widow.alert()");
 
     // Save and reload the cache.
     minimalRebuildCacheManager.putCache(moduleName, permutationDescription, startingCache);
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
index b9699ae..84aedbe 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
@@ -116,7 +116,8 @@
 
     assertBuggyFails(
         "Line 8: 'int EntryPoint.Buggy.display' cannot be exported because the global "
-            + "name 'test.EntryPoint.Buggy.show' is already taken.");
+            + "name 'test.EntryPoint.Buggy.show' is already taken by "
+            + "'int EntryPoint.Buggy.show'.");
   }
 
   public void testJsPropertyGetterStyleSucceeds() throws Exception {
@@ -275,7 +276,8 @@
 
     assertBuggyFails(
         "Line 8: 'void EntryPoint.Buggy.display()' cannot be exported because the global name "
-            + "'test.EntryPoint.Buggy.show' is already taken.");
+            + "'test.EntryPoint.Buggy.show' is already taken "
+            + "by 'void EntryPoint.Buggy.show()'.");
   }
 
   public void testCollidingMethodToFieldExportsFails() throws Exception {
@@ -291,7 +293,8 @@
 
     assertBuggyFails(
         "Line 7: 'void EntryPoint.Buggy.show()' cannot be exported because the global name "
-            + "'test.EntryPoint.Buggy.show' is already taken.");
+            + "'test.EntryPoint.Buggy.show' is already taken by "
+            + "'int EntryPoint.Buggy.show'.");
   }
 
   public void testCollidingMethodToFieldJsTypeFails() throws Exception {
@@ -862,7 +865,8 @@
     assertBuggyFails(
         "Line 5: More than one JsConstructor exists for EntryPoint.Buggy.",
         "Line 7: 'EntryPoint.Buggy.EntryPoint$Buggy(int)' cannot be exported because the global "
-            + "name 'test.EntryPoint.Buggy' is already taken.");
+            + "name 'test.EntryPoint.Buggy' is already taken by "
+            + "'EntryPoint.Buggy.EntryPoint$Buggy()'.");
   }
 
   public void testNonCollidingAccidentalOverrideSucceeds() throws Exception {