1 /*
2  * Copyright (c) 2017, 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 8188225 8204674
27  * @summary Check that variables of type var have a consistent model
28  * @modules jdk.compiler/com.sun.tools.javac.api
29  */
30 
31 import com.sun.source.tree.CompilationUnitTree;
32 import com.sun.tools.javac.api.JavacTaskImpl;
33 
34 import java.io.IOException;
35 import java.net.URI;
36 import java.util.Arrays;
37 import java.util.List;
38 
39 import javax.tools.JavaCompiler;
40 import javax.tools.JavaFileObject;
41 import javax.tools.SimpleJavaFileObject;
42 import javax.tools.ToolProvider;
43 
44 import com.sun.source.tree.VariableTree;
45 import com.sun.source.util.JavacTask;
46 import com.sun.source.util.TreeScanner;
47 import com.sun.source.util.Trees;
48 
49 public class VarTree {
50     private final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
51 
main(String... args)52     public static void main(String... args) throws Exception {
53         VarTree test = new VarTree();
54         test.run("|var testVar = 0;| ",
55                  "int testVar = 0");
56         test.run("|var testVar = 0;| undef undef;",
57                  "int testVar = 0");
58         test.run("|final var testVar = 0;| ",
59                  "final int testVar = 0");
60         test.run("for (|var testVar| : java.util.Arrays.asList(0, 1)) {}",
61                  "java.lang.Integer testVar");
62         test.run("for (|final var testVar| : java.util.Arrays.asList(0, 1)) {}",
63                  "final java.lang.Integer testVar");
64         test.run("java.util.function.Consumer<String> c = |testVar| -> {};",
65                  "java.lang.String testVar");
66         test.run("java.util.function.Consumer<String> c = (|testVar|) -> {};",
67                  "java.lang.String testVar");
68         test.run("java.util.function.Consumer<String> c = (|var testVar|) -> {};",
69                  "java.lang.String testVar");
70         test.run("java.util.function.IntBinaryOperator c = (var x, |testType|) -> 1;",
71                  "testType ");
72     }
73 
run(String code, String expected)74     void run(String code, String expected) throws IOException {
75         String[] parts = code.split("\\|");
76 
77         if (parts.length != 3) {
78             throw new IllegalStateException("Incorrect number of markers.");
79         }
80 
81         String prefix = "public class Test { void test() { ";
82         String src = prefix + parts[0] + parts[1] + parts[2] + " } }";
83 
84         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, d -> {},
85                                                         List.of("--should-stop=at=FLOW"),
86                                                         null, Arrays.asList(new MyFileObject(src)));
87 
88         Iterable<? extends CompilationUnitTree> units = ct.parse();
89 
90         runSpanCheck(ct, units, src, prefix.length() + parts[0].length(), prefix.length() + parts[0].length() + parts[1].length());
91 
92         ct.analyze();
93 
94         runSpanCheck(ct, units, src, prefix.length() + parts[0].length(), prefix.length() + parts[0].length() + parts[1].length());
95 
96         for (CompilationUnitTree cut : units) {
97             new TreeScanner<Void, Void>() {
98                 @Override
99                 public Void visitVariable(VariableTree node, Void p) {
100                     if (node.getName().contentEquals("testVar")) {
101                         if (!expected.equals(node.toString())) {
102                             throw new AssertionError("Unexpected tree: " + node.toString());
103                         }
104                     }
105                     if (String.valueOf(node.getType()).equals("testType")) {
106                         if (!expected.equals(node.toString())) {
107                             throw new AssertionError("Unexpected tree: " + node.toString());
108                         }
109                     }
110                     return super.visitVariable(node, p);
111                 }
112 
113             }.scan(cut, null);
114         }
115     }
116 
runSpanCheck(JavacTask ct, Iterable<? extends CompilationUnitTree> units, String src, int spanStart, int spanEnd)117     private void runSpanCheck(JavacTask ct, Iterable<? extends CompilationUnitTree> units, String src, int spanStart, int spanEnd) {
118         Trees trees = Trees.instance(ct);
119         boolean[] found = new boolean[1];
120 
121         for (CompilationUnitTree cut : units) {
122             new TreeScanner<Void, Void>() {
123                 @Override
124                 public Void visitVariable(VariableTree node, Void p) {
125                     if (node.getName().contentEquals("testVar")) {
126                         int start = (int) trees.getSourcePositions().getStartPosition(cut, node);
127                         int end   = (int) trees.getSourcePositions().getEndPosition(cut, node);
128 
129                         String snip = src.substring(start, end);
130 
131                         if (start != spanStart || end != spanEnd) {
132                             throw new AssertionError("Unexpected span: " + snip);
133                         }
134 
135                         int typeStart = (int) trees.getSourcePositions().getStartPosition(cut, node.getType());
136                         int typeEnd   = (int) trees.getSourcePositions().getEndPosition(cut, node.getType());
137 
138                         if (typeStart != (-1) && typeEnd != (-1)) {
139                             throw new AssertionError("Unexpected type position: " + typeStart + ", " + typeEnd);
140                         }
141 
142                         found[0] = true;
143                     }
144                     if (String.valueOf(node.getType()).equals("testType")) {
145                         int start = (int) trees.getSourcePositions().getStartPosition(cut, node);
146                         int end   = (int) trees.getSourcePositions().getEndPosition(cut, node);
147 
148                         String snip = src.substring(start, end);
149 
150                         if (start != spanStart || end != spanEnd) {
151                             throw new AssertionError("Unexpected span: " + snip);
152                         }
153 
154                         found[0] = true;
155                     }
156                     return super.visitVariable(node, p);
157                 }
158 
159             }.scan(cut, null);
160         }
161 
162         if (!found[0]) {
163             throw new AssertionError("Didn't find the test variable.");
164         }
165     }
166 
167     class MyFileObject extends SimpleJavaFileObject {
168 
169         private String text;
170 
MyFileObject(String text)171         public MyFileObject(String text) {
172             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
173             this.text = text;
174         }
175 
176         @Override
getCharContent(boolean ignoreEncodingErrors)177         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
178             return text;
179         }
180     }
181 }
182