Cleanup of JsniChecker per spoon suggestions.

Suggested by: spoon
Review by: spoon (TBR)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4782 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java b/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
index fdd0bc7..4584aa6 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsniChecker.java
@@ -36,7 +36,6 @@
 import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
-import org.eclipse.jdt.internal.compiler.lookup.TagBits;
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
@@ -140,27 +139,28 @@
       for (String jsniRefString : sloppyRefsVisitor.getJsniRefs()) {
         JsniRef jsniRef = JsniRef.parse(jsniRefString);
         if (jsniRef != null) {
-          Set<String> refErrors = new LinkedHashSet<String>();
           ReferenceBinding clazz = findClass(jsniRef);
-          if (clazz != null) {
-            if (clazz.isAnonymousType()) {
-              GWTProblem.recordInCud(
-                  ProblemSeverities.Warning,
-                  meth,
-                  cud,
-                  "Referencing class '" + jsniRef.className()
-                      + ": JSNI references to anonymous classes are deprecated",
-                  null);
+          if (looksLikeAnonymousClass(jsniRef)
+              || (clazz != null && clazz.isAnonymousType())) {
+            GWTProblem.recordInCud(ProblemSeverities.Warning, meth, cud,
+                "Referencing class '" + jsniRef.className()
+                    + ": JSNI references to anonymous classes are deprecated",
+                null);
+          } else if (clazz != null) {
+            Set<String> refErrors = new LinkedHashSet<String>();
+            if (jsniRef.isMethod()) {
+              checkMethodRef(clazz, jsniRef, refErrors);
             } else {
-              if (jsniRef.isMethod()) {
-                checkMethodRef(clazz, jsniRef, refErrors);
-              } else {
-                checkFieldRef(clazz, jsniRef, refErrors);
-              }
-              if (!refErrors.isEmpty()) {
-                errors.put(jsniRefString, refErrors);
-              }
+              checkFieldRef(clazz, jsniRef, refErrors);
             }
+            if (!refErrors.isEmpty()) {
+              errors.put(jsniRefString, refErrors);
+            }
+          } else {
+            GWTProblem.recordInCud(ProblemSeverities.Warning, meth, cud,
+                "Referencing class '" + jsniRef.className()
+                    + ": unable to resolve class, expect subsequent failures",
+                null);
           }
         }
       }
@@ -200,16 +200,14 @@
     }
 
     private ReferenceBinding findClass(JsniRef jsniRef) {
-      String className = jsniRef.className().replace('$', '.');
-      char[][] compoundName = CharOperation.splitOn('.',
-          className.toCharArray());
+      char[][] compoundName = getCompoundName(jsniRef);
       TypeBinding binding = cud.scope.getType(compoundName, compoundName.length);
 
       if (binding instanceof ProblemReferenceBinding) {
         ProblemReferenceBinding prb = (ProblemReferenceBinding) binding;
         if (prb.problemId() == ProblemReasons.NotVisible) {
           // It's just a visibility problem, so try drilling down manually
-          ReferenceBinding drilling  = prb.closestReferenceMatch();
+          ReferenceBinding drilling = prb.closestReferenceMatch();
           for (int i = prb.compoundName.length; i < compoundName.length; i++) {
             drilling = drilling.getMemberType(compoundName[i]);
           }
@@ -221,19 +219,16 @@
           && !(binding instanceof ProblemReferenceBinding)) {
         return (ReferenceBinding) binding;
       }
-      // See if it looks like an anonymous inner class, we can't look those up.
-      for (char[] part : compoundName) {
-        if (Character.isDigit(part[0])) {
-          return new ReferenceBinding() {
-            {
-              tagBits = TagBits.IsAnonymousType;
-            }
-          };
-        }
-      }
       return null;
     }
 
+    private char[][] getCompoundName(JsniRef jsniRef) {
+      String className = jsniRef.className().replace('$', '.');
+      char[][] compoundName = CharOperation.splitOn('.',
+          className.toCharArray());
+      return compoundName;
+    }
+
     private FieldBinding getField(ReferenceBinding clazz, JsniRef jsniRef) {
       assert jsniRef.isField();
       return clazz.getField(jsniRef.memberName().toCharArray(), false);
@@ -282,6 +277,16 @@
           "longJsniRestriction.html"));
     }
 
+    private boolean looksLikeAnonymousClass(JsniRef jsniRef) {
+      char[][] compoundName = getCompoundName(jsniRef);
+      for (char[] part : compoundName) {
+        if (Character.isDigit(part[0])) {
+          return true;
+        }
+      }
+      return false;
+    }
+
     private boolean paramTypesMatch(MethodBinding method, JsniRef jsniRef) {
       StringBuilder methodSig = new StringBuilder();
       if (method.parameters != null) {
diff --git a/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java b/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
index 5ebe56e..5a3e358 100644
--- a/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/JsniCheckerTest.java
@@ -50,6 +50,28 @@
         + "JSNI references to anonymous classes are deprecated");
   }
 
+  /**
+   * JSNI references to anonymous inner classes is deprecated.
+   */
+  public void testAnoymousJsniRefNested() {
+    StringBuffer code = new StringBuffer();
+    code.append("class Buggy {\n");
+    code.append("  static void main() {\n");
+    code.append("    new Object() {\n");
+    code.append("      class A {\n");
+    code.append("        int foo = 3;\n");
+    code.append("      };\n");
+    code.append("    };\n");
+    code.append("  }\n");
+    code.append("  native void jsniMeth(Object o) /*-{\n");
+    code.append("    o.@Buggy$1.A::foo;\n");
+    code.append("  }-*/;\n");
+    code.append("}\n");
+
+    shouldGenerateWarning(code, 9, "Referencing class \'Buggy$1.A: "
+        + "JSNI references to anonymous classes are deprecated");
+  }
+
   public void testCyclicReferences() {
     {
       StringBuffer buggy = new StringBuffer();
@@ -123,6 +145,21 @@
         + "type 'long' is not safe to access in JSNI code");
   }
 
+  public void testInnerClassDollar() {
+    StringBuffer code = new StringBuffer();
+    code.append("public class Buggy {\n");
+    code.append("  static class Inner {\n");
+    code.append("    long x = 3;\n");
+    code.append("  }\n");
+    code.append("  native void jsniMeth() /*-{\n");
+    code.append("    $wnd.alert(@Buggy$Inner::x);\n");
+    code.append("  }-*/;\n");
+    code.append("}\n");
+
+    shouldGenerateError(code, 5, "Referencing field 'Buggy$Inner.x': "
+        + "type 'long' is not safe to access in JSNI code");
+  }
+
   /**
    * The proper behavior here is a close call. In hosted mode, Java arrays are
    * completely unusable in JavaScript, so the current reasoning is to allow