1 /*
2  * Copyright (c) 2013, 2018, 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 8017216 8019422 8019421 8054956 8205418
27  * @summary verify start and end positions
28  * @modules java.compiler
29  *          jdk.compiler
30  * @run main TreeEndPosTest
31  */
32 
33 import java.io.ByteArrayOutputStream;
34 import java.io.File;
35 import java.io.IOException;
36 import java.io.PrintWriter;
37 import java.net.URI;
38 import java.util.ArrayList;
39 import java.util.List;
40 import javax.tools.Diagnostic;
41 import javax.tools.DiagnosticCollector;
42 import javax.tools.JavaCompiler;
43 import javax.tools.JavaFileManager;
44 import javax.tools.JavaFileObject;
45 import javax.tools.SimpleJavaFileObject;
46 import javax.tools.ToolProvider;
47 import com.sun.source.tree.CompilationUnitTree;
48 import com.sun.source.tree.Tree;
49 import com.sun.source.tree.Tree.Kind;
50 import com.sun.source.util.JavacTask;
51 import com.sun.source.util.SourcePositions;
52 import com.sun.source.util.TreeScanner;
53 import com.sun.source.util.Trees;
54 
55 public class TreeEndPosTest {
getJavaFileManager(JavaCompiler compiler, DiagnosticCollector dc)56     private static JavaFileManager getJavaFileManager(JavaCompiler compiler,
57             DiagnosticCollector dc) {
58         return compiler.getStandardFileManager(dc, null, null);
59     }
60 
61     static class JavaSource extends SimpleJavaFileObject {
62 
63         final String source;
64         int startPos;
65         int endPos;
66 
JavaSource(String filename, String source)67         private JavaSource(String filename, String source) {
68             super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
69             this.source = source;
70         }
71 
72         @Override
getCharContent(boolean ignoreEncodingErrors)73         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
74             return source;
75         }
76 
createJavaSource(String preamble, String body, String postamble, String expected)77         static JavaSource createJavaSource(String preamble, String body,
78                 String postamble, String expected) {
79             JavaSource js = createJavaSource(preamble, body, postamble, -1, -1);
80             js.startPos = js.source.indexOf(expected);
81             js.endPos   = js.startPos + expected.length();
82             return js;
83         }
84 
createJavaSource(String body, String expected)85         static JavaSource createJavaSource(String body, String expected) {
86             return createJavaSource(null, body, null, expected);
87         }
88 
createJavaSource(String preamble, String body, String postamble, int start, int end)89         private static JavaSource createJavaSource(String preamble, String body,
90                 String postamble, int start, int end) {
91             final String name = "Bug";
92             StringBuilder code = new StringBuilder();
93             if (preamble != null) {
94                 code.append(preamble);
95             }
96             code.append("public class " + name + "{");
97             if (body != null) {
98                 code.append(body);
99             }
100             code.append("}");
101             if (postamble != null) {
102                 code.append(postamble);
103             }
104             JavaSource js = new JavaSource(name + ".java", code.toString());
105             js.startPos = start;
106             js.endPos = end;
107             return js;
108         }
109 
createFullJavaSource(String code)110         static JavaSource createFullJavaSource(String code) {
111             final String name = "Bug";
112             String[] parts = code.split("\\|", 3);
113             JavaSource js = new JavaSource(name + ".java", parts[0] + parts[1] + parts[2]);
114             js.startPos = parts[0].length();
115             js.endPos = parts[0].length() + parts[1].length();
116             return js;
117         }
118     }
119 
main(String... args)120     public static void main(String... args) throws IOException {
121         testUninitializedVariable();
122         testMissingAnnotationValue();
123         testUnresolvableAnnotationAttribute();
124         testFinalVariableWithDefaultConstructor();
125         testFinalVariableWithConstructor();
126         testWholeTextSpan();
127     }
128 
testUninitializedVariable()129     static void testUninitializedVariable() throws IOException {
130         compile(JavaSource.createJavaSource("Object o = new A().new B(); class A { }",
131                 "B()"));
132     }
testMissingAnnotationValue()133     static void testMissingAnnotationValue() throws IOException {
134         compile(JavaSource.createJavaSource("@Foo(\"vvvv\")",
135                 null, "@interface Foo { }", "\"vvvv\""));
136     }
137 
testUnresolvableAnnotationAttribute()138     static void testUnresolvableAnnotationAttribute() throws IOException {
139         compile(JavaSource.createJavaSource("@Foo(value=\"vvvv\")",
140                 null, "@interface Foo { }", "value"));
141     }
142 
testFinalVariableWithDefaultConstructor()143     static void testFinalVariableWithDefaultConstructor() throws IOException {
144         compile(JavaSource.createJavaSource("private static final String Foo; public void bar() { }",
145                 "private static final String Foo;"));
146     }
147 
testFinalVariableWithConstructor()148     static void testFinalVariableWithConstructor() throws IOException {
149         compile(JavaSource.createJavaSource("public Bug (){} private static final String Foo; public void bar() { }",
150                 "{}"));
151     }
152 
testWholeTextSpan()153     static void testWholeTextSpan() throws IOException {
154         treeSpan(JavaSource.createFullJavaSource("|class X    |"));
155     }
156 
compile(JavaSource src)157     static void compile(JavaSource src) throws IOException {
158         ByteArrayOutputStream ba = new ByteArrayOutputStream();
159         PrintWriter writer = new PrintWriter(ba);
160         File tempDir = new File(".");
161         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
162         DiagnosticCollector dc = new DiagnosticCollector();
163         try (JavaFileManager javaFileManager = getJavaFileManager(compiler, dc)) {
164             List<String> options = new ArrayList<>();
165             options.add("-cp");
166             options.add(tempDir.getPath());
167             options.add("-d");
168             options.add(tempDir.getPath());
169             options.add("--should-stop=at=GENERATE");
170 
171             List<JavaFileObject> sources = new ArrayList<>();
172             sources.add(src);
173             JavaCompiler.CompilationTask task =
174                     compiler.getTask(writer, javaFileManager,
175                     dc, options, null,
176                     sources);
177             task.call();
178             for (Diagnostic diagnostic : (List<Diagnostic>) dc.getDiagnostics()) {
179                 long actualStart = diagnostic.getStartPosition();
180                 long actualEnd = diagnostic.getEndPosition();
181                 System.out.println("Source: " + src.source);
182                 System.out.println("Diagnostic: " + diagnostic);
183                 System.out.print("Start position: Expected: " + src.startPos);
184                 System.out.println(", Actual: " + actualStart);
185                 System.out.print("End position: Expected: " + src.endPos);
186                 System.out.println(", Actual: " + actualEnd);
187                 if (src.startPos != actualStart || src.endPos != actualEnd) {
188                     throw new RuntimeException("error: trees don't match");
189                 }
190             }
191         }
192     }
193 
treeSpan(JavaSource src)194     static void treeSpan(JavaSource src) throws IOException {
195         ByteArrayOutputStream ba = new ByteArrayOutputStream();
196         PrintWriter writer = new PrintWriter(ba);
197         File tempDir = new File(".");
198         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
199         DiagnosticCollector dc = new DiagnosticCollector();
200         try (JavaFileManager javaFileManager = getJavaFileManager(compiler, dc)) {
201             List<String> options = new ArrayList<>();
202             options.add("-cp");
203             options.add(tempDir.getPath());
204             options.add("-d");
205             options.add(tempDir.getPath());
206             options.add("--should-stop=at=GENERATE");
207 
208             List<JavaFileObject> sources = new ArrayList<>();
209             sources.add(src);
210             JavacTask task = (JavacTask) compiler.getTask(writer, javaFileManager,
211                                                           dc, options, null,
212                                                           sources);
213             SourcePositions sp = Trees.instance(task).getSourcePositions();
214             boolean[] found = new boolean[1];
215             new TreeScanner<Void, Void>() {
216                 CompilationUnitTree cut;
217                 @Override
218                 public Void scan(Tree tree, Void p) {
219                     if (tree == null)
220                         return null;
221                     if (tree.getKind() == Kind.COMPILATION_UNIT) {
222                         cut = (CompilationUnitTree) tree;
223                     }
224                     found[0] |= (sp.getStartPosition(cut, tree) == src.startPos) &&
225                                 (sp.getEndPosition(cut, tree) == src.endPos);
226                     return super.scan(tree, p);
227                 }
228             }.scan(task.parse(), null);
229 
230             if (!found[0]) {
231                 throw new IllegalStateException();
232             }
233         }
234     }
235 }
236