Wed, 03 Feb 2010 16:58:57 -0800
6921979: add test program to verify annotations are attached to nodes as expected
Reviewed-by: darcy
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/tools/javac/treeannotests/AnnoTreeTests.java Wed Feb 03 16:58:57 2010 -0800 1.3 @@ -0,0 +1,44 @@ 1.4 +/* 1.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.24 + * have any questions. 1.25 + */ 1.26 + 1.27 +/* 1.28 + * @test 1.29 + * @build DA TA Test TestProcessor 1.30 + * @compile -proc:only -processor TestProcessor AnnoTreeTests.java 1.31 + */ 1.32 + 1.33 +@Test(6) 1.34 +class AnnoTreeTests { 1.35 + // primitive types 1.36 + @DA("int") int i1; 1.37 + int i2 = (@TA("int") int) 0; 1.38 + 1.39 + // simple array types 1.40 + @DA("int[]") int[] a1; 1.41 + int @TA("int") [] a2; 1.42 + int[] a3 = (@TA("int[]") int[]) a1; 1.43 + int[] a4 = (int @TA("int") []) a1; 1.44 + 1.45 + // multi-dimensional array types 1.46 + // (still to come) 1.47 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/treeannotests/DA.java Wed Feb 03 16:58:57 2010 -0800 2.3 @@ -0,0 +1,37 @@ 2.4 +/* 2.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. 2.11 + * 2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.15 + * version 2 for more details (a copy is included in the LICENSE file that 2.16 + * accompanied this code). 2.17 + * 2.18 + * You should have received a copy of the GNU General Public License version 2.19 + * 2 along with this work; if not, write to the Free Software Foundation, 2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.21 + * 2.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 2.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 2.24 + * have any questions. 2.25 + */ 2.26 + 2.27 +import java.lang.annotation.*; 2.28 +import static java.lang.annotation.ElementType.*; 2.29 + 2.30 +/** 2.31 + * Annotation used by TestProcessor to indicate the expected type 2.32 + * of the declaration to which the annotation is attached. 2.33 + * These annotations are expected to be found in the modifiers 2.34 + * field on ClassDef, MethodDef and VarDef nodes. 2.35 + */ 2.36 +@Target({FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) 2.37 +@interface DA { 2.38 + String value(); 2.39 +} 2.40 +
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/tools/javac/treeannotests/TA.java Wed Feb 03 16:58:57 2010 -0800 3.3 @@ -0,0 +1,36 @@ 3.4 +/* 3.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 3.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 3.24 + * have any questions. 3.25 + */ 3.26 + 3.27 +import java.lang.annotation.*; 3.28 +import static java.lang.annotation.ElementType.*; 3.29 + 3.30 +/** 3.31 + * Annotation used by TestProcessor to indicate the expected type 3.32 + * to which the annotation is attached. 3.33 + * These annotations are expected to be found in AnnotatedType nodes. 3.34 + */ 3.35 +@Target({TYPE_USE, TYPE_PARAMETER}) 3.36 +@interface TA { 3.37 + String value(); 3.38 +} 3.39 +
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/treeannotests/Test.java Wed Feb 03 16:58:57 2010 -0800 4.3 @@ -0,0 +1,32 @@ 4.4 +/* 4.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 4.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 4.24 + * have any questions. 4.25 + */ 4.26 + 4.27 +/** 4.28 + * Annotation used by TestProcessor to indicate the expected number of 4.29 + * @DA and @TA annotations in the source tree to which the Test annotation 4.30 + * is attached. 4.31 + */ 4.32 +@interface Test { 4.33 + int value(); 4.34 +} 4.35 +
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/tools/javac/treeannotests/TestProcessor.java Wed Feb 03 16:58:57 2010 -0800 5.3 @@ -0,0 +1,302 @@ 5.4 +/* 5.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 5.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 5.24 + * have any questions. 5.25 + */ 5.26 + 5.27 +import java.io.*; 5.28 +import java.util.*; 5.29 +import javax.annotation.processing.*; 5.30 +import javax.lang.model.*; 5.31 +import javax.lang.model.element.*; 5.32 +import javax.lang.model.util.*; 5.33 +import javax.tools.*; 5.34 + 5.35 +import com.sun.source.util.*; 5.36 +import com.sun.tools.javac.code.BoundKind; 5.37 +import com.sun.tools.javac.tree.JCTree.*; 5.38 +import com.sun.tools.javac.tree.TreeScanner; 5.39 +import com.sun.tools.javac.tree.*; 5.40 +import com.sun.tools.javac.util.List; 5.41 + 5.42 +/** 5.43 + * Test processor used to check test programs using the @Test, @DA, and @TA 5.44 + * annotations. 5.45 + * 5.46 + * The processor looks for elements annotated with @Test, and analyzes the 5.47 + * syntax trees for those elements. Within such trees, the processor looks 5.48 + * for the DA annotations on decls and TA annotations on types. 5.49 + * The value of these annotations should be a simple string rendition of 5.50 + * the tree node to which it is attached. 5.51 + * The expected number of annotations is given by the parameter to the 5.52 + * @Test annotation itself. 5.53 + */ 5.54 +@SupportedAnnotationTypes({"Test"}) 5.55 +public class TestProcessor extends AbstractProcessor { 5.56 + public SourceVersion getSupportedSourceVersion() { 5.57 + return SourceVersion.latest(); 5.58 + } 5.59 + 5.60 + /** Process trees for elements annotated with the @Test(n) annotation. */ 5.61 + public boolean process(Set<? extends TypeElement> annos, RoundEnvironment renv) { 5.62 + if (renv.processingOver()) 5.63 + return true; 5.64 + 5.65 + Elements elements = processingEnv.getElementUtils(); 5.66 + Trees trees = Trees.instance(processingEnv); 5.67 + 5.68 + TypeElement testAnno = elements.getTypeElement("Test"); 5.69 + for (Element elem: renv.getElementsAnnotatedWith(testAnno)) { 5.70 + System.err.println("ELEM: " + elem); 5.71 + int count = getValue(getAnnoMirror(elem, testAnno), Integer.class); 5.72 + System.err.println("count: " + count); 5.73 + TreePath p = trees.getPath(elem); 5.74 + JavaFileObject file = p.getCompilationUnit().getSourceFile(); 5.75 + JCTree tree = (JCTree) p.getLeaf(); 5.76 + System.err.println("tree: " + tree); 5.77 + new TestScanner(file).check(tree, count); 5.78 + } 5.79 + return true; 5.80 + } 5.81 + 5.82 + /** Get the AnnotationMirror on an element for a given annotation. */ 5.83 + AnnotationMirror getAnnoMirror(Element e, TypeElement anno) { 5.84 + Types types = processingEnv.getTypeUtils(); 5.85 + for (AnnotationMirror m: e.getAnnotationMirrors()) { 5.86 + if (types.isSameType(m.getAnnotationType(), anno.asType())) 5.87 + return m; 5.88 + } 5.89 + return null; 5.90 + } 5.91 + 5.92 + /** Get the value of the value element of an annotation mirror. */ 5.93 + <T> T getValue(AnnotationMirror m, Class<T> type) { 5.94 + for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> e: m.getElementValues().entrySet()) { 5.95 + ExecutableElement ee = e.getKey(); 5.96 + if (ee.getSimpleName().contentEquals("value")) { 5.97 + AnnotationValue av = e.getValue(); 5.98 + return type.cast(av.getValue()); 5.99 + } 5.100 + } 5.101 + return null; 5.102 + } 5.103 + 5.104 + /** Report an error to the annotation processing system. */ 5.105 + void error(String msg) { 5.106 + Messager messager = processingEnv.getMessager(); 5.107 + messager.printMessage(Diagnostic.Kind.ERROR, msg); 5.108 + } 5.109 + 5.110 + /** Report an error to the annotation processing system. */ 5.111 + void error(JavaFileObject file, JCTree tree, String msg) { 5.112 + // need better API for reporting tree position errors to the messager 5.113 + Messager messager = processingEnv.getMessager(); 5.114 + String text = file.getName() + ":" + getLine(file, tree) + ": " + msg; 5.115 + messager.printMessage(Diagnostic.Kind.ERROR, text); 5.116 + } 5.117 + 5.118 + /** Get the line number for the primary position for a tree. 5.119 + * The code is intended to be simple, although not necessarily efficient. 5.120 + * However, note that a file manager such as JavacFileManager is likely 5.121 + * to cache the results of file.getCharContent, avoiding the need to read 5.122 + * the bits from disk each time this method is called. 5.123 + */ 5.124 + int getLine(JavaFileObject file, JCTree tree) { 5.125 + try { 5.126 + CharSequence cs = file.getCharContent(true); 5.127 + int line = 1; 5.128 + for (int i = 0; i < tree.pos; i++) { 5.129 + if (cs.charAt(i) == '\n') // jtreg tests always use Unix line endings 5.130 + line++; 5.131 + } 5.132 + return line; 5.133 + } catch (IOException e) { 5.134 + return -1; 5.135 + } 5.136 + } 5.137 + 5.138 + /** Scan a tree, looking for @DA and @TA annotations, and verifying that such 5.139 + * annotations are attached to the expected tree node matching the string 5.140 + * parameter of the annotation. 5.141 + */ 5.142 + class TestScanner extends TreeScanner { 5.143 + /** Create a scanner for a given file. */ 5.144 + TestScanner(JavaFileObject file) { 5.145 + this.file = file; 5.146 + } 5.147 + 5.148 + /** Check the annotations in a given tree. */ 5.149 + void check(JCTree tree, int expectCount) { 5.150 + foundCount = 0; 5.151 + scan(tree); 5.152 + if (foundCount != expectCount) 5.153 + error(file, tree, "Wrong number of annotations found: " + foundCount + ", expected: " + expectCount); 5.154 + } 5.155 + 5.156 + /** Check @DA annotations on a class declaration. */ 5.157 + @Override 5.158 + public void visitClassDef(JCClassDecl tree) { 5.159 + super.visitClassDef(tree); 5.160 + check(tree.mods.annotations, "DA", tree); 5.161 + } 5.162 + 5.163 + /** Check @DA annotations on a method declaration. */ 5.164 + @Override 5.165 + public void visitMethodDef(JCMethodDecl tree) { 5.166 + super.visitMethodDef(tree); 5.167 + check(tree.mods.annotations, "DA", tree); 5.168 + } 5.169 + 5.170 + /** Check @DA annotations on a field, parameter or local variable declaration. */ 5.171 + @Override 5.172 + public void visitVarDef(JCVariableDecl tree) { 5.173 + super.visitVarDef(tree); 5.174 + check(tree.mods.annotations, "DA", tree); 5.175 + } 5.176 + 5.177 + /** Check @TA annotations on a type. */ 5.178 + public void visitAnnotatedType(JCAnnotatedType tree) { 5.179 + super.visitAnnotatedType(tree); 5.180 + check(tree.annotations, "TA", tree); 5.181 + } 5.182 + 5.183 + /** Check to see if a list of annotations contains a named annotation, and 5.184 + * if so, verify the annotation is expected by comparing the value of the 5.185 + * annotation's argument against the string rendition of the reference tree 5.186 + * node. 5.187 + * @param annos the list of annotations to be checked 5.188 + * @param name the name of the annotation to be checked 5.189 + * @param tree the tree against which to compare the annotations's argument 5.190 + */ 5.191 + void check(List<? extends JCAnnotation> annos, String name, JCTree tree) { 5.192 + for (List<? extends JCAnnotation> l = annos; l.nonEmpty(); l = l.tail) { 5.193 + JCAnnotation anno = l.head; 5.194 + if (anno.annotationType.toString().equals(name) && (anno.args.size() == 1)) { 5.195 + String expect = getStringValue(anno.args.head); 5.196 + foundCount++; 5.197 + System.err.println("found: " + name + " " + expect); 5.198 + String found = new TypePrinter().print(tree); 5.199 + if (!found.equals(expect)) 5.200 + error(file, anno, "Unexpected result: expected: \"" + expect + "\", found: \"" + found + "\""); 5.201 + } 5.202 + } 5.203 + } 5.204 + 5.205 + /** Get the string value of an annotation argument, which is given by the 5.206 + * expression <i>name</i>=<i>value</i>. 5.207 + */ 5.208 + String getStringValue(JCExpression e) { 5.209 + if (e.getTag() == JCTree.ASSIGN) { 5.210 + JCAssign a = (JCAssign) e; 5.211 + JCExpression rhs = a.rhs; 5.212 + if (rhs.getTag() == JCTree.LITERAL) { 5.213 + JCLiteral l = (JCLiteral) rhs; 5.214 + return (String) l.value; 5.215 + } 5.216 + } 5.217 + throw new IllegalArgumentException(e.toString()); 5.218 + } 5.219 + 5.220 + /** The file for the tree. Used to locate errors. */ 5.221 + JavaFileObject file; 5.222 + /** The number of annotations that have been found. @see #check */ 5.223 + int foundCount; 5.224 + } 5.225 + 5.226 + /** Convert a type or decl tree to a reference string used by the @DA and @TA annotations. */ 5.227 + class TypePrinter extends Visitor { 5.228 + /** Convert a type or decl tree to a string. */ 5.229 + String print(JCTree tree) { 5.230 + if (tree == null) 5.231 + return null; 5.232 + tree.accept(this); 5.233 + return result; 5.234 + } 5.235 + 5.236 + String print(List<? extends JCTree> list) { 5.237 + return print(list, ", "); 5.238 + } 5.239 + 5.240 + String print(List<? extends JCTree> list, String sep) { 5.241 + StringBuilder sb = new StringBuilder(); 5.242 + if (list.nonEmpty()) { 5.243 + sb.append(print(list.head)); 5.244 + for (List<? extends JCTree> l = list.tail; l.nonEmpty(); l = l.tail) { 5.245 + sb.append(sep); 5.246 + sb.append(print(l.head)); 5.247 + } 5.248 + } 5.249 + return sb.toString(); 5.250 + } 5.251 + 5.252 + @Override 5.253 + public void visitClassDef(JCClassDecl tree) { 5.254 + result = tree.name.toString(); 5.255 + } 5.256 + 5.257 + @Override 5.258 + public void visitMethodDef(JCMethodDecl tree) { 5.259 + result = tree.name.toString(); 5.260 + } 5.261 + 5.262 + @Override 5.263 + public void visitVarDef(JCVariableDecl tree) { 5.264 + tree.vartype.accept(this); 5.265 + } 5.266 + 5.267 + @Override 5.268 + public void visitAnnotatedType(JCAnnotatedType tree) { 5.269 + tree.underlyingType.accept(this); 5.270 + } 5.271 + 5.272 + @Override 5.273 + public void visitTypeIdent(JCPrimitiveTypeTree tree) { 5.274 + result = tree.toString(); 5.275 + } 5.276 + 5.277 + @Override 5.278 + public void visitTypeArray(JCArrayTypeTree tree) { 5.279 + result = print(tree.elemtype) + "[]"; 5.280 + } 5.281 + 5.282 + @Override 5.283 + public void visitTypeApply(JCTypeApply tree) { 5.284 + result = print(tree.clazz) + "<" + print(tree.arguments) + ">"; 5.285 + } 5.286 + 5.287 + @Override 5.288 + public void visitTypeParameter(JCTypeParameter tree) { 5.289 + if (tree.bounds.isEmpty()) 5.290 + result = tree.name.toString(); 5.291 + else 5.292 + result = tree.name + " extends " + print(tree.bounds, "&"); 5.293 + } 5.294 + 5.295 + @Override 5.296 + public void visitWildcard(JCWildcard tree) { 5.297 + if (tree.kind.kind == BoundKind.UNBOUND) 5.298 + result = tree.kind.toString(); 5.299 + else 5.300 + result = tree.kind + " " + print(tree.inner); 5.301 + } 5.302 + 5.303 + private String result; 5.304 + } 5.305 +}