6930507: Symbols for anonymous and local classes made too late for use by java tree API

Reviewed-by: mcimadamore
This commit is contained in:
Jonathan Gibbons 2010-09-06 12:55:09 -07:00
parent 5b58b51e22
commit c09fe5cafb
5 changed files with 268 additions and 9 deletions

View File

@ -45,6 +45,7 @@ import com.sun.source.tree.Tree;
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 class JavacTrees extends Trees {
}
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) {

View File

@ -637,6 +637,18 @@ public class TreeInfo {
}
}
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.
*/

View File

@ -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
}

View File

@ -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,

View File

@ -67,7 +67,7 @@ public class TestAnonSourceNames extends AbstractProcessor {
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 {