TreePosRoundsTest.java revision 2933:49d207bf704d
1/* 2 * Copyright (c) 2010, 2015, 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 * @modules jdk.compiler 29 * @build TreePosRoundsTest 30 * @compile -proc:only -processor TreePosRoundsTest TreePosRoundsTest.java 31 * @run main TreePosRoundsTest 32 */ 33 34import java.io.*; 35import java.util.*; 36import javax.annotation.processing.*; 37import javax.lang.model.*; 38import javax.lang.model.element.*; 39import javax.tools.*; 40 41import com.sun.source.tree.*; 42import com.sun.source.util.*; 43import javax.tools.JavaCompiler.CompilationTask; 44 45// This test is an annotation processor that performs multiple rounds of 46// processing, and on each round, it checks that source positions are 47// available and correct. 48// 49// The test can be run directly as a processor from the javac command line 50// or via JSR 199 by invoking the main program. 51 52@SupportedAnnotationTypes("*") 53public class TreePosRoundsTest extends AbstractProcessor { 54 public static void main(String... args) throws Exception { 55 String testSrc = System.getProperty("test.src"); 56 String testClasses = System.getProperty("test.classes"); 57 JavaCompiler c = ToolProvider.getSystemJavaCompiler(); 58 try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { 59 String thisName = TreePosRoundsTest.class.getName(); 60 File thisFile = new File(testSrc, thisName + ".java"); 61 Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(thisFile); 62 List<String> options = Arrays.asList( 63 "-proc:only", 64 "-processor", thisName, 65 "-processorpath", testClasses); 66 CompilationTask t = c.getTask(null, fm, null, options, null, files); 67 boolean ok = t.call(); 68 if (!ok) 69 throw new Exception("processing failed"); 70 } 71 } 72 73 Filer filer; 74 Messager messager; 75 Trees trees; 76 77 @Override 78 public SourceVersion getSupportedSourceVersion() { 79 return SourceVersion.latest(); 80 } 81 82 @Override 83 public void init(ProcessingEnvironment pEnv) { 84 super.init(pEnv); 85 filer = pEnv.getFiler(); 86 messager = pEnv.getMessager(); 87 trees = Trees.instance(pEnv); 88 } 89 90 int round = 0; 91 92 @Override 93 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 94 round++; 95 96 // Scan trees for elements, verifying source tree positions 97 for (Element e: roundEnv.getRootElements()) { 98 try { 99 TreePath p = trees.getPath(e); 100 new TestTreeScanner(p.getCompilationUnit(), trees).scan(trees.getPath(e), null); 101 } catch (IOException ex) { 102 messager.printMessage(Diagnostic.Kind.ERROR, 103 "Cannot get source: " + ex, e); 104 } 105 } 106 107 final int MAXROUNDS = 3; 108 if (round < MAXROUNDS) 109 generateSource("Gen" + round); 110 111 return true; 112 } 113 114 void generateSource(String name) { 115 StringBuilder text = new StringBuilder(); 116 text.append("class ").append(name).append("{\n"); 117 text.append(" int one = 1;\n"); 118 text.append(" int two = 2;\n"); 119 text.append(" int three = one + two;\n"); 120 text.append("}\n"); 121 122 try { 123 JavaFileObject fo = filer.createSourceFile(name); 124 Writer out = fo.openWriter(); 125 try { 126 out.write(text.toString()); 127 } finally { 128 out.close(); 129 } 130 } catch (IOException e) { 131 throw new Error(e); 132 } 133 } 134 135 class TestTreeScanner extends TreePathScanner<Void,Void> { 136 TestTreeScanner(CompilationUnitTree unit, Trees trees) throws IOException { 137 this.unit = unit; 138 JavaFileObject sf = unit.getSourceFile(); 139 source = sf.getCharContent(true).toString(); 140 sourcePositions = trees.getSourcePositions(); 141 } 142 143 @Override 144 public Void visitVariable(VariableTree tree, Void p) { 145 check(getCurrentPath()); 146 return super.visitVariable(tree, p); 147 } 148 149 void check(TreePath tp) { 150 Tree tree = tp.getLeaf(); 151 152 String expect = tree.toString(); 153 if (tree.getKind() == Tree.Kind.VARIABLE) { 154 // tree.toString() does not know enough context to add ";", 155 // so deal with that manually... 156 Tree.Kind enclKind = tp.getParentPath().getLeaf().getKind(); 157 //System.err.println(" encl: " +enclKind); 158 if (enclKind == Tree.Kind.CLASS || enclKind == Tree.Kind.BLOCK) 159 expect += ";"; 160 // t-w-r- adds implicit final: remove it 161 if (enclKind == Tree.Kind.TRY && expect.startsWith("final ")) 162 expect = expect.substring(6); 163 } 164 //System.err.println("expect: " + expect); 165 166 int start = (int)sourcePositions.getStartPosition(unit, tree); 167 if (start == Diagnostic.NOPOS) { 168 messager.printMessage(Diagnostic.Kind.ERROR, "start pos not set for " + trim(tree)); 169 return; 170 } 171 172 int end = (int)sourcePositions.getEndPosition(unit, tree); 173 if (end == Diagnostic.NOPOS) { 174 messager.printMessage(Diagnostic.Kind.ERROR, "end pos not set for " + trim(tree)); 175 return; 176 } 177 178 String found = source.substring(start, end); 179 //System.err.println(" found: " + found); 180 181 // allow for long lines, in which case just compare beginning and 182 // end of the strings 183 boolean equal; 184 if (found.contains("\n")) { 185 String head = found.substring(0, found.indexOf("\n")); 186 String tail = found.substring(found.lastIndexOf("\n")).trim(); 187 equal = expect.startsWith(head) && expect.endsWith(tail); 188 } else { 189 equal = expect.equals(found); 190 } 191 192 if (!equal) { 193 messager.printMessage(Diagnostic.Kind.ERROR, 194 "unexpected value found: '" + found + "'; expected: '" + expect + "'"); 195 } 196 } 197 198 String trim(Tree tree) { 199 final int MAXLEN = 32; 200 String s = tree.toString().replaceAll("\\s+", " ").trim(); 201 return (s.length() < MAXLEN) ? s : s.substring(0, MAXLEN); 202 203 } 204 205 CompilationUnitTree unit; 206 SourcePositions sourcePositions; 207 String source; 208 } 209 210} 211