8165211: JShell: Fix completion analysis problems
authorrfield
Thu, 01 Sep 2016 13:21:52 -0700
changeset 40771 bf0e92ede33f
parent 40770 dfccc36b61b6
child 40772 de87954b8f20
8165211: JShell: Fix completion analysis problems Reviewed-by: jlahoda
langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java
langtools/test/jdk/jshell/CompletenessTest.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<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 @@
                         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<Matched> matchedFactory;
+        private final Supplier<ParseTask> parseFactory;
+        private Matched in;
+        private CT token;
+        private Completeness checkResult;
 
+        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 @@
             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<? extends Tree> 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);
--- 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<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 @@
         "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<int[]> 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[] {