diff --git a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java index 81d6680b11f737d9df101d11c1f4be777647e610..9f5d71de189cc2578e1f90bc1e7e91511356d0d1 100644 --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -787,7 +787,7 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { 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 @@ public class JavacParser implements Parser { // 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#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 args = new ListBuffer(); -// 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#m + int pos1 = token.pos; + accept(LT); + ListBuffer args = new ListBuffer(); + 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 @@ public class JavacParser implements Parser { return term3Rest(t, typeArgs); } - JCExpression term3NoParams() { - try { - mode |= NOPARAMS; - return term3(); - } finally { - mode &= ~NOPARAMS; - } - } - JCExpression term3Rest(JCExpression t, List typeArgs) { if (typeArgs != null) illegal(); while (true) { @@ -1297,6 +1274,41 @@ public class JavacParser implements Parser { 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 params = new ListBuffer(); params.append(firstParam); diff --git a/test/tools/javac/lambda/MethodReferenceParserTest.java b/test/tools/javac/lambda/MethodReferenceParserTest.java index 00f7f33923db389842d86325cfd046f155e8a721..a5af9622f9f34d8672e95aa6f4d1ac285ce849c7 100644 --- a/test/tools/javac/lambda/MethodReferenceParserTest.java +++ b/test/tools/javac/lambda/MethodReferenceParserTest.java @@ -24,7 +24,6 @@ /* * @test * @bug 7115052 - * @ignore 7120266 * @summary Add parser support for method references */ @@ -45,6 +44,7 @@ public class MethodReferenceParserTest { 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 @@ public class MethodReferenceParserTest { } } + 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(""), @@ -97,7 +112,10 @@ public class MethodReferenceParserTest { UBOUND_SIMPLE("A"), UNBOUND_GENERIC1("A"), UNBOUND_GENERIC2("A"), - UNBOUND_GENERIC3("A"); + UNBOUND_GENERIC3("A"), + UNBOUND_GENERIC4("A"), + NESTED_GENERIC1("A, A>"), + NESTED_GENERIC2("A,A>, A,A>>"); String qualifier; @@ -153,7 +171,9 @@ public class MethodReferenceParserTest { 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 @@ public class MethodReferenceParserTest { 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 @@ public class MethodReferenceParserTest { 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