Removes synthetic methods from JsInterop collision checks.

Recently added JsInterop name collision checks were firing on a lot of
legal circumstances where compiler generated synthetic methods (like
override bridge methods with different type signatures) had duplicate
names. Since this error is not the users fault and can be automatically
handled in JS generation it makes sense not to report it.

Change-Id: I72c8cd94f58197d5e80a9552df9268b186849793
diff --git a/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
index 76e210f..965c8fc 100644
--- a/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
+++ b/dev/core/src/com/google/gwt/dev/MinimalRebuildCache.java
@@ -172,7 +172,6 @@
   private final JsIncrementalNamerState jsIncrementalNamerState = new JsIncrementalNamerState();
   private final Set<String> jsoStatusChangedTypeNames = Sets.newHashSet();
   private final Set<String> jsoTypeNames = Sets.newHashSet();
-  private final Multimap<String, String> jsTypeMemberNamesByTypeName = HashMultimap.create();
   private Integer lastLinkedJsBytes;
   private final Map<String, Long> lastModifiedByDiskSourcePath = Maps.newHashMap();
   private final Map<String, Long> lastModifiedByResourcePath = Maps.newHashMap();
@@ -211,10 +210,6 @@
     this.generatedArtifacts.addAll(generatedArtifacts);
   }
 
