changeset 709:ea54372637a5

6930507: Symbols for anonymous and local classes made too late for use by java tree API Reviewed-by: mcimadamore
author jjg
date Mon, 06 Sep 2010 12:55:09 -0700
parents 68e765b1e9ed
children 7ae4016c5938
files src/share/classes/com/sun/tools/javac/api/JavacTrees.java src/share/classes/com/sun/tools/javac/tree/TreeInfo.java test/tools/javac/api/TestGetElement.java test/tools/javac/processing/model/element/TestAnonClassNames.java test/tools/javac/processing/model/element/TestAnonSourceNames.java
diffstat 5 files changed, 268 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/api/JavacTrees.java	Fri Sep 03 12:00:21 2010 -0700
+++ b/src/share/classes/com/sun/tools/javac/api/JavacTrees.java	Mon Sep 06 12:55:09 2010 -0700
@@ -45,6 +45,7 @@
 import com.sun.source.util.SourcePositions;
 import com.sun.source.util.TreePath;
 import com.sun.source.util.Trees;
+import com.sun.tools.javac.code.Flags;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 import com.sun.tools.javac.code.Symbol.TypeSymbol;
 import com.sun.tools.javac.code.Symbol;
@@ -189,8 +190,24 @@
     }
 
     public Element getElement(TreePath path) {
-        Tree t = path.getLeaf();
-        return TreeInfo.symbolFor((JCTree) t);
+        JCTree tree = (JCTree) path.getLeaf();
+        Symbol sym = TreeInfo.symbolFor(tree);
+        if (sym == null && TreeInfo.isDeclaration(tree)) {
+            for (TreePath p = path; p != null; p = p.getParentPath()) {
+                JCTree t = (JCTree) p.getLeaf();
+                if (t.getTag() == JCTree.CLASSDEF) {
+                    JCClassDecl ct = (JCClassDecl) t;
+                    if (ct.sym != null) {
+                        if ((ct.sym.flags_field & Flags.UNATTRIBUTED) != 0) {
+                            attr.attribClass(ct.pos(), ct.sym);
+                            sym = TreeInfo.symbolFor(tree);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        return sym;
     }
 
     public TypeMirror getTypeMirror(TreePath path) {
--- a/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Fri Sep 03 12:00:21 2010 -0700
+++ b/src/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Mon Sep 06 12:55:09 2010 -0700
@@ -637,6 +637,18 @@
         }
     }
 
+    public static boolean isDeclaration(JCTree node) {
+        node = skipParens(node);
+        switch (node.getTag()) {
+        case JCTree.CLASSDEF:
+        case JCTree.METHODDEF:
+        case JCTree.VARDEF:
+            return true;
+        default:
+            return false;
+        }
+    }
+
     /** If this tree is an identifier or a field, return its symbol,
      *  otherwise return null.
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/api/TestGetElement.java	Mon Sep 06 12:55:09 2010 -0700
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6930507
+ * @summary Symbols for anonymous and local classes made too late for use by java tree API
+ */
+
+import java.io.*;
+import java.util.*;
+import javax.annotation.processing.*;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.*;
+import javax.tools.Diagnostic;
+import static javax.lang.model.util.ElementFilter.*;
+
+import com.sun.source.tree.*;
+import com.sun.source.util.*;
+
+@SupportedOptions({"test", "last"})
+@SupportedAnnotationTypes("*")
+public class TestGetElement extends AbstractProcessor {
+    public static void main(String... args) throws Exception {
+        new TestGetElement().run();
+    }
+
+    public TestGetElement() { }
+
+    public void run() throws Exception {
+        final String testSrc = System.getProperty("test.src");
+        final String testClasses = System.getProperty("test.classes");
+        final String myClassName = getClass().getName();
+        final String mySrc = new File(testSrc, myClassName + ".java").getPath();
+
+        final int NUM_TESTS = 90; // #decls in this source file
+        for (int i = 1; i <= NUM_TESTS; i++) {
+            System.err.println("test " + i);
+            File testDir = new File("test" + i);
+            File classesDir = new File(testDir, "classes");
+            classesDir.mkdirs();
+            String[] args = {
+                "-d", classesDir.getPath(),
+                "-processorpath", testClasses,
+                "-processor", myClassName,
+                "-proc:only",
+                "-Atest=" + i,
+                "-Alast=" + (i == NUM_TESTS),
+                mySrc
+            };
+
+//            System.err.println("compile: " + Arrays.asList(args));
+
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            int rc = com.sun.tools.javac.Main.compile(args, pw);
+            pw.close();
+            String out = sw.toString();
+            if (out != null)
+                System.err.println(out);
+            if (rc != 0) {
+                System.err.println("compilation failed: rc=" + rc);
+                errors++;
+            }
+        }
+
+        if (errors > 0)
+            throw new Exception(errors + " errors occurred");
+    }
+
+
+    int errors;
+
+    public boolean process(Set<? extends TypeElement> annotations,
+                           RoundEnvironment roundEnvironment)
+    {
+        if (roundEnvironment.processingOver())
+            return true;
+
+        Map<String,String> options = processingEnv.getOptions();
+        int test = Integer.parseInt(options.get("test"));
+        boolean _last = Boolean.parseBoolean(options.get("last"));
+
+        Trees trees = Trees.instance(processingEnv);
+        Scanner scanner = new Scanner(trees, _last);
+        int nelems = 0;
+        for (TypeElement e : typesIn(roundEnvironment.getRootElements())) {
+            nelems += scanner.scan(trees.getPath(e), test);
+        }
+
+        Messager m = processingEnv.getMessager();
+        int EXPECT = 1;
+        if (nelems != EXPECT) {
+            m.printMessage(Diagnostic.Kind.ERROR,
+                    "Unexpected number of elements found: " + nelems + " expected: " + EXPECT);
+        }
+        return true;
+    }
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    class Scanner extends TreePathScanner<Integer,Integer> {
+        final Trees trees;
+        final boolean last;
+        int count;
+
+        Scanner(Trees trees, boolean last) {
+            this.trees = trees;
+            this.last = last;
+        }
+
+        @Override
+        public Integer visitClass(ClassTree tree, Integer test) {
+            return reduce(check(test), super.visitClass(tree, test));
+        }
+
+        @Override
+        public Integer visitMethod(MethodTree tree, Integer test) {
+            return reduce(check(test), super.visitMethod(tree, test));
+        }
+
+        @Override
+        public Integer visitVariable(VariableTree tree, Integer test) {
+            return reduce(check(test), super.visitVariable(tree, test));
+        }
+
+        @Override
+        public Integer reduce(Integer i1, Integer i2) {
+            if (i1 == null || i1.intValue() == 0)
+                return i2;
+            if (i2 == null || i2.intValue() == 0)
+                return i1;
+            return (i1 + i2);
+        }
+
+        int check(int test) {
+            count++;
+
+            if (count != test)
+                return 0;
+
+            TreePath p = getCurrentPath();
+            Element e = trees.getElement(p);
+
+            String text = p.getLeaf().toString().replaceAll("\\s+", " ").trim();
+            int MAXLEN = 40;
+            if (text.length() > MAXLEN)
+                text = text.substring(0, MAXLEN - 3) + "...";
+
+            System.err.println(String.format("%3d: %-" + MAXLEN + "s -- %s",
+                    count, text,
+                    (e == null ? "null" : e.getKind() + " " + e)));
+
+            Messager m = processingEnv.getMessager();
+            if (e == null) {
+                m.printMessage(Diagnostic.Kind.ERROR, "Null element found for " + text);
+                return 0;
+            }
+
+            if (last && !e.getSimpleName().contentEquals("last")) {
+                m.printMessage(Diagnostic.Kind.ERROR, "Unexpected name in last test: "
+                        + e.getSimpleName() + ", expected: last");
+            }
+
+            return 1;
+        }
+    }
+
+    // following are all fodder for the test
+
+    class MemberClass {
+        class NestedMemberClass { }
+    }
+
+    {
+        class InnerClassInInit { }
+        Object o = new Object() { };
+    }
+
+    TestGetElement(TestGetElement unused) {
+        class InnerClassInConstr { }
+        Object o = new Object() { };
+    }
+
+    void m() {
+        class InnerClassInMethod { }
+        Object o = new Object() { };
+
+        class C {
+            class MemberClass {
+                class NestedMemberClass { }
+            }
+
+            {
+                class InnerClassInInit { }
+                Object o = new Object() { };
+            }
+
+            C(Object unused) {
+                class InnerClassInConstr { }
+                Object o = new Object() { };
+            }
+
+            void m() {
+                class InnerClassInMethod { }
+                Object o = new Object() { };
+            }
+        }
+    }
+
+    int last; // this name is verified by the test to make sure that all decls are checked
+}
--- a/test/tools/javac/processing/model/element/TestAnonClassNames.java	Fri Sep 03 12:00:21 2010 -0700
+++ b/test/tools/javac/processing/model/element/TestAnonClassNames.java	Mon Sep 06 12:55:09 2010 -0700
@@ -27,8 +27,7 @@
  * @summary Test that reported names of anonymous classes are non-null.
  * @author  Joseph D. Darcy
  * @build TestAnonSourceNames
- * @compile/fail -processor TestAnonSourceNames TestAnonClassNames.java
- * @build TestAnonClassNames
+ * @compile -processor TestAnonSourceNames TestAnonClassNames.java
  * @run main TestAnonClassNames
  */
 
@@ -40,10 +39,6 @@
  *
  * Source files will be tested by the @compile line which runs
  * TestAnonSourceNames as an annotation processor over this file.
- * This compile line is expected to fail until 6930507 is fixed.  Once
- * bug 6930507 is fixed, the "@compile/fail -processor ..." and
- * following "@build..." steps can be replaced with a single "@compile
- * -processor ..." directive.
  *
  * Class files are tested by the @run command on this type.  This
  * class gets the names of classes with different nesting kinds,
--- a/test/tools/javac/processing/model/element/TestAnonSourceNames.java	Fri Sep 03 12:00:21 2010 -0700
+++ b/test/tools/javac/processing/model/element/TestAnonSourceNames.java	Mon Sep 06 12:55:09 2010 -0700
@@ -67,7 +67,7 @@
                      Element element = trees.getElement(trees.getPath(cu, node));
            if (element == null) {
                processingEnv.getMessager().printMessage(ERROR,
-                                                        "No element retreived for node named ''" +
+                                                        "No element retrieved for node named ''" +
                                                         node.getSimpleName() + "''.");
            } else {