/* * Copyright (c) 2015, 2016, 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 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 * @summary Test SourceCodeAnalysis * @build KullaTesting TestingInputStream * @run testng CompletenessTest */ import java.util.Map; import java.util.HashMap; import org.testng.annotations.Test; import jdk.jshell.SourceCodeAnalysis.Completeness; import static jdk.jshell.SourceCodeAnalysis.Completeness.*; @Test public class CompletenessTest extends KullaTesting { // Add complete units that end with semicolon to complete_with_semi (without // the semicolon). Both cases will be tested. static final String[] complete = new String[] { "{ x= 4; }", "int mm(int x) {kll}", "if (t) { ddd; }", "for (int i = 0; i < lines.length(); ++i) { foo }", "while (ct == null) { switch (current.kind) { case EOF: { } } }", "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \"Unmatched \" + unmatched); }", "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); }", "List<T> f() { return null; }", "List<?> f() { return null; }", "List<? extends Object> f() { return null; }", "Map<? extends Object, ? super Object> f() { return null; }", "class C { int z; }", "synchronized (r) { f(); }", "try { } catch (Exception ex) { }", "try { } catch (Exception ex) { } finally { }", "try { } finally { }", "try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)) { }", "foo: while (true) { printf(\"Innn\"); break foo; }", "class Case<E1 extends Enum<E1>, E2 extends Enum<E2>, E3 extends Enum<E3>> {}", ";", "enum Tt { FOO, BAR, BAZ,; }" }; static final String[] expression = new String[] { "test", "x + y", "x + y ++", "p = 9", "match(BRACKETS, TokenKind.LBRACKET)", "new C()", "new C() { public String toString() { return \"Hi\"; } }", "new int[]", "new int[] {1, 2,3}", "new Foo() {}", "i >= 0 && Character.isWhitespace(s.charAt(i))", "int.class", "String.class", }; static final String[] complete_with_semi = new String[] { "int mm", "if (t) ddd", "int p = 9", "int p", "Deque<Token> stack = new ArrayDeque<>()", "final Deque<Token> stack = new ArrayDeque<>()", "java.util.Scanner input = new java.util.Scanner(System.in)", "java.util.Scanner input = new java.util.Scanner(System.in) { }", "int j = -i", "String[] a = { \"AAA\" }", "assert true", "int path[]", "int path[][]", "int path[][] = new int[22][]", "int path[] = new int[22]", "int path[] = new int[] {1, 2, 3}", "int[] path", "int path[] = new int[22]", "int path[][] = new int[22][]", "for (Object o : a) System.out.println(\"Yep\")", "while (os == null) System.out.println(\"Yep\")", "do f(); while (t)", "if (os == null) System.out.println(\"Yep\")", "if (t) if (!t) System.out.println(123)", "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else break", "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else continue", "for (int i = 0; i < 10; ++i) if (i < 5) System.out.println(i); else return", "throw ex", "C c = new C()", "java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)", "BufferedReader br = new BufferedReader(new FileReader(path))", "bar: g()", "baz: while (true) if (t()) printf('-'); else break baz", "java.util.function.IntFunction<int[]> ggg = int[]::new", "List<? extends Object> l", "int[] m = {1, 2}", "int[] m = {1, 2}, n = null", "int[] m = {1, 2}, n", "int[] m = {1, 2}, n = {3, 4}", }; static final String[] considered_incomplete = new String[] { "if (t)", "if (t) { } else", "if (t) if (!t)", "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE))", "for (int i = 0; i < 10; ++i)", "while (os == null)", }; static final String[] definitely_incomplete = new String[] { "int mm(", "int mm(int x", "int mm(int x)", "int mm(int x) {", "int mm(int x) {kll", "if", "if (", "if (t", "if (t) {", "if (t) { ddd", "if (t) { ddd;", "if (t) if (", "if (stack.isEmpty()) {", "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) {", "if (match.kind == BRACES && (prevCT.kind == ARROW || prevCT.kind == NEW_MIDDLE)) { new CT(UNMATCHED, current, \"Unmatched \" + unmatched);", "x +", "x *", "3 *", "int", "for (int i = 0; i < lines.length(); ++i) {", "new", "new C(", "new int[", "new int[] {1, 2,3", "new int[] {", "while (ct == null) {", "while (ct == null) { switch (current.kind) {", "while (ct == null) { switch (current.kind) { case EOF: {", "while (ct == null) { switch (current.kind) { case EOF: { } }", "enum TK {", "enum TK { EOF(TokenKind.EOF, 0),", "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM)", "enum TK { EOF(TokenKind.EOF, 0), NEW_MIDDLE(XEXPR1|XTERM); ", "enum Tt { FOO, BAR, BAZ,;", "class C", "class C extends D", "class C implements D", "class C implements D, E", "interface I extends D", "interface I extends D, E", "enum E", "enum E implements I1", "enum E implements I1, I2", "@interface Anno", "void f()", "void f() throws E", "@A(", "int n = 4,", "int n,", "int[] m = {1, 2},", "int[] m = {1, 2}, n = {3, 4},", "Map<String," }; static final String[] unknown = new String[] { "new ;" }; static final Map<Completeness, String[]> statusToCases = new HashMap<>(); static { statusToCases.put(COMPLETE, complete); statusToCases.put(COMPLETE_WITH_SEMI, complete_with_semi); statusToCases.put(CONSIDERED_INCOMPLETE, considered_incomplete); statusToCases.put(DEFINITELY_INCOMPLETE, definitely_incomplete); } private void assertStatus(String input, Completeness status, String source) { String augSrc; switch (status) { case COMPLETE_WITH_SEMI: augSrc = source + ";"; break; case DEFINITELY_INCOMPLETE: case CONSIDERED_INCOMPLETE: augSrc = null; break; case EMPTY: case COMPLETE: case UNKNOWN: augSrc = source; break; default: throw new AssertionError(); } assertAnalyze(input, status, augSrc); } private void assertStatus(String[] ins, Completeness status) { for (String input : ins) { assertStatus(input, status, input); } } public void test_complete() { assertStatus(complete, COMPLETE); } public void test_expression() { assertStatus(expression, COMPLETE); } public void test_complete_with_semi() { assertStatus(complete_with_semi, COMPLETE_WITH_SEMI); } public void test_considered_incomplete() { assertStatus(considered_incomplete, CONSIDERED_INCOMPLETE); } public void test_definitely_incomplete() { assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE); } public void test_unknown() { assertStatus(definitely_incomplete, DEFINITELY_INCOMPLETE); } public void testCompleted_complete_with_semi() { for (String in : complete_with_semi) { String input = in + ";"; assertStatus(input, COMPLETE, input); } } public void testCompleted_expression_with_semi() { for (String in : expression) { String input = in + ";"; assertStatus(input, COMPLETE, input); } } public void testCompleted_considered_incomplete() { for (String in : considered_incomplete) { String input = in + ";"; assertStatus(input, COMPLETE, input); } } private void assertSourceByStatus(String first) { for (Map.Entry<Completeness, String[]> e : statusToCases.entrySet()) { for (String in : e.getValue()) { String input = first + in; assertAnalyze(input, COMPLETE, first, in, true); } } } public void testCompleteSource_complete() { for (String input : complete) { assertSourceByStatus(input); } } public void testCompleteSource_complete_with_semi() { for (String in : complete_with_semi) { String input = in + ";"; assertSourceByStatus(input); } } public void testCompleteSource_expression() { for (String in : expression) { String input = in + ";"; assertSourceByStatus(input); } } public void testCompleteSource_considered_incomplete() { for (String in : considered_incomplete) { String input = in + ";"; assertSourceByStatus(input); } } public void testTrailingSlash() { assertStatus("\"abc\\", UNKNOWN, "\"abc\\"); } public void testOpenComment() { assertStatus("int xx; /* hello", DEFINITELY_INCOMPLETE, null); assertStatus("/** test", DEFINITELY_INCOMPLETE, null); } public void testMiscSource() { assertStatus("if (t) if ", DEFINITELY_INCOMPLETE, "if (t) if"); //Bug assertStatus("int m() {} dfd", COMPLETE, "int m() {}"); assertStatus("int p = ", DEFINITELY_INCOMPLETE, "int p ="); //Bug assertStatus("int[] m = {1, 2}, n = new int[0]; int i;", COMPLETE, "int[] m = {1, 2}, n = new int[0];"); } }