-  public boolean addJsTypeMemberName(String exportedMemberName, String inTypeName) {
-    return jsTypeMemberNamesByTypeName.put(inTypeName, exportedMemberName);
-  }
-
   public void addModifiedCompilationUnitNames(TreeLogger logger,
       Set<String> modifiedCompilationUnitNames) {
     logger.log(TreeLogger.DEBUG, "adding to cached list of known modified compilation units "
@@ -479,7 +474,6 @@
     copyMap(that.statementRangesByTypeName, this.statementRangesByTypeName);
 
     copyMultimap(that.exportedGlobalNamesByTypeName, this.exportedGlobalNamesByTypeName);
-    copyMultimap(that.jsTypeMemberNamesByTypeName, this.jsTypeMemberNamesByTypeName);
     copyMultimap(that.generatedCompilationUnitNamesByReboundTypeNames,
         this.generatedCompilationUnitNamesByReboundTypeNames);
     copyMultimap(that.nestedTypeNamesByUnitTypeName, this.nestedTypeNamesByUnitTypeName);
@@ -688,7 +682,6 @@
   }
 
   public void removeJsInteropNames(String inTypeName) {
-    jsTypeMemberNamesByTypeName.removeAll(inTypeName);
     Collection<String> exportedGlobalNamesForType =
         exportedGlobalNamesByTypeName.removeAll(inTypeName);
     exportedGlobalNames.removeAll(exportedGlobalNamesForType);
@@ -779,7 +772,6 @@
         && Objects.equal(this.generatedArtifacts, that.generatedArtifacts)
         && Objects.equal(this.exportedGlobalNames, that.exportedGlobalNames)
         && Objects.equal(this.exportedGlobalNamesByTypeName, that.exportedGlobalNamesByTypeName)
-        && Objects.equal(this.jsTypeMemberNamesByTypeName, that.jsTypeMemberNamesByTypeName)
         && Objects.equal(this.generatedCompilationUnitNamesByReboundTypeNames,
             that.generatedCompilationUnitNamesByReboundTypeNames)
         && this.intTypeMapper.hasSameContent(that.intTypeMapper)
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 0ac9629..b810303 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
@@ -23,6 +23,9 @@
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
+
+import java.util.Set;
 
 /**
  * Checks and throws errors for invalid JsInterop constructs.
@@ -45,6 +48,7 @@
     }
   }
 
+  private final Set<String> currentJsTypeMemberNames = Sets.newHashSet();
   private JDeclaredType currentType;
   private final JProgram jprogram;
   private final TreeLogger logger;
@@ -61,11 +65,13 @@
   public void endVisit(JDeclaredType x, Context ctx) {
     assert currentType == x;
     currentType = null;
+    currentJsTypeMemberNames.clear();
   }
 
   @Override
   public boolean visit(JDeclaredType x, Context ctx) {
     assert currentType == null;
+    assert currentJsTypeMemberNames.isEmpty();
     minimalRebuildCache.removeJsInteropNames(x.getName());
     currentType = x;
 
@@ -84,7 +90,7 @@
         throw new UnsupportedOperationException();
       }
     } else if (jprogram.typeOracle.isJsTypeField(x)) {
-      boolean success = minimalRebuildCache.addJsTypeMemberName(x.getName(), currentType.getName());
+      boolean success = addJsTypeMemberName(x.getName());
       if (!success) {
         logger.log(TreeLogger.ERROR, String.format(
             "Instance field '%s' can't be exported because the member name '%s' is already taken.",
@@ -107,11 +113,20 @@
             computeReadableSignature(x), x.getQualifiedExportName()));
         throw new UnsupportedOperationException();
       }
-    } else if (jprogram.typeOracle.isJsTypeMethod(x) && isDirectOrTransitiveJsProperty(x)) {
-      // JsProperty methods are mangled and obfuscated and so do not consume an unobfuscated
-      // collidable name slot.
     } else if (jprogram.typeOracle.isJsTypeMethod(x)) {
-      boolean success = minimalRebuildCache.addJsTypeMemberName(x.getName(), currentType.getName());
+      if (isDirectOrTransitiveJsProperty(x)) {
+        // JsProperty methods are mangled and obfuscated and so do not consume an unobfuscated
+        // collidable name slot.
+        return true;
+      } else if (x.isSynthetic()) {
+        // A name slot taken up by a synthetic method, such as a bridge method for a generic method,
+        // is not the fault of the user and so should not be reported as an error. JS generation
+        // should take responsibility for ensuring that only the correct method version (in this
+        // particular set of colliding method names) is exported.
+        return true;
+      }
+
+      boolean success = addJsTypeMemberName(x.getName());
       if (!success) {
         logger.log(TreeLogger.ERROR, String.format(
             "Instance method '%s' can't be exported because the member name '%s' is already taken.",
@@ -123,6 +138,10 @@
     return true;
   }
 
+  private boolean addJsTypeMemberName(String exportedMemberName) {
+    return currentJsTypeMemberNames.add(exportedMemberName);
+  }
+
   private String computeReadableSignature(JField field) {
     return field.getEnclosingType().getName() + "." + field.getName();
   }
diff --git a/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java b/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java
index 7b9d0b6..c352f94 100644
--- a/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java
+++ b/dev/core/test/com/google/gwt/dev/MinimalRebuildCacheManagerTest.java
@@ -87,7 +87,6 @@
     startingCache.computeAndClearStaleTypesCache(TreeLogger.NULL,
         new JTypeOracle(null, startingCache, true));
     startingCache.addExportedGlobalName("alert", "Window");
-    startingCache.addJsTypeMemberName("length", "String");
 
     // 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 46efe15..64a7ac1 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
@@ -96,6 +96,22 @@
     assertCompileFails();
   }
 
+  public void testCollidingSyntheticBridgeMethodSucceeds() throws Exception {
+    addSnippetImport("com.google.gwt.core.client.js.JsType");
+    addSnippetImport("com.google.gwt.core.client.js.JsProperty");
+    addSnippetClassDecl(
+        "public static interface Comparable<T> {",
+        "  int compareTo(T other);",
+        "}",
+        "@JsType",
+        "public static class Enum<E extends Enum<E>> implements Comparable<E> {",
+        "  public int compareTo(E other) {return 0;}",
+        "}",
+        "public static class Buggy {}");
+
+    assertCompileSucceeds();
+  }
+
   public void testSingleExportSucceeds() throws Exception {
     addSnippetImport("com.google.gwt.core.client.js.JsExport");
     addSnippetClassDecl(