7120463: Fix method reference parser support in order to avoid ambiguities
authormcimadamore
Mon, 19 Dec 2011 12:07:07 +0000
changeset 11322 6ee85f80967a
parent 11321 ce0082fea571
child 11323 8e546b2b9dd0
child 11381 890ea587d133
7120463: Fix method reference parser support in order to avoid ambiguities Summary: Add lookahead routine to disambiguate between method reference in method context and binary expression Reviewed-by: jjg, dlsmith
langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java
langtools/test/tools/javac/lambda/MethodReferenceParserTest.java
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Dec 16 16:41:00 2011 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Mon Dec 19 12:07:07 2011 +0000
@@ -787,7 +787,7 @@
             top++;
             topOp = token;
             nextToken();
-            odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3NoParams();
+            odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3();
             while (top > 0 && prec(topOp.kind) >= prec(token.kind)) {
                 odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1],
                                         odStack[top]);
@@ -931,7 +931,7 @@
                     mode = EXPR;
                     t = literal(names.hyphen, pos);
                 } else {
-                    t = term3NoParams();
+                    t = term3();
                     return F.at(pos).Unary(unoptag(tk), t);
                 }
             } else return illegal();
@@ -947,8 +947,8 @@
                     break;
                 } else {
                     nextToken();
-                    mode = EXPR | TYPE;
-                    t = term3NoParams();
+                    mode = EXPR | TYPE | NOPARAMS;
+                    t = term3();
                     if ((mode & TYPE) != 0 && token.kind == LT) {
                         // Could be a cast to a parameterized type
                         JCTree.Tag op = JCTree.Tag.LT;
@@ -1011,7 +1011,7 @@
                 lastmode = mode;
                 mode = EXPR;
                 if ((lastmode & EXPR) == 0) {
-                    JCExpression t1 = term3NoParams();
+                    JCExpression t1 = term3();
                     return F.at(pos).TypeCast(t, t1);
                 } else if ((lastmode & TYPE) != 0) {
                     switch (token.kind) {
@@ -1024,7 +1024,7 @@
                         case NEW: case IDENTIFIER: case ASSERT: case ENUM:
                     case BYTE: case SHORT: case CHAR: case INT:
                     case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
-                        JCExpression t1 = term3NoParams();
+                        JCExpression t1 = term3();
                         return F.at(pos).TypeCast(t, t1);
                     }
                 }
@@ -1143,49 +1143,35 @@
                         // typeArgs saved for next loop iteration.
                         t = toP(F.at(pos).Select(t, ident()));
                         break;
-//                    case LT:
-//                        if ((mode & (TYPE | NOPARAMS)) == 0) {
-//                            //could be an unbound method reference whose qualifier
-//                            //is a generic type i.e. A<S>#m
-//                            mode = EXPR | TYPE;
-//                            JCTree.Tag op = JCTree.Tag.LT;
-//                            int pos1 = token.pos;
-//                            nextToken();
-//                            mode |= EXPR | TYPE | TYPEARG;
-//                            JCExpression t1 = term3();
-//                            if ((mode & TYPE) != 0 &&
-//                                (token.kind == COMMA || token.kind == GT)) {
-//                                mode = TYPE;
-//                                ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
-//                                args.append(t1);
-//                                while (token.kind == COMMA) {
-//                                    nextToken();
-//                                    args.append(typeArgument());
-//                                }
-//                                accept(GT);
-//                                t = toP(F.at(pos1).TypeApply(t, args.toList()));
-//                                checkGenerics();
-//                                while (token.kind == DOT) {
-//                                    nextToken();
-//                                    mode = TYPE;
-//                                    t = toP(F.at(token.pos).Select(t, ident()));
-//                                    t = typeArgumentsOpt(t);
-//                                }
-//                                if (token.kind != HASH) {
-//                                    //method reference expected here
-//                                    t = illegal();
-//                                }
-//                                mode = EXPR;
-//                                break;
-//                            } else if ((mode & EXPR) != 0) {
-//                                //rollback - it was a binary expression
-//                                mode = EXPR;
-//                                JCExpression e = term2Rest(t1, TreeInfo.shiftPrec);
-//                                t = F.at(pos1).Binary(op, t, e);
-//                                t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
-//                            }
-//                        }
-//                        break loop;
+                    case LT:
+                        if ((mode & TYPE) == 0 && isUnboundMemberRef()) {
+                            //this is an unbound method reference whose qualifier
+                            //is a generic type i.e. A<S>#m
+                            int pos1 = token.pos;
+                            accept(LT);
+                            ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
+                            args.append(typeArgument());
+                            while (token.kind == COMMA) {
+                                nextToken();
+                                args.append(typeArgument());
+                            }
+                            accept(GT);
+                            t = toP(F.at(pos1).TypeApply(t, args.toList()));
+                            checkGenerics();
+                            while (token.kind == DOT) {
+                                nextToken();
+                                mode = TYPE;
+                                t = toP(F.at(token.pos).Select(t, ident()));
+                                t = typeArgumentsOpt(t);
+                            }
+                            if (token.kind != HASH) {
+                                //method reference expected here
+                                t = illegal();
+                            }
+                            mode = EXPR;
+                            return term3Rest(t, typeArgs);
+                        }
+                        break loop;
                     default:
                         break loop;
                     }
@@ -1225,15 +1211,6 @@
         return term3Rest(t, typeArgs);
     }
 
