Fix JSNIRestrictionChecker error messages.

Bug: #9356
Bug-Link: http://github.com/gwtproject/gwt/issues/9356
Change-Id: Ifbb582be9612e323a1a2e6e72a06bdcf813a9f1d
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/AbstractRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/AbstractRestrictionChecker.java
new file mode 100644
index 0000000..6dafe98
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/AbstractRestrictionChecker.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 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.jjs.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.dev.jjs.HasSourceInfo;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMember;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
+import com.google.gwt.thirdparty.guava.common.collect.Multimap;
+import com.google.gwt.thirdparty.guava.common.collect.Ordering;
+import com.google.gwt.thirdparty.guava.common.collect.Sets;
+import com.google.gwt.thirdparty.guava.common.collect.TreeMultimap;
+
+import java.util.TreeSet;
+
+/**
+ * Abstract base class for error checking in the full AST.
+ */
+public abstract class AbstractRestrictionChecker {
+  private Multimap<String, String> errorsByFilename
+      = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
+  private Multimap<String, String> warningsByFilename
+      = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
+
+  protected static String getDescription(HasSourceInfo hasSourceInfo) {
+    if (hasSourceInfo instanceof JDeclaredType) {
+      return getTypeDescription((JDeclaredType) hasSourceInfo);
+    } else {
+      return getMemberDescription((JMember) hasSourceInfo);
+    }
+  }
+
+  protected static String getMemberDescription(JMember member) {
+    if (member instanceof JField) {
+      return String.format("'%s'", JjsUtils.getReadableDescription(member));
+    }
+    JMethod method = (JMethod) member;
+    if ((method.isSyntheticAccidentalOverride() || method.isSynthetic())
+        // Some synthetic methods are created by JDT, it is not safe to assume
+        // that they will always be overriding and crash the compiler.
+        && !method.getOverriddenMethods().isEmpty()) {
+      JMethod overridenMethod = Iterables.getFirst(method.getOverriddenMethods(), null);
+      return String.format("'%s' (exposed by '%s')",
+          JjsUtils.getReadableDescription(overridenMethod),
+          JjsUtils.getReadableDescription(method.getEnclosingType()));
+    }
+    return String.format("'%s'", JjsUtils.getReadableDescription(method));
+  }
+
+  private static String getTypeDescription(JDeclaredType type) {
+    return String.format("'%s'", JjsUtils.getReadableDescription(type));
+  }
+
+  protected void logError(String format, JType type) {
+    logError(type, format, JjsUtils.getReadableDescription(type));
+  }
+
+  protected void logError(HasSourceInfo hasSourceInfo, String format, Object... args) {
+    errorsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
+        String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
+            + String.format(format, args));
+  }
+
+  protected void logWarning(HasSourceInfo hasSourceInfo, String format, Object... args) {
+    warningsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
+        String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
+            + String.format(format, args));
+  }
+
+  protected boolean reportErrorsAndWarnings(TreeLogger logger) {
+    TreeSet<String> filenamesToReport = Sets.newTreeSet(
+        Iterables.concat(errorsByFilename.keySet(), warningsByFilename.keySet()));
+    for (String fileName : filenamesToReport) {
+      boolean hasErrors = !errorsByFilename.get(fileName).isEmpty();
+      TreeLogger branch = logger.branch(
+         Type.INFO, (hasErrors ? "Errors" : "Warnings") + " in " + fileName);
+      for (String message : errorsByFilename.get(fileName)) {
+        branch.log(Type.ERROR, message);
+      }
+      for (String message :warningsByFilename.get(fileName)) {
+        branch.log(Type.WARN, message);
+      }
+    }
+    return !errorsByFilename.isEmpty();
+  }
+}
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 a781602..1519ca6 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
@@ -14,7 +14,6 @@
 package com.google.gwt.dev.jjs.impl;
 
 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.MinimalRebuildCache;
 import com.google.gwt.dev.javac.JsInteropUtil;
