1 /*
2  * Copyright (c) 2015, 2016, 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 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268
27  * @summary Test SourceCodeAnalysis
28  * @build KullaTesting TestingInputStream
29  * @run testng CompletenessTest
30  */
31 
32 import java.util.Map;
33 import java.util.HashMap;
34 
35 import org.testng.annotations.Test;
36 import jdk.jshell.SourceCodeAnalysis.Completeness;
37 
38 import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
39 
40 @Test
41 public class CompletenessTest extends KullaTesting {
42 
43     // Add complete units that end with semicolon to complete_with_semi (without
44     // the semicolon).  Both cases will be tested.
45     static final String[] complete = new String[] {
46         "{ x= 4; }",
47         "int mm(int x) {kll}",
48         "if (t) { ddd; }",
49         "for (int i = 0; i < lines.length(); ++i) { foo }",
50         "while (ct == null) { switch (current.kind) { case EOF: { } } }",
51         "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \"Unmatched \" + unmatched); }",
52         "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); }",
53         "List<T> f() { return null; }",
54         "List<?> f() { return null; }",
55         "List<? extends Object> f() { return null; }",
56         "Map<? extends Object, ? super Object> f() { return null; }",
57         "class C { int z; }",
58         "synchronized (r) { f(); }",
59         "try { } catch (Exception ex) { }",
60         "try { } catch (Exception ex) { } finally { }",
61         "try { } finally { }",
62         "try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)) { }",
63         "foo: while (true) { printf(\"Innn\"); break foo; }",
64         "class Case<E1 extends Enum<E1>, E2 extends Enum<E2>, E3 extends Enum<E3>> {}",
65         ";",
66         "enum Tt { FOO, BAR, BAZ,; }"
67     };
68 
69     static final String[] expression = new String[] {
70         "test",
71         "x + y",
72         "x + y ++",
73         "p = 9",
74         "match(BRACKETS, TokenKind.LBRACKET)",
75         "new C()",
76         "new C() { public String toString() { return \"Hi\"; } }",
77         "new int[]",
78         "new int[] {1, 2,3}",
79         "new Foo() {}",
80         "i >= 0 && Character.isWhitespace(s.charAt(i))",
81         "int.class",
82         "String.class",
83     };
84 
85     static final String[] complete_with_semi = new String[] {
86         "int mm",
87         "if (t) ddd",
88         "int p = 9",
89         "int p",
90         "Deque<Token> stack = new ArrayDeque<>()",
91         "final Deque<Token> stack = new ArrayDeque<>()",
92         "java.util.Scanner input = new java.util.Scanner(System.in)",
93         "java.util.Scanner input = new java.util.Scanner(System.in) { }",
94         "int j = -i",
95         "String[] a = { \"AAA\" }",
96         "assert true",
97         "int path[]",
98         "int path[][]",
99         "int path[][] = new int[22][]",
100         "int path[] = new int[22]",
101         "int path[] = new int[] {1, 2, 3}",
102         "int[] path",
103         "int path[] = new int[22]",
104         "int path[][] = new int[22][]",
105         "for (Object o : a) System.out.println(\"Yep\")",
106         "while (os == null) System.out.println(\"Yep\")",
107         "do f(); while (t)",
108         "if (os == null) System.out.println(\"Yep\")",
109         "if (t) if (!t) System.out.println(123)",
110         "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else break",
111         "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else continue",
112         "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else return",
113         "throw ex",
114         "C c = new C()",
115         "java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)",
116         "BufferedReader br = new BufferedReader(new FileReader(path))",
117         "bar: g()",
118         "baz: while (true) if (t()) printf('-'); else break baz",
119         "java.util.function.IntFunction<int[]> ggg = int[]::new",
120         "List<? extends Object> l",
121         "int[] m = {1, 2}",
122         "int[] m = {1, 2}, n = null",
123         "int[] m = {1, 2}, n",
124         "int[] m = {1, 2}, n = {3, 4}",
125     };
126 
127     static final String[] considered_incomplete = new String[] {
128         "if (t)",
129         "if (t) { } else",
130         "if (t) if (!t)",
131         "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE))",
132         "for (int i = 0; i < 10; ++i)",
133         "while (os == null)",
134     };
135 
136     static final String[] definitely_incomplete = new String[] {
137         "int mm(",
138         "int mm(int x",
139         "int mm(int x)",
140         "int mm(int x) {",
141         "int mm(int x) {kll",
142         "if",
143         "if (",
144         "if (t",
145         "if (t) {",
146         "if (t) { ddd",
147         "if (t) { ddd;",
148         "if (t) if (",
149         "if (stack.isEmpty()) {",
150         "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) {",
151         "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \"Unmatched \" + unmatched);",
152         "x +",
153         "x *",
154         "3 *",
155         "int",
156         "for (int i = 0; i < lines.length(); ++i) {",
157         "new",
158         "new C(",
159         "new int[",
160         "new int[] {1, 2,3",
161         "new int[] {",
162         "while (ct == null) {",
163         "while (ct == null) { switch (current.kind) {",
164         "while (ct == null) { switch (current.kind) { case EOF: {",
165         "while (ct == null) { switch (current.kind) { case EOF: { } }",
166         "enum TK {",
167         "enum TK { EOF(TokenKind.EOF, 0),",
168         "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM)",
169         "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); ",
170         "enum Tt { FOO, BAR, BAZ,;",
171         "class C",
172         "class C extends D",
173         "class C implements D",
174         "class C implements D, E",
175         "interface I extends D",
176         "interface I extends D, E",
177         "enum E",
178         "enum E implements I1",
179         "enum E implements I1, I2",
180         "@interface Anno",
181         "void f()",
182         "void f() throws E",
183         "@A(",
184         "int n = 4,",
185         "int n,",
186         "int[] m = {1, 2},",
187         "int[] m = {1, 2}, n = {3, 4},",
188         "Map<String,"
189     };
190 
191     static final String[] unknown = new String[] {
192         "new ;"
193     };
194 
195     static final Map<Completeness, String[]> statusToCases = new HashMap<>();
196     static {
statusToCases.put(COMPLETE, complete)197         statusToCases.put(COMPLETE, complete);
statusToCases.put(COMPLETE_WITH_SEMI, complete_with_semi)198         statusToCases.put(COMPLETE_WITH_SEMI, complete_with_semi);
statusToCases.put(CONSIDERED_INCOMPLETE, considered_incomplete)199         statusToCases.put(CONSIDERED_INCOMPLETE, considered_incomplete);
statusToCases.put(DEFINITELY_INCOMPLETE, definitely_incomplete)200         statusToCases.put(DEFINITELY_INCOMPLETE, definitely_incomplete);
201     }
202 
assertStatus(String input, Completeness status, String source)203     private void assertStatus(String input, Completeness status, String source) {
204         String augSrc;
205         switch (status) {
206             case COMPLETE_WITH_SEMI:
207                 augSrc = source + ";";
208                 break;
209 
210             case DEFINITELY_INCOMPLETE:
211                 augSrc = null;
212                 break;
213 
214             case CONSIDERED_INCOMPLETE:
215                 augSrc = source + ";";
216                 break;
217 
218             case EMPTY:
219             case COMPLETE:
220             case UNKNOWN:
221                 augSrc = source;
222                 break;
223 
224             default:
225                 throw new AssertionError();
226         }
227         assertAnalyze(input, status, augSrc);
228     }
229 
assertStatus(String[] ins, Completeness status)230     private void assertStatus(String[] ins, Completeness status) {
231         for (String input : ins) {
232             assertStatus(input, status, input);
233         }
234     }
235 
test_complete()236     public void test_complete() {
237         assertStatus(complete, COMPLETE);
238     }
239 
test_expression()240     public void test_expression() {
241         assertStatus(expression, COMPLETE);
242     }
243 
test_complete_with_semi()244     public void test_complete_with_semi() {
245         assertStatus(complete_with_semi, COMPLETE_WITH_SEMI);
246     }
247 
test_considered_incomplete()248     public void test_considered_incomplete() {
249         assertStatus(considered_incomplete, CONSIDERED_INCOMPLETE);
250     }
251 
test_definitely_incomplete()252     public void test_definitely_incomplete() {
253         assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
254     }
255 
test_unknown()256     public void test_unknown() {
257         assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
258     }
259 
testCompleted_complete_with_semi()260     public void testCompleted_complete_with_semi() {
261         for (String in : complete_with_semi) {
262             String input = in + ";";
263             assertStatus(input, COMPLETE, input);
264         }
265     }
266 
testCompleted_expression_with_semi()267     public void testCompleted_expression_with_semi() {
268         for (String in : expression) {
269             String input = in + ";";
270             assertStatus(input, COMPLETE, input);
271         }
272     }
273 
testCompleted_considered_incomplete()274     public void testCompleted_considered_incomplete() {
275         for (String in : considered_incomplete) {
276             String input = in + ";";
277             assertStatus(input, COMPLETE, input);
278         }
279     }
280 
assertSourceByStatus(String first)281     private void assertSourceByStatus(String first) {
282         for (Map.Entry<Completeness, String[]> e : statusToCases.entrySet()) {
283             for (String in : e.getValue()) {
284                 String input = first + in;
285                 assertAnalyze(input, COMPLETE, first, in, true);
286             }
287         }
288     }
289 
testCompleteSource_complete()290     public void testCompleteSource_complete() {
291         for (String input : complete) {
292             assertSourceByStatus(input);
293         }
294     }
295 
testCompleteSource_complete_with_semi()296     public void testCompleteSource_complete_with_semi() {
297         for (String in : complete_with_semi) {
298             String input = in + ";";
299             assertSourceByStatus(input);
300         }
301     }
302 
testCompleteSource_expression()303     public void testCompleteSource_expression() {
304         for (String in : expression) {
305             String input = in + ";";
306             assertSourceByStatus(input);
307         }
308     }
309 
testCompleteSource_considered_incomplete()310     public void testCompleteSource_considered_incomplete() {
311         for (String in : considered_incomplete) {
312             String input = in + ";";
313             assertSourceByStatus(input);
314         }
315     }
316 
testTrailingSlash()317     public void testTrailingSlash() {
318         assertStatus("\"abc\\", UNKNOWN, "\"abc\\");
319     }
320 
testOpenComment()321     public void testOpenComment() {
322         assertStatus("int xx; /* hello", DEFINITELY_INCOMPLETE, null);
323         assertStatus("/**  test", DEFINITELY_INCOMPLETE, null);
324     }
325 
testMiscSource()326     public void testMiscSource() {
327         assertStatus("if (t) if ", DEFINITELY_INCOMPLETE, "if (t) if"); //Bug
328         assertStatus("int m() {} dfd", COMPLETE, "int m() {}");
329         assertStatus("int p = ", DEFINITELY_INCOMPLETE, "int p ="); //Bug
330         assertStatus("int[] m = {1, 2}, n = new int[0];  int i;", COMPLETE,
331                      "int[] m = {1, 2}, n = new int[0];");
332     }
333 }
334