-    JCExpression term3NoParams() {
-        try {
-            mode |= NOPARAMS;
-            return term3();
-        } finally {
-            mode &= ~NOPARAMS;
-        }
-    }
-
     JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) {
         if (typeArgs != null) illegal();
         while (true) {
@@ -1297,6 +1274,41 @@
         return toP(t);
     }
 
+    /**
+     * If we see an identifier followed by a '&lt;' it could be an unbound
+     * method reference or a binary expression. To disambiguate, look for a
+     * matching '&gt;' and see if the subsequent terminal is either '.' or '#'.
+     */
+    @SuppressWarnings("fallthrough")
+    boolean isUnboundMemberRef() {
+        int pos = 0, depth = 0;
+        for (Token t = S.token(pos) ; ; t = S.token(++pos)) {
+            switch (t.kind) {
+                case IDENTIFIER: case QUES: case EXTENDS: case SUPER:
+                case DOT: case RBRACKET: case LBRACKET: case COMMA:
+                case BYTE: case SHORT: case INT: case LONG: case FLOAT:
+                case DOUBLE: case BOOLEAN: case CHAR:
+                    break;
+                case LT:
+                    depth++; break;
+                case GTGTGT:
+                    depth--;
+                case GTGT:
+                    depth--;
+                case GT:
+                    depth--;
+                    if (depth == 0) {
+                        return
+                            S.token(pos + 1).kind == TokenKind.DOT ||
+                            S.token(pos + 1).kind == TokenKind.HASH;
+                    }
+                    break;
+                default:
+                    return false;
+            }
+        }
+    }
+
     JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) {
         ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
         params.append(firstParam);
--- a/langtools/test/tools/javac/lambda/MethodReferenceParserTest.java	Fri Dec 16 16:41:00 2011 -0800
+++ b/langtools/test/tools/javac/lambda/MethodReferenceParserTest.java	Mon Dec 19 12:07:07 2011 +0000
@@ -24,7 +24,6 @@
 /*
  * @test
  * @bug 7115052
- * @ignore 7120266
  * @summary Add parser support for method references
  */
 
@@ -45,6 +44,7 @@
     enum ReferenceKind {
         METHOD_REF("#Q##Gm"),
         CONSTRUCTOR_REF("#Q##Gnew"),
+        FALSE_REF("min < max"),
         ERR_SUPER("#Q##Gsuper"),
         ERR_METH0("#Q##Gm()"),
         ERR_METH1("#Q##Gm(X)"),
@@ -76,6 +76,21 @@
         }
     }
 
+    enum ContextKind {
+        ASSIGN("SAM s = #E;"),
+        METHOD("m(#E, i);");
+
+        String contextTemplate;
+
+        ContextKind(String contextTemplate) {
+            this.contextTemplate = contextTemplate;
+        }
+
+        String contextString(ExprKind ek, ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk) {
+            return contextTemplate.replaceAll("#E", ek.expressionString(rk, qk, gk, sk));
+        }
+    }
+
     enum GenericKind {
         NONE(""),
         ONE("<X>"),
@@ -97,7 +112,10 @@
         UBOUND_SIMPLE("A"),
         UNBOUND_GENERIC1("A<X>"),
         UNBOUND_GENERIC2("A<X, Y>"),
-        UNBOUND_GENERIC3("A<? extends X, ? super Y>");
+        UNBOUND_GENERIC3("A<? extends X, ? super Y>"),
+        UNBOUND_GENERIC4("A<int[], short[][]>"),
+        NESTED_GENERIC1("A<A<X,Y>, A<X,Y>>"),
+        NESTED_GENERIC2("A<A<A<X,Y>,A<X,Y>>, A<A<X,Y>,A<X,Y>>>");
 
         String qualifier;
 
@@ -153,7 +171,9 @@
                 for (GenericKind gk : GenericKind.values()) {
                     for (SubExprKind sk : SubExprKind.values()) {
                         for (ExprKind ek : ExprKind.values()) {
-                            new MethodReferenceParserTest(rk, qk, gk, sk, ek).run(comp, fm);
+                            for (ContextKind ck : ContextKind.values()) {
+                                new MethodReferenceParserTest(rk, qk, gk, sk, ek, ck).run(comp, fm);
+                            }
                         }
                     }
                 }
@@ -167,15 +187,17 @@
     GenericKind gk;
     SubExprKind sk;
     ExprKind ek;
+    ContextKind ck;
     JavaSource source;
     DiagnosticChecker diagChecker;
 
-    MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek) {
+    MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek, ContextKind ck) {
         this.rk = rk;
         this.qk = qk;
         this.gk = gk;
         this.sk = sk;
         this.ek = ek;
+        this.ck = ck;
         this.source = new JavaSource();
         this.diagChecker = new DiagnosticChecker();
     }
@@ -183,14 +205,16 @@
     class JavaSource extends SimpleJavaFileObject {
 
         String template = "class Test {\n" +
-                          "   SAM s = #E;\n" +
+                          "   void test() {\n" +
+                          "      #C\n" +
+                          "   }" +
                           "}";
 
         String source;
 
         public JavaSource() {
             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
-            source = template.replaceAll("#E", ek.expressionString(rk, qk, gk, sk));
+            source = template.replaceAll("#C", ck.contextString(ek, rk, qk, gk, sk));
         }
 
         @Override