@@ -43,7 +42,6 @@
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JStatement;
-import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
 import com.google.gwt.dev.js.JsUtils;
@@ -53,25 +51,19 @@
 import com.google.gwt.dev.js.ast.JsParameter;
 import com.google.gwt.dev.js.ast.JsVisitor;
 import com.google.gwt.dev.util.Pair;
-import com.google.gwt.dev.util.log.AbstractTreeLogger;
 import com.google.gwt.thirdparty.guava.common.base.Predicate;
 import com.google.gwt.thirdparty.guava.common.collect.FluentIterable;
 import com.google.gwt.thirdparty.guava.common.collect.Iterables;
 import com.google.gwt.thirdparty.guava.common.collect.Maps;
-import com.google.gwt.thirdparty.guava.common.collect.Multimap;
-import com.google.gwt.thirdparty.guava.common.collect.Ordering;
-import com.google.gwt.thirdparty.guava.common.collect.Sets;
-import com.google.gwt.thirdparty.guava.common.collect.TreeMultimap;
 
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.TreeSet;
 
 /**
  * Checks and throws errors for invalid JsInterop constructs.
  */
-public class JsInteropRestrictionChecker {
+public class JsInteropRestrictionChecker extends AbstractRestrictionChecker {
 
   public static void exec(TreeLogger logger, JProgram jprogram,
       MinimalRebuildCache minimalRebuildCache) throws UnableToCompleteException {
@@ -83,10 +75,6 @@
     }
   }
 
-  private Multimap<String, String> errorsByFilename
-      = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
-  private Multimap<String, String> warningsByFilename
-      = TreeMultimap.create(Ordering.natural(), AbstractTreeLogger.LOG_LINE_COMPARATOR);
   private final JProgram jprogram;
   private final MinimalRebuildCache minimalRebuildCache;
 
@@ -923,70 +911,8 @@
                .equals(potentiallyOverriddenMethod.getJsniSignature(false, false));
   }
 
-  private static String getDescription(HasSourceInfo hasSourceInfo) {
-    if (hasSourceInfo instanceof JDeclaredType) {
-      return getTypeDescription((JDeclaredType) hasSourceInfo);
-    } else {
-      return getMemberDescription((JMember) hasSourceInfo);
-    }
-  }
-  private static String getMemberDescription(JMember member) {
-    if (member instanceof JField) {
-      return String.format("'%s'", JjsUtils.getReadableDescription(member));
-    }
-    JMethod method = (JMethod) member;
-    if ((method.isSyntheticAccidentalOverride() || method.isSynthetic())
-        // Some synthetic methods are created by JDT, it is not save to assume
-        // that they will always be overriding and crash the compiler.
-        && !method.getOverriddenMethods().isEmpty()) {
-      JMethod overridenMethod = method.getOverriddenMethods().iterator().next();
-      return String.format("'%s' (exposed by '%s')",
-          JjsUtils.getReadableDescription(overridenMethod),
-          JjsUtils.getReadableDescription(method.getEnclosingType()));
-    }
-    return String.format("'%s'", JjsUtils.getReadableDescription(method));
-  }
-
-  private static String getTypeDescription(JDeclaredType type) {
-    return String.format("'%s'", JjsUtils.getReadableDescription(type));
-  }
-
   private boolean isUnusableByJsSuppressed(CanHaveSuppressedWarnings x) {
     return x.getSuppressedWarnings() != null &&
         x.getSuppressedWarnings().contains(JsInteropUtil.UNUSABLE_BY_JS);
   }
