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 8223782
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         "switch (x) {",
190         "var v = switch (x) {",
191         "var v = switch (x) { case ",
192         "var v = switch (x) { case 0:",
193         "var v = switch (x) { case 0: break 12; ",
194     };
195 
196     static final String[] unknown = new String[] {
197         "new ;"
198     };
199 
200     static final Map<Completeness, String[]> statusToCases = new HashMap<>();
201     static {
statusToCases.put(COMPLETE, complete)202         statusToCases.put(COMPLETE, complete);
statusToCases.put(COMPLETE_WITH_SEMI, complete_with_semi)203         statusToCases.put(COMPLETE_WITH_SEMI, complete_with_semi);
statusToCases.put(CONSIDERED_INCOMPLETE, considered_incomplete)204         statusToCases.put(CONSIDERED_INCOMPLETE, considered_incomplete);
statusToCases.put(DEFINITELY_INCOMPLETE, definitely_incomplete)205         statusToCases.put(DEFINITELY_INCOMPLETE, definitely_incomplete);
206     }
207 
assertStatus(String input, Completeness status, String source)208     private void assertStatus(String input, Completeness status, String source) {
209         String augSrc;
210         switch (status) {
211             case COMPLETE_WITH_SEMI:
212                 augSrc = source + ";";
213                 break;
214 
215             case DEFINITELY_INCOMPLETE:
216                 augSrc = null;
217                 break;
218 
219             case CONSIDERED_INCOMPLETE:
220                 augSrc = source + ";";
221                 break;
222 
223             case EMPTY:
224             case COMPLETE:
225             case UNKNOWN:
226                 augSrc = source;
227                 break;
228 
229             default:
230                 throw new AssertionError();
231         }
232         assertAnalyze(input, status, augSrc);
233     }
234 
assertStatus(String[] ins, Completeness status)235     private void assertStatus(String[] ins, Completeness status) {
236         for (String input : ins) {
237             assertStatus(input, status, input);
238         }
239     }
240 
test_complete()241     public void test_complete() {
242         assertStatus(complete, COMPLETE);
243     }
244 
test_expression()245     public void test_expression() {
246         assertStatus(expression, COMPLETE);
247     }
248 
test_complete_with_semi()249     public void test_complete_with_semi() {
250         assertStatus(complete_with_semi, COMPLETE_WITH_SEMI);
251     }
252 
test_considered_incomplete()253     public void test_considered_incomplete() {
254         assertStatus(considered_incomplete, CONSIDERED_INCOMPLETE);
255     }
256 
test_definitely_incomplete()257     public void test_definitely_incomplete() {
258         assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
259     }
260 
test_unknown()261     public void test_unknown() {
262         assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE);
263     }
264 
testCompleted_complete_with_semi()265     public void testCompleted_complete_with_semi() {
266         for (String in : complete_with_semi) {
267             String input = in + ";";
268             assertStatus(input, COMPLETE, input);
269         }
270     }
271 
testCompleted_expression_with_semi()272     public void testCompleted_expression_with_semi() {
273         for (String in : expression) {
274             String input = in + ";";
275             assertStatus(input, COMPLETE, input);
276         }
277     }
278 
testCompleted_considered_incomplete()279     public void testCompleted_considered_incomplete() {
280         for (String in : considered_incomplete) {
281             String input = in + ";";
282             assertStatus(input, COMPLETE, input);
283         }
284     }
285 
assertSourceByStatus(String first)286     private void assertSourceByStatus(String first) {
287         for (Map.Entry<Completeness, String[]> e : statusToCases.entrySet()) {
288             for (String in : e.getValue()) {
289                 String input = first + in;
290                 assertAnalyze(input, COMPLETE, first, in, true);
291             }
292         }
293     }
294 
testCompleteSource_complete()295     public void testCompleteSource_complete() {
296         for (String input : complete) {
297             assertSourceByStatus(input);
298         }
299     }
300 
testCompleteSource_complete_with_semi()301     public void testCompleteSource_complete_with_semi() {
302         for (String in : complete_with_semi) {
303             String input = in + ";";
304             assertSourceByStatus(input);
305         }
306     }
307 
testCompleteSource_expression()308     public void testCompleteSource_expression() {
309         for (String in : expression) {
310             String input = in + ";";
311             assertSourceByStatus(input);
312         }
313     }
314 
testCompleteSource_considered_incomplete()315     public void testCompleteSource_considered_incomplete() {
316         for (String in : considered_incomplete) {
317             String input = in + ";";
318             assertSourceByStatus(input);
319         }
320     }
321 
testTrailingSlash()322     public void testTrailingSlash() {
323         assertStatus("\"abc\\", UNKNOWN, "\"abc\\");
324     }
325 
testOpenComment()326     public void testOpenComment() {
327         assertStatus("int xx; /* hello", DEFINITELY_INCOMPLETE, null);
328         assertStatus("/**  test", DEFINITELY_INCOMPLETE, null);
329     }
330 
testTextBlocks()331     public void testTextBlocks() {
332         assertStatus("\"\"\"", DEFINITELY_INCOMPLETE, null);
333         assertStatus("\"\"\"broken", DEFINITELY_INCOMPLETE, null);
334         assertStatus("\"\"\"\ntext", DEFINITELY_INCOMPLETE, null);
335         assertStatus("\"\"\"\ntext\"\"", DEFINITELY_INCOMPLETE, "\"\"\"\ntext\"\"\"");
336         assertStatus("\"\"\"\ntext\"\"\"", COMPLETE, "\"\"\"\ntext\"\"\"");
337         assertStatus("\"\"\"\ntext\\\"\"\"\"", COMPLETE, "\"\"\"\ntext\\\"\"\"\"");
338         assertStatus("\"\"\"\ntext\\\"\"\"", DEFINITELY_INCOMPLETE, null);
339         assertStatus("\"\"\"\ntext\\\"\"\"\\\"\"\"", DEFINITELY_INCOMPLETE, null);
340         assertStatus("\"\"\"\ntext\\\"\"\"\\\"\"\"\"\"\"", COMPLETE, "\"\"\"\ntext\\\"\"\"\\\"\"\"\"\"\"");
341     }
342 
testMiscSource()343     public void testMiscSource() {
344         assertStatus("if (t) if ", DEFINITELY_INCOMPLETE, "if (t) if"); //Bug
345         assertStatus("int m() {} dfd", COMPLETE, "int m() {}");
346         assertStatus("int p = ", DEFINITELY_INCOMPLETE, "int p ="); //Bug
347         assertStatus("int[] m = {1, 2}, n = new int[0];  int i;", COMPLETE,
348                      "int[] m = {1, 2}, n = new int[0];");
349     }
350 }
351