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
--- 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 '<' it could be an unbound
+ * method reference or a binary expression. To disambiguate, look for a
+ * matching '>' 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