/* * Copyright (c) 2013, 2018, 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 8017216 8019422 8019421 8054956 8205418 * @summary verify start and end positions * @modules java.compiler * jdk.compiler * @run main TreeEndPosTest */ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.URI; import java.util.ArrayList; import java.util.List; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.ToolProvider; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; import com.sun.source.util.JavacTask; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreeScanner; import com.sun.source.util.Trees; public class TreeEndPosTest { private static JavaFileManager getJavaFileManager(JavaCompiler compiler, DiagnosticCollector dc) { return compiler.getStandardFileManager(dc, null, null); } static class JavaSource extends SimpleJavaFileObject { final String source; int startPos; int endPos; private JavaSource(String filename, String source) { super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE); this.source = source; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return source; } static JavaSource createJavaSource(String preamble, String body, String postamble, String expected) { JavaSource js = createJavaSource(preamble, body, postamble, -1, -1); js.startPos = js.source.indexOf(expected); js.endPos = js.startPos + expected.length(); return js; } static JavaSource createJavaSource(String body, String expected) { return createJavaSource(null, body, null, expected); } private static JavaSource createJavaSource(String preamble, String body, String postamble, int start, int end) { final String name = "Bug"; StringBuilder code = new StringBuilder(); if (preamble != null) { code.append(preamble); } code.append("public class " + name + "{"); if (body != null) { code.append(body); } code.append("}"); if (postamble != null) { code.append(postamble); } JavaSource js = new JavaSource(name + ".java", code.toString()); js.startPos = start; js.endPos = end; return js; } static JavaSource createFullJavaSource(String code) { final String name = "Bug"; String[] parts = code.split("\\|", 3); JavaSource js = new JavaSource(name + ".java", parts[0] + parts[1] + parts[2]); js.startPos = parts[0].length(); js.endPos = parts[0].length() + parts[1].length(); return js; } } public static void main(String... args) throws IOException { testUninitializedVariable(); testMissingAnnotationValue(); testUnresolvableAnnotationAttribute(); testFinalVariableWithDefaultConstructor(); testFinalVariableWithConstructor(); testWholeTextSpan(); } static void testUninitializedVariable() throws IOException { compile(JavaSource.createJavaSource("Object o = new A().new BT(); class A { }", "BT")); } static void testMissingAnnotationValue() throws IOException { compile(JavaSource.createJavaSource("@Foo(\"vvvv\")", null, "@interface Foo { }", "\"vvvv\"")); } static void testUnresolvableAnnotationAttribute() throws IOException { compile(JavaSource.createJavaSource("@Foo(value=\"vvvv\")", null, "@interface Foo { }", "value")); } static void testFinalVariableWithDefaultConstructor() throws IOException { compile(JavaSource.createJavaSource("private static final String Foo; public void bar() { }", "private static final String Foo;")); } static void testFinalVariableWithConstructor() throws IOException { compile(JavaSource.createJavaSource("public Bug (){} private static final String Foo; public void bar() { }", "private static final String Foo;")); } static void testWholeTextSpan() throws IOException { treeSpan(JavaSource.createFullJavaSource("|class X |")); } static void compile(JavaSource src) throws IOException { ByteArrayOutputStream ba = new ByteArrayOutputStream(); PrintWriter writer = new PrintWriter(ba); File tempDir = new File("."); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector dc = new DiagnosticCollector(); try (JavaFileManager javaFileManager = getJavaFileManager(compiler, dc)) { List options = new ArrayList<>(); options.add("-cp"); options.add(tempDir.getPath()); options.add("-d"); options.add(tempDir.getPath()); options.add("--should-stop=at=GENERATE"); List sources = new ArrayList<>(); sources.add(src); JavaCompiler.CompilationTask task = compiler.getTask(writer, javaFileManager, dc, options, null, sources); task.call(); for (Diagnostic diagnostic : (List) dc.getDiagnostics()) { long actualStart = diagnostic.getStartPosition(); long actualEnd = diagnostic.getEndPosition(); System.out.println("Source: " + src.source); System.out.println("Diagnostic: " + diagnostic); System.out.print("Start position: Expected: " + src.startPos); System.out.println(", Actual: " + actualStart); System.out.print("End position: Expected: " + src.endPos); System.out.println(", Actual: " + actualEnd); if (src.startPos != actualStart || src.endPos != actualEnd) { throw new RuntimeException("error: trees don't match"); } } } } static void treeSpan(JavaSource src) throws IOException { ByteArrayOutputStream ba = new ByteArrayOutputStream(); PrintWriter writer = new PrintWriter(ba); File tempDir = new File("."); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector dc = new DiagnosticCollector(); try (JavaFileManager javaFileManager = getJavaFileManager(compiler, dc)) { List options = new ArrayList<>(); options.add("-cp"); options.add(tempDir.getPath()); options.add("-d"); options.add(tempDir.getPath()); options.add("--should-stop=at=GENERATE"); List sources = new ArrayList<>(); sources.add(src); JavacTask task = (JavacTask) compiler.getTask(writer, javaFileManager, dc, options, null, sources); SourcePositions sp = Trees.instance(task).getSourcePositions(); boolean[] found = new boolean[1]; new TreeScanner() { CompilationUnitTree cut; @Override public Void scan(Tree tree, Void p) { if (tree == null) return null; if (tree.getKind() == Kind.COMPILATION_UNIT) { cut = (CompilationUnitTree) tree; } found[0] |= (sp.getStartPosition(cut, tree) == src.startPos) && (sp.getEndPosition(cut, tree) == src.endPos); return super.scan(tree, p); } }.scan(task.parse(), null); if (!found[0]) { throw new IllegalStateException(); } } } }