TreePosRoundsTest.java revision 2747:84a76798cff3
1/* 2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 6985205 6986246 27 * @summary access to tree positions and doc comments may be lost across annotation processing rounds 28 * @build TreePosRoundsTest 29 * @compile -proc:only -processor TreePosRoundsTest TreePosRoundsTest.java 30 * @run main TreePosRoundsTest 31 */ 32 33import java.io.*; 34import java.util.*; 35import javax.annotation.processing.*; 36import javax.lang.model.*; 37import javax.lang.model.element.*; 38import javax.tools.*; 39 40import com.sun.source.tree.*; 41import com.sun.source.util.*; 42import javax.tools.JavaCompiler.CompilationTask; 43 44// This test is an annotation processor that performs multiple rounds of 45// processing, and on each round, it checks that source positions are 46// available and correct. 47// 48// The test can be run directly as a processor from the javac command line 49// or via JSR 199 by invoking the main program. 50 51@SupportedAnnotationTypes("*") 52public class TreePosRoundsTest extends AbstractProcessor { 53 public static void main(String... args) throws Exception { 54 String testSrc = System.getProperty("test.src"); 55 String testClasses = System.getProperty("test.classes"); 56 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 57 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 58 String thisName = TreePosRoundsTest.class.getName(); 59 File thisFile = new File(testSrc, thisName + ".java"); 60 Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(thisFile); 61 List<String> options = Arrays.asList( 62 "-proc:only", 63 "-processor", thisName, 64 "-processorpath", testClasses); 65 CompilationTask t = c.getTask(null, fm, null, options, null, files); 66 boolean ok = t.call(); 67 if (!ok) 68 throw new Exception("processing failed"); 69 } 70 } 71 72 Filer filer; 73 Messager messager; 74 Trees trees; 75 76 @Override 77 public SourceVersion getSupportedSourceVersion() { 78 return SourceVersion.latest(); 79 } 80 81 @Override 82 public void init(ProcessingEnvironment pEnv) { 83 super.init(pEnv); 84 filer = pEnv.getFiler(); 85 messager = pEnv.getMessager(); 86 trees = Trees.instance(pEnv); 87 } 88 89 int round = 0; 90 91 @Override 92 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 93 round++; 94 95 // Scan trees for elements, verifying source tree positions 96 for (Element e: roundEnv.getRootElements()) { 97 try { 98 TreePath p = trees.getPath(e); 99 new TestTreeScanner(p.getCompilationUnit(), trees).scan(trees.getPath(e), null); 100 } catch (IOException ex) { 101 messager.printMessage(Diagnostic.Kind.ERROR, 102 "Cannot get source: " + ex, e); 103 } 104 } 105 106 final int MAXROUNDS = 3; 107 if (round < MAXROUNDS) 108 generateSource("Gen" + round); 109 110 return true; 111 } 112 113 void generateSource(String name) { 114 StringBuilder text = new StringBuilder(); 115 text.append("class ").append(name).append("{\n"); 116 text.append(" int one = 1;\n"); 117 text.append(" int two = 2;\n"); 118 text.append(" int three = one + two;\n"); 119 text.append("}\n"); 120 121 try { 122 JavaFileObject fo = filer.createSourceFile(name); 123 Writer out = fo.openWriter(); 124 try { 125 out.write(text.toString()); 126 } finally { 127 out.close(); 128 } 129 } catch (IOException e) { 130 throw new Error(e); 131 } 132 } 133 134 class TestTreeScanner extends TreePathScanner<Void,Void> { 135 TestTreeScanner(CompilationUnitTree unit, Trees trees) throws IOException { 136 this.unit = unit; 137 JavaFileObject sf = unit.getSourceFile(); 138 source = sf.getCharContent(true).toString(); 139 sourcePositions = trees.getSourcePositions(); 140 } 141 142 @Override 143 public Void visitVariable(VariableTree tree, Void p) { 144 check(getCurrentPath()); 145 return super.visitVariable(tree, p); 146 } 147 148 void check(TreePath tp) { 149 Tree tree = tp.getLeaf(); 150 151 String expect = tree.toString(); 152 if (tree.getKind() == Tree.Kind.VARIABLE) { 153 // tree.toString() does not know enough context to add ";", 154 // so deal with that manually... 155 Tree.Kind enclKind = tp.getParentPath().getLeaf().getKind(); 156 //System.err.println(" encl: " +enclKind); 157 if (enclKind == Tree.Kind.CLASS || enclKind == Tree.Kind.BLOCK) 158 expect += ";"; 159 // t-w-r- adds implicit final: remove it 160 if (enclKind == Tree.Kind.TRY && expect.startsWith("final ")) 161 expect = expect.substring(6); 162 } 163 //System.err.println("expect: " + expect); 164 165 int start = (int)sourcePositions.getStartPosition(unit, tree); 166 if (start == Diagnostic.NOPOS) { 167 messager.printMessage(Diagnostic.Kind.ERROR, "start pos not set for " + trim(tree)); 168 return; 169 } 170 171 int end = (int)sourcePositions.getEndPosition(unit, tree); 172 if (end == Diagnostic.NOPOS) { 173 messager.printMessage(Diagnostic.Kind.ERROR, "end pos not set for " + trim(tree)); 174 return; 175 } 176 177 String found = source.substring(start, end); 178 //System.err.println(" found: " + found); 179 180 // allow for long lines, in which case just compare beginning and 181 // end of the strings 182 boolean equal; 183 if (found.contains("\n")) { 184 String head = found.substring(0, found.indexOf("\n")); 185 String tail = found.substring(found.lastIndexOf("\n")).trim(); 186 equal = expect.startsWith(head) && expect.endsWith(tail); 187 } else { 188 equal = expect.equals(found); 189 } 190 191 if (!equal) { 192 messager.printMessage(Diagnostic.Kind.ERROR, 193 "unexpected value found: '" + found + "'; expected: '" + expect + "'"); 194 } 195 } 196 197 String trim(Tree tree) { 198 final int MAXLEN = 32; 199 String s = tree.toString().replaceAll("\\s+", " ").trim(); 200 return (s.length() < MAXLEN) ? s : s.substring(0, MAXLEN); 201 202 } 203 204 CompilationUnitTree unit; 205 SourcePositions sourcePositions; 206 String source; 207 } 208 209} 210