8165211: JShell: Fix completion analysis problems

Reviewed-by: jlahoda
This commit is contained in:
Robert Field 2016-09-01 13:21:52 -07:00
parent 8302f64ee6
commit 4017bf5f7a
2 changed files with 65 additions and 86 deletions

View File

@ -46,6 +46,8 @@ import com.sun.source.tree.Tree;
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
// 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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
// 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 @@ class CompletenessAnalyzer {
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<TokenKind,TK> tokenKindToTKMap = new EnumMap<>(TokenKind.class);
final TokenKind tokenKind;
final int belongs;
Function<TK,TK> 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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
}
}
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
*/
private static class Parser {
final Matched in;
CT token;
Completeness checkResult;
private final Supplier<Matched> matchedFactory;
private final Supplier<ParseTask> parseFactory;
private Matched in;
private CT token;
private Completeness checkResult;
final JShell proc;
final String scannedInput;
Parser(Supplier<Matched> matchedFactory, Supplier<ParseTask> 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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
public Completeness disambiguateDeclarationVsExpression() {
// String folding messes up position information.
ParseTask pt = proc.taskFactory.new ParseTask(scannedInput);
ParseTask pt = parseFactory.get();
List<? extends Tree> units = pt.units();
if (units.isEmpty()) {
return error();
@ -679,7 +696,7 @@ class CompletenessAnalyzer {
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 @@ class CompletenessAnalyzer {
}
}
// 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);

View File

@ -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 @@ public class CompletenessTest extends KullaTesting {
"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[] {
@ -77,6 +78,8 @@ public class CompletenessTest extends KullaTesting {
"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 @@ public class CompletenessTest extends KullaTesting {
"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",
};
static final String[] considered_incomplete = new String[] {
@ -141,6 +145,8 @@ public class CompletenessTest extends KullaTesting {
"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 @@ public class CompletenessTest extends KullaTesting {
"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[] {