# HG changeset patch # User rfield # Date 1472761312 25200 # Node ID bf0e92ede33fdebbe99d64df462bd3afe9e314f7 # Parent dfccc36b61b6d3b7e372e91e20130c782dcfd2e5 8165211: JShell: Fix completion analysis problems Reviewed-by: jlahoda diff -r dfccc36b61b6 -r bf0e92ede33f langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java Thu Sep 01 21:25:33 2016 +0200 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java Thu Sep 01 13:21:52 2016 -0700 @@ -46,6 +46,8 @@ import static jdk.jshell.CompletenessAnalyzer.TK.*; import jdk.jshell.TaskFactory.ParseTask; import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; /** * Low level scanner to determine completeness of input. @@ -81,13 +83,13 @@ CaInfo scan(String s) { try { - Scanner scanner = scannerFactory.newScanner(s, false); - Matched in = new Matched(scanner); - Parser parser = new Parser(in, proc, s); + Parser parser = new Parser( + () -> new Matched(scannerFactory.newScanner(s, false)), + () -> proc.taskFactory.new ParseTask(s)); Completeness stat = parser.parseUnit(); int endPos = stat == Completeness.UNKNOWN ? s.length() - : in.prevCT.endPos; + : parser.endPos(); return new CaInfo(stat, endPos); } catch (SyntaxException ex) { return new CaInfo(error(), s.length()); @@ -188,7 +190,7 @@ ERROR(TokenKind.ERROR, XERRO), // IDENTIFIER(TokenKind.IDENTIFIER, XEXPR1|XDECL1|XTERM), // UNDERSCORE(TokenKind.UNDERSCORE, XERRO), // _ - CLASS(TokenKind.CLASS, XEXPR|XDECL1|XTERM), // class decl and .class + CLASS(TokenKind.CLASS, XEXPR|XDECL1), // class decl (MAPPED: DOTCLASS) MONKEYS_AT(TokenKind.MONKEYS_AT, XEXPR|XDECL1), // @ IMPORT(TokenKind.IMPORT, XDECL1|XSTART), // import -- consider declaration SEMI(TokenKind.SEMI, XSTMT1|XTERM|XSTART), // ; @@ -206,15 +208,15 @@ THROWS(TokenKind.THROWS, XDECL), // throws // Primarive type names - BOOLEAN(TokenKind.BOOLEAN, XEXPR|XDECL1), // boolean - BYTE(TokenKind.BYTE, XEXPR|XDECL1), // byte - CHAR(TokenKind.CHAR, XEXPR|XDECL1), // char - DOUBLE(TokenKind.DOUBLE, XEXPR|XDECL1), // double - FLOAT(TokenKind.FLOAT, XEXPR|XDECL1), // float - INT(TokenKind.INT, XEXPR|XDECL1), // int - LONG(TokenKind.LONG, XEXPR|XDECL1), // long - SHORT(TokenKind.SHORT, XEXPR|XDECL1), // short - VOID(TokenKind.VOID, XEXPR|XDECL1), // void + BOOLEAN(TokenKind.BOOLEAN, XEXPR1|XDECL1), // boolean + BYTE(TokenKind.BYTE, XEXPR1|XDECL1), // byte + CHAR(TokenKind.CHAR, XEXPR1|XDECL1), // char + DOUBLE(TokenKind.DOUBLE, XEXPR1|XDECL1), // double + FLOAT(TokenKind.FLOAT, XEXPR1|XDECL1), // float + INT(TokenKind.INT, XEXPR1|XDECL1), // int + LONG(TokenKind.LONG, XEXPR1|XDECL1), // long + SHORT(TokenKind.SHORT, XEXPR1|XDECL1), // short + VOID(TokenKind.VOID, XEXPR1|XDECL1), // void // Modifiers keywords ABSTRACT(TokenKind.ABSTRACT, XDECL1), // abstract @@ -230,7 +232,7 @@ // Declarations and type parameters (thus expressions) EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL), // extends - COMMA(TokenKind.COMMA, XEXPR|XDECL), // , + COMMA(TokenKind.COMMA, XEXPR|XDECL|XTERM), // , AMP(TokenKind.AMP, XEXPR|XDECL), // & GT(TokenKind.GT, XEXPR|XDECL), // > LT(TokenKind.LT, XEXPR|XDECL1), // < @@ -239,7 +241,7 @@ GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL), // >>> QUES(TokenKind.QUES, XEXPR|XDECL), // ? DOT(TokenKind.DOT, XEXPR|XDECL), // . - STAR(TokenKind.STAR, XEXPR|XDECL|XTERM), // * -- import foo.* //TODO handle these case separately, XTERM + STAR(TokenKind.STAR, XEXPR), // * (MAPPED: DOTSTAR) // Statement keywords ASSERT(TokenKind.ASSERT, XSTMT1|XSTART), // assert @@ -280,7 +282,7 @@ // Expressions cannot terminate INSTANCEOF(TokenKind.INSTANCEOF, XEXPR), // instanceof - NEW(TokenKind.NEW, XEXPR1), // new + NEW(TokenKind.NEW, XEXPR1), // new (MAPPED: COLCOLNEW) SUPER(TokenKind.SUPER, XEXPR1|XDECL), // super -- shouldn't see as rec. But in type parameters ARROW(TokenKind.ARROW, XEXPR), // -> COLCOL(TokenKind.COLCOL, XEXPR), // :: @@ -323,24 +325,29 @@ UNMATCHED(XERRO), PARENS(XEXPR1|XDECL|XSTMT|XTERM), BRACKETS(XEXPR|XDECL|XTERM), - BRACES(XSTMT1|XEXPR|XTERM); + BRACES(XSTMT1|XEXPR|XTERM), + DOTSTAR(XDECL|XTERM), // import foo.* + COLCOLNEW(XEXPR|XTERM), // :: new + DOTCLASS(XEXPR|XTERM), // class decl and .class + ; static final EnumMap tokenKindToTKMap = new EnumMap<>(TokenKind.class); final TokenKind tokenKind; final int belongs; + Function mapping; TK(int b) { - this.tokenKind = null; - this.belongs = b; + this(null, b); } TK(TokenKind tokenKind, int b) { this.tokenKind = tokenKind; this.belongs = b; + this.mapping = null; } - private static TK tokenKindToTK(TokenKind kind) { + private static TK tokenKindToTK(TK prev, TokenKind kind) { TK tk = tokenKindToTKMap.get(kind); if (tk == null) { System.err.printf("No corresponding %s for %s: %s\n", @@ -349,7 +356,9 @@ kind); throw new InternalError("No corresponding TK for TokenKind: " + kind); } - return tk; + return tk.mapping != null + ? tk.mapping.apply(prev) + : tk; } boolean isOkToTerminate() { @@ -383,8 +392,12 @@ } } for (TokenKind kind : TokenKind.values()) { - tokenKindToTK(kind); // assure they can be retrieved without error + tokenKindToTK(null, kind); // assure they can be retrieved without error } + // Mappings of disambiguated contexts + STAR.mapping = prev -> prev == DOT ? DOTSTAR : STAR; + NEW.mapping = prev -> prev == COLCOL ? COLCOLNEW : NEW; + CLASS.mapping = prev -> prev == DOT ? DOTCLASS : CLASS; } } @@ -520,7 +533,7 @@ ct = match(BRACKETS, TokenKind.LBRACKET); break; default: - ct = new CT(TK.tokenKindToTK(current.kind), advance()); + ct = new CT(TK.tokenKindToTK(prevTK, current.kind), advance()); break; } if (ct.kind.isStart() && !prevTK.isOkToTerminate()) { @@ -539,21 +552,21 @@ */ private static class Parser { - final Matched in; - CT token; - Completeness checkResult; - - final JShell proc; - final String scannedInput; - + private final Supplier matchedFactory; + private final Supplier parseFactory; + private Matched in; + private CT token; + private Completeness checkResult; + Parser(Supplier matchedFactory, Supplier parseFactory) { + this.matchedFactory = matchedFactory; + this.parseFactory = parseFactory; + resetInput(); + } - Parser(Matched in, JShell proc, String scannedInput) { - this.in = in; + final void resetInput() { + this.in = matchedFactory.get(); nextToken(); - - this.proc = proc; - this.scannedInput = scannedInput; } final void nextToken() { @@ -598,6 +611,10 @@ return flags != Completeness.COMPLETE; } + public int endPos() { + return in.prevCT.endPos; + } + public Completeness parseUnit() { //System.err.printf("%s: belongs %o XANY1 %o\n", token.kind, token.kind.belongs, token.kind.belongs & XANY1); switch (token.kind.belongs & XANY1) { @@ -651,11 +668,11 @@ case IDENTIFIER: case BRACKETS: return Completeness.COMPLETE_WITH_SEMI; - case STAR: + case DOTSTAR: if (isImport) { return Completeness.COMPLETE_WITH_SEMI; } else { - return Completeness.DEFINITELY_INCOMPLETE; + return Completeness.UNKNOWN; } default: return Completeness.DEFINITELY_INCOMPLETE; @@ -667,7 +684,7 @@ public Completeness disambiguateDeclarationVsExpression() { // String folding messes up position information. - ParseTask pt = proc.taskFactory.new ParseTask(scannedInput); + ParseTask pt = parseFactory.get(); List units = pt.units(); if (units.isEmpty()) { return error(); @@ -679,7 +696,7 @@ case LABELED_STATEMENT: if (shouldAbort(IDENTIFIER)) return checkResult; if (shouldAbort(COLON)) return checkResult; - return parseStatement(); + return parseStatement(); case VARIABLE: case IMPORT: case CLASS: @@ -693,51 +710,6 @@ } } -// public Status parseExpressionOrDeclaration() { -// if (token.kind == IDENTIFIER) { -// nextToken(); -// switch (token.kind) { -// case IDENTIFIER: -// return parseDeclaration(); -// } -// } -// while (token.kind.isExpressionOrDeclaration()) { -// if (!token.kind.isExpression()) { -// return parseDeclaration(); -// } -// if (!token.kind.isDeclaration()) { -// // Expression not declaration -// if (token.kind == EQ) { -// // Check for array initializer -// nextToken(); -// if (token.kind == BRACES) { -// nextToken(); -// return lastly(SEMI); -// } -// } -// return parseExpressionStatement(); -// } -// nextToken(); -// } -// switch (token.kind) { -// case BRACES: -// case SEMI: -// nextToken(); -// return Status.COMPLETE; -// case UNMATCHED: -// nextToken(); -// return Status.DEFINITELY_INCOMPLETE; -// case EOF: -// if (in.prevCT.kind.isOkToTerminate()) { -// return Status.COMPLETE_WITH_SEMI; -// } else { -// return Status.DEFINITELY_INCOMPLETE; -// } -// default: -// return error(); -// } -// } - public Completeness parseExpressionStatement() { if (shouldAbort(parseExpression())) return checkResult; return lastly(SEMI); diff -r dfccc36b61b6 -r bf0e92ede33f langtools/test/jdk/jshell/CompletenessTest.java --- a/langtools/test/jdk/jshell/CompletenessTest.java Thu Sep 01 21:25:33 2016 +0200 +++ b/langtools/test/jdk/jshell/CompletenessTest.java Thu Sep 01 13:21:52 2016 -0700 @@ -23,7 +23,7 @@ /* * @test - * @bug 8149524 8131024 + * @bug 8149524 8131024 8165211 8080071 8130454 * @summary Test SourceCodeAnalysis * @build KullaTesting TestingInputStream * @run testng CompletenessTest @@ -63,6 +63,7 @@ "foo: while (true) { printf(\"Innn\"); break foo; }", "class Case, E2 extends Enum, E3 extends Enum> {}", ";", + "enum Tt { FOO, BAR, BAZ,; }" }; static final String[] expression = new String[] { @@ -77,6 +78,8 @@ "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[] { @@ -113,6 +116,7 @@ "BufferedReader br = new BufferedReader(new FileReader(path))", "bar: g()", "baz: while (true) if (t()) printf('-'); else break baz", + "java.util.function.IntFunction ggg = int[]::new", }; static final String[] considered_incomplete = new String[] { @@ -141,6 +145,8 @@ "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", @@ -156,6 +162,7 @@ "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,;" }; static final String[] unknown = new String[] {