-
-  private void logError(String format, JType type) {
-    logError(type, format, JjsUtils.getReadableDescription(type));
-  }
-
-  private void logError(HasSourceInfo hasSourceInfo, String format, Object... args) {
-    errorsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
-        String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
-            + String.format(format, args));
-  }
-
-  private void logWarning(HasSourceInfo hasSourceInfo, String format, Object... args) {
-    warningsByFilename.put(hasSourceInfo.getSourceInfo().getFileName(),
-        String.format("Line %d: ", hasSourceInfo.getSourceInfo().getStartLine())
-            + String.format(format, args));
-  }
-
-  private boolean reportErrorsAndWarnings(TreeLogger logger) {
-    TreeSet<String> filenamesToReport = Sets.newTreeSet(
-        Iterables.concat(errorsByFilename.keySet(), warningsByFilename.keySet()));
-    for (String fileName : filenamesToReport) {
-      boolean hasErrors = !errorsByFilename.get(fileName).isEmpty();
-      TreeLogger branch = logger.branch(
-          hasErrors ? Type.ERROR : Type.WARN,
-          (hasErrors ? "Errors" : "Warnings") + " in " + fileName);
-      for (String message : errorsByFilename.get(fileName)) {
-        branch.log(Type.ERROR, message);
-      }
-      for (String message :warningsByFilename.get(fileName)) {
-        branch.log(Type.WARN, message);
-      }
-    }
-    return !errorsByFilename.isEmpty();
-  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java
index 6ba6624..631213f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsniRestrictionChecker.java
@@ -14,16 +14,14 @@
 package com.google.gwt.dev.jjs.impl;
 
 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.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodBody;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
 import com.google.gwt.thirdparty.guava.common.collect.Sets;
 
@@ -32,32 +30,78 @@
 /**
  * Checks and throws errors for invalid JSNI constructs.
  */
-public class JsniRestrictionChecker extends JVisitor {
+public class JsniRestrictionChecker extends AbstractRestrictionChecker {
 
-  public static void exec(TreeLogger logger, JProgram jprogram) throws UnableToCompleteException {
-    JsniRestrictionChecker jsniRestrictionChecker = new JsniRestrictionChecker(logger, jprogram);
-    jsniRestrictionChecker.accept(jprogram);
-    if (jsniRestrictionChecker.hasErrors) {
+  public static void exec(TreeLogger logger, JProgram program)
+      throws UnableToCompleteException {
+    new JsniRestrictionChecker().checkProgram(logger, program);
+  }
+
+  private void checkProgram(TreeLogger logger, final JProgram program)
+      throws UnableToCompleteException {
+    final Set<JDeclaredType> typesRequiringTrampolineDispatch = Sets.newHashSet();
+    for (JDeclaredType type : program.getRepresentedAsNativeTypes()) {
+      collectAllSuperTypes(type, typesRequiringTrampolineDispatch);
+    }
+    new JVisitor() {
+      @Override
+      public boolean visit(JMethodBody x, Context ctx) {
+        // Skip non jsni methods.
+        return false;
+      }
+
+      @Override
+      public boolean visit(JsniMethodRef x, Context ctx) {
+        checkJsniMethodReference(x);
+        return true;
+      }
+
+      private void checkJsniMethodReference(JsniMethodRef jsniMethodReference) {
+        JMethod method = jsniMethodReference.getTarget();
+        JDeclaredType enclosingType = method.getEnclosingType();
+
+        if (isNonStaticJsoClassDispatch(method, enclosingType)) {
+          logError(jsniMethodReference,
+              "Cannot call non-static method %s on an instance which is a "
+                  + "subclass of JavaScriptObject. Only static method calls on JavaScriptObject "
+                  + "subclasses are allowed in JSNI.",
+              getDescription(method));
+        } else if (isJsoInterface(enclosingType)) {
+          logError(jsniMethodReference,
+              "Cannot call method %s on an instance which might be a JavaScriptObject. "
+                  + "Such a method call is only allowed in pure Java (non-JSNI) functions.",
+              getDescription(method));
+        } else if (program.isRepresentedAsNativeJsPrimitive(enclosingType)
+            && !method.isStatic()
+            && !method.isConstructor()) {
+          logError(jsniMethodReference,
+              "Cannot call method %s. Instance methods on %s cannot be called from JSNI.",
+              getDescription(method),
+              getDescription(enclosingType));
+        } else if (typesRequiringTrampolineDispatch.contains(enclosingType)
+            && !method.isStatic()
+            && !method.isConstructor()) {
+          logWarning(jsniMethodReference,
+              "Unsafe call to method %s. Instance methods from %s should "
+                  + "not be called on Boolean, Double, String, Array or JSO instances from JSNI.",
+              getDescription(method),
+              getDescription(enclosingType));
+        }
+      }
+
+      private boolean isJsoInterface(JDeclaredType type) {
+        return program.typeOracle.isSingleJsoImpl(type)
+            || program.typeOracle.isDualJsoInterface(type);
+      }
+    }.accept(program);
+
+    boolean hasErrors = reportErrorsAndWarnings(logger);
+    if (hasErrors) {
       throw new UnableToCompleteException();
     }
   }
 
-  private JMethod currentJsniMethod;
-  private final JProgram jprogram;
-  private final Set<JDeclaredType> typesRequiringTrampolineDispatch;
-  private TreeLogger logger;
-  private boolean hasErrors;
-
-  public JsniRestrictionChecker(TreeLogger logger, JProgram jprogram) {
-    this.logger = logger;
-    this.jprogram = jprogram;
-    this.typesRequiringTrampolineDispatch = Sets.newHashSet();
-    for (JDeclaredType type : jprogram.getRepresentedAsNativeTypes()) {
-      collectAllSuperTypes(type , typesRequiringTrampolineDispatch);
-    }
-  }
-
-  private void collectAllSuperTypes(JDeclaredType type,  Set<JDeclaredType> allSuperTypes) {
+  private static void collectAllSuperTypes(JDeclaredType type, Set<JDeclaredType> allSuperTypes) {
     if (type.getSuperClass() != null) {
       allSuperTypes.add(type.getSuperClass());
       collectAllSuperTypes(type.getSuperClass(), allSuperTypes);
@@ -68,77 +112,10 @@
     }
   }
 
-  @Override
-  public boolean visit(JDeclaredType x, Context ctx) {
-    TreeLogger currentLogger = this.logger;
-    this.logger = this.logger.branch(Type.INFO, "Errors in " + x.getSourceInfo().getFileName());
-    accept(x.getMethods());
-    this.logger = currentLogger;
-    return false;
-  }
-
-  @Override
-  public boolean visit(JsniMethodBody x, Context ctx) {
-    currentJsniMethod = x.getMethod();
-    return true;
-  }
-
-  @Override
-  public boolean visit(JsniMethodRef x, Context ctx) {
-    JMethod calledMethod = x.getTarget();
-    JDeclaredType enclosingTypeOfCalledMethod = calledMethod.getEnclosingType();
-
-    if (isNonStaticJsoClassDispatch(calledMethod, enclosingTypeOfCalledMethod)) {
-      logError(x, "JSNI method %s calls non-static method %s on an instance which is a "
-          + "subclass of JavaScriptObject. Only static method calls on JavaScriptObject subclasses "
-          + "are allowed in JSNI.",
-          currentJsniMethod.getQualifiedName(),
-          calledMethod.getQualifiedName());
-    } else if (isJsoInterface(enclosingTypeOfCalledMethod)) {
-      logError(x, "JSNI method %s calls method %s on an instance which might be a "
-          + "JavaScriptObject. Such a method call is only allowed in pure Java (non-JSNI) "
-          + "functions.",
-          currentJsniMethod.getQualifiedName(),
-          calledMethod.getQualifiedName());
-    } else if (jprogram.isRepresentedAsNativeJsPrimitive(enclosingTypeOfCalledMethod)
-        && !calledMethod.isStatic()
-        && !calledMethod.isConstructor()) {
-      logError(x, "JSNI method %s calls method %s. Instance methods on %s "
-          + "cannot be called from JSNI.",
-          currentJsniMethod.getQualifiedName(),
-          calledMethod.getQualifiedName(),
-          enclosingTypeOfCalledMethod.getName());
-    } else if (typesRequiringTrampolineDispatch.contains(enclosingTypeOfCalledMethod)
-        && !calledMethod.isStatic()
-        && !calledMethod.isConstructor()) {
-      log(x, Type.WARN, "JSNI method %s calls method %s. Instance methods from %s should "
-          + "not be called on Boolean, Double, String, Array or JSO instances from JSNI.",
-          currentJsniMethod.getQualifiedName(),
-          calledMethod.getQualifiedName(),
-          enclosingTypeOfCalledMethod.getName());
-    }
-    return true;
-  }
-
-  private void log(HasSourceInfo hasSourceInfo, Type type, String format, Object... args) {
-    logger.log(type, String.format(
-        String.format("Line %d: %s",
-            hasSourceInfo.getSourceInfo().getStartLine(),
-            format),
-        args));
-    hasErrors |= type == Type.ERROR;
-  }
-
-  private void logError(HasSourceInfo hasSourceInfo, String format, Object... args) {
-    log(hasSourceInfo, Type.ERROR, format, args);
-  }
-
-  private boolean isJsoInterface(JDeclaredType type) {
-    return jprogram.typeOracle.isSingleJsoImpl(type)
-        || jprogram.typeOracle.isDualJsoInterface(type);
-  }
-
-  private boolean isNonStaticJsoClassDispatch(JMethod method, JDeclaredType enclosingType) {
+  private static boolean isNonStaticJsoClassDispatch(JMethod method, JDeclaredType enclosingType) {
     return !method.isStatic() && enclosingType.isJsoType();
   }
+
+  private JsniRestrictionChecker() {
+  }
 }
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 24b0ac5..ae5835a 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
@@ -21,10 +21,6 @@
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.util.arg.SourceLevel;
-import com.google.gwt.thirdparty.guava.common.collect.Lists;
-
-import java.util.Arrays;
-import java.util.List;
 
 /**
  * Tests for the JsInteropRestrictionChecker.
@@ -2221,23 +2217,13 @@
 
   public final void assertBuggySucceeds(String... expectedWarnings)
       throws Exception {
-    List<String> allWarnings = Lists.newArrayList();
-    if (expectedWarnings.length > 0) {
-      allWarnings.add("Warnings in test/EntryPoint.java");
-      allWarnings.addAll(Arrays.asList(expectedWarnings));
-    }
-    Result result = assertCompileSucceeds("Buggy buggy = null;",
-        allWarnings.toArray(new String[0]));
+    Result result = assertCompileSucceeds("Buggy buggy = null;", expectedWarnings);
     assertNotNull(result.findClass("test.EntryPoint$Buggy"));
   }
 
   public final void assertBuggyFails(String... expectedErrors) {
     assertTrue(expectedErrors.length > 0);
-
-    List<String> allErrors = Lists.newArrayList();
-    allErrors.add("Errors in test/EntryPoint.java");
-    allErrors.addAll(Arrays.asList(expectedErrors));
-    assertCompileFails("Buggy buggy = null;", allErrors.toArray(new String[0]));
+    assertCompileFails("Buggy buggy = null;", expectedErrors);
   }
 
   @Override
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java
index 5ad8740..9aabbf9 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsniRestrictionCheckerTest.java
@@ -48,9 +48,8 @@
         "}");
 
     assertCompileFails("new Buggy().jsniMethod(null);",
-        "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
-            + "java.lang.Double.doubleValue()D. Instance methods on java.lang.Double cannot be "
-            + "called from JSNI.");
+        "Line 6: Cannot call method 'double Double.doubleValue()'. Instance methods on 'Double' "
+            + "cannot be called from JSNI.");
   }
 
   public void testInstanceCallToTrampolineWarns() throws Exception {
@@ -65,18 +64,14 @@
         "}");
 
     assertCompileSucceeds("new Buggy().jsniMethod(null);",
-        "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
-            + "java.lang.Number.doubleValue()D. "
-            + "Instance methods from java.lang.Number should not be called on "
-            + "Boolean, Double, String, Array or JSO instances from JSNI.",
-        "Line 7: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
-            + "java.lang.CharSequence.charAt(I)C. "
-            + "Instance methods from java.lang.CharSequence should not be called on "
-            + "Boolean, Double, String, Array or JSO instances from JSNI.",
-        "Line 8: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method "
-            + "java.lang.Object.toString()Ljava/lang/String;. "
-            + "Instance methods from java.lang.Object should not be called on "
-            + "Boolean, Double, String, Array or JSO instances from JSNI.");
+        "Line 6: Unsafe call to method 'double Number.doubleValue()'. Instance methods from "
+            + "'Number' should not be called on Boolean, Double, String, Array or JSO instances "
+            + "from JSNI.",
+        "Line 7: Unsafe call to method 'char CharSequence.charAt(int)'. Instance methods from "
+            + "'CharSequence' should not be called on Boolean, Double, String, Array or JSO "
+            + "instances from JSNI.",
+        "Line 8: Unsafe call to method 'String Object.toString()'. Instance methods from 'Object' "
+            + "should not be called on Boolean, Double, String, Array or JSO instances from JSNI.");
   }
 
   public void testStaticJsoDispatchSucceeds() throws Exception {
@@ -112,10 +107,9 @@
         "}");
 
     assertCompileFails("new Buggy().jsniMethod(null);",
-        "Line 13: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls " +
-            "method test.EntryPoint$Buggy$IFoo.foo()V on an instance which might be a " +
-            "JavaScriptObject. Such a method call is only allowed in pure Java " +
-            "(non-JSNI) functions.");
+        "Line 13: Cannot call method 'void EntryPoint.Buggy.IFoo.foo()' on an instance which might "
+            + "be a JavaScriptObject. Such a method call is only allowed in pure Java (non-JSNI) "
+            + "functions.");
   }
 
   public void testNonstaticJsoDispatchFails() throws Exception {
@@ -128,11 +122,9 @@
         "}");
 
     assertCompileFails("new Buggy().jsniMethod(null);",
-        "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls " +
-            "non-static method " +
-            "com.google.gwt.core.client.JavaScriptObject.toString()Ljava/lang/String; on an " +
-            "instance which is a subclass of JavaScriptObject. Only static method calls on " +
-            "JavaScriptObject subclasses are allowed in JSNI.");
+        "Line 6: Cannot call non-static method 'String JavaScriptObject.toString()' on an instance "
+            + "which is a subclass of JavaScriptObject. Only static method calls on "
+            + "JavaScriptObject subclasses are allowed in JSNI.");
   }
 
   public void testNonstaticJsoSubclassDispatchFails() throws Exception {
@@ -149,10 +141,9 @@
         "}");
 
     assertCompileFails("new Buggy().jsniMethod(null);",
-        "Line 10: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls " +
-            "non-static method test.EntryPoint$Buggy$Foo.foo()V on an instance which is a " +
-            "subclass of JavaScriptObject. Only static method calls on JavaScriptObject " +
-            "subclasses are allowed in JSNI.");
+        "Line 10: Cannot call non-static method 'void EntryPoint.Buggy.Foo.foo()' on an instance "
+            + "which is a subclass of JavaScriptObject. Only static method calls on "
+            + "JavaScriptObject subclasses are allowed in JSNI.");
   }
 
   public void testStringInstanceMethodCallFail() throws Exception {
@@ -165,9 +156,8 @@
         "}");
 
     assertCompileFails("new Buggy().jsniMethod(null);",
-        "Line 6: JSNI method test.EntryPoint$Buggy.jsniMethod(Ljava/lang/Object;)V calls method " +
-            "java.lang.String.length()I. Instance methods on java.lang.String cannot be called " +
-            "from JSNI.");
+        "Line 6: Cannot call method 'int String.length()'. Instance methods on 'String' cannot be "
+            + "called from JSNI.");
   }
 
   public void testStringStaticMethodCallSucceeds() throws Exception {