| /* |
| * Copyright (c) 2010, 2015, 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 8041648 |
| * @summary Verify that end positions are sane if semicolons are missing. |
| * @modules jdk.compiler/com.sun.tools.javac.api |
| * jdk.compiler/com.sun.tools.javac.file |
| * jdk.compiler/com.sun.tools.javac.parser |
| * jdk.compiler/com.sun.tools.javac.tree |
| * jdk.compiler/com.sun.tools.javac.util |
| * @run main MissingSemicolonTest MissingSemicolonTest.java |
| */ |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.nio.file.Files; |
| import java.util.*; |
| |
| import javax.tools.*; |
| |
| import com.sun.source.tree.*; |
| import com.sun.source.tree.Tree.Kind; |
| import com.sun.source.util.*; |
| import com.sun.tools.javac.api.JavacTool; |
| import com.sun.tools.javac.parser.Scanner; |
| import com.sun.tools.javac.parser.ScannerFactory; |
| import com.sun.tools.javac.tree.JCTree; |
| import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; |
| import com.sun.tools.javac.util.Context; |
| |
| public class MissingSemicolonTest { |
| public static void main(String... args) throws IOException { |
| String testSrc = System.getProperty("test.src"); |
| File baseDir = new File(testSrc); |
| boolean ok = new MissingSemicolonTest().run(baseDir, args); |
| if (!ok) { |
| throw new Error("failed"); |
| } |
| } |
| |
| boolean run(File baseDir, String... args) throws IOException { |
| try { |
| if (args.length == 0) { |
| throw new IllegalStateException("Needs input files."); |
| } |
| |
| for (String arg : args) { |
| File file = new File(baseDir, arg); |
| if (file.exists()) |
| test(file); |
| else |
| error("File not found: " + file); |
| } |
| |
| System.err.println(fileCount + " files read"); |
| if (errors > 0) |
| System.err.println(errors + " errors"); |
| |
| return errors == 0; |
| } finally { |
| fm.close(); |
| } |
| } |
| |
| void test(File file) { |
| if (file.isFile() && file.getName().endsWith(".java")) { |
| try { |
| fileCount++; |
| String content = new String(Files.readAllBytes(file.toPath())); |
| List<int[]> spans = gatherTreeSpans(file, content); |
| int nextSemicolon = -1; |
| |
| //remove semicolons, one at a time, and verify the positions are still meaningful: |
| while ((nextSemicolon = content.indexOf(';', nextSemicolon + 1)) != (-1)) { |
| String updatedContent = |
| content.substring(0, nextSemicolon) + |
| " " + |
| content.substring(nextSemicolon + 1); |
| verifyTreeSpans(file, spans, updatedContent, nextSemicolon); |
| } |
| } catch (IOException e) { |
| error("Error reading " + file + ": " + e); |
| } |
| } |
| } |
| |
| public List<int[]> gatherTreeSpans(File file, String content) throws IOException { |
| JCCompilationUnit unit = read(file.toURI(), content); |
| List<int[]> spans = new ArrayList<>(); |
| new TreePathScanner<Void, Void>() { |
| @Override |
| public Void scan(Tree tree, Void p) { |
| if (tree != null) { |
| int start = ((JCTree) tree).getStartPosition(); |
| int end = ((JCTree) tree).getEndPosition(unit.endPositions); |
| |
| spans.add(new int[] {start, end}); |
| } |
| return super.scan(tree, p); |
| } |
| }.scan(unit, null); |
| return spans; |
| } |
| |
| public void verifyTreeSpans(File file, List<int[]> spans, |
| String updatedContent, int semicolon) throws IOException { |
| JCCompilationUnit updated = read(file.toURI(), updatedContent); |
| Iterator<int[]> nextSpan = spans.iterator(); |
| new TreePathScanner<Void, Void>() { |
| @Override |
| public Void scan(Tree tree, Void p) { |
| if (tree != null) { |
| int start = ((JCTree) tree).getStartPosition(); |
| int end = ((JCTree) tree).getEndPosition(updated.endPositions); |
| |
| if (tree.getKind() != Kind.ERRONEOUS) { |
| int[] expected = nextSpan.next(); |
| int expectedEnd = expected[1]; |
| |
| if (expectedEnd == semicolon + 1) { |
| Scanner scanner = scannerFactory.newScanner(updatedContent, true); |
| scanner.nextToken(); |
| while (scanner.token().pos < expectedEnd) |
| scanner.nextToken(); |
| expectedEnd = scanner.token().pos; |
| } |
| |
| if (expected[0] != start || expectedEnd != end) { |
| error(updatedContent + "; semicolon: " + semicolon + "; expected: " + |
| expected[0] + "-" + expectedEnd + "; found=" + start + "-" + end + |
| ";" + tree); |
| } |
| } |
| } |
| return super.scan(tree, p); |
| } |
| }.scan(updated, null); |
| } |
| |
| DiagnosticListener<JavaFileObject> devNull = (d) -> {}; |
| JavacTool tool = JavacTool.create(); |
| StandardJavaFileManager fm = tool.getStandardFileManager(devNull, null, null); |
| ScannerFactory scannerFactory = ScannerFactory.instance(new Context()); |
| |
| /** |
| * Read a file. |
| * @param file the file to be read |
| * @return the tree for the content of the file |
| * @throws IOException if any IO errors occur |
| * @throws MissingSemicolonTest.ParseException if any errors occur while parsing the file |
| */ |
| JCCompilationUnit read(URI uri, String content) throws IOException { |
| JavacTool tool = JavacTool.create(); |
| JavacTask task = tool.getTask(null, fm, devNull, Collections.<String>emptyList(), null, |
| Arrays.<JavaFileObject>asList(new JavaSource(uri, content))); |
| Iterable<? extends CompilationUnitTree> trees = task.parse(); |
| Iterator<? extends CompilationUnitTree> iter = trees.iterator(); |
| if (!iter.hasNext()) |
| throw new Error("no trees found"); |
| JCCompilationUnit t = (JCCompilationUnit) iter.next(); |
| if (iter.hasNext()) |
| throw new Error("too many trees found"); |
| return t; |
| } |
| |
| class JavaSource extends SimpleJavaFileObject { |
| |
| private final String content; |
| public JavaSource(URI uri, String content) { |
| super(uri, JavaFileObject.Kind.SOURCE); |
| this.content = content; |
| } |
| |
| @Override |
| public CharSequence getCharContent(boolean ignoreEncodingErrors) { |
| return content; |
| } |
| } |
| |
| /** |
| * Report an error. When the program is complete, the program will either |
| * exit or throw an Error if any errors have been reported. |
| * @param msg the error message |
| */ |
| void error(String msg) { |
| System.err.println(msg); |
| errors++; |
| } |
| |
| /** Number of files that have been analyzed. */ |
| int fileCount; |
| /** Number of errors reported. */ |
| int errors; |
| |
| } |
| |
| class TestCase { |
| String str1; |
| String str2; |
| public TestCase() { |
| super(); |
| super.hashCode(); |
| } |
| public TestCase(String str1, String str2) { |
| super(); |
| this.str1 = str1; |
| this.str2 = str2; |
| assert true; |
| } |
| |
| void newClass() { |
| new String(); |
| new String(); |
| } |
| |
| void localVars() { |
| String str1 = ""; |
| String str2; |
| String str3; |
| final String str4; |
| } |
| |
| void throwsException() { |
| throw new IllegalStateException(); |
| } |
| |
| int returnWithExpression() { |
| return 1; |
| } |
| |
| void returnWithoutExpression() { |
| return ; |
| } |
| |
| void doWhileBreakContinue() { |
| do { |
| if (true) |
| break; |
| if (false) |
| continue; |
| } while(true); |
| } |
| |
| void labelled() { |
| LABEL: doWhileBreakContinue(); |
| } |
| |
| } |