Mon, 19 Dec 2011 12:07:07 +0000
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
1.1 --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Fri Dec 16 16:41:00 2011 -0800 1.2 +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Mon Dec 19 12:07:07 2011 +0000 1.3 @@ -787,7 +787,7 @@ 1.4 top++; 1.5 topOp = token; 1.6 nextToken(); 1.7 - odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3NoParams(); 1.8 + odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3(); 1.9 while (top > 0 && prec(topOp.kind) >= prec(token.kind)) { 1.10 odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1], 1.11 odStack[top]); 1.12 @@ -931,7 +931,7 @@ 1.13 mode = EXPR; 1.14 t = literal(names.hyphen, pos); 1.15 } else { 1.16 - t = term3NoParams(); 1.17 + t = term3(); 1.18 return F.at(pos).Unary(unoptag(tk), t); 1.19 } 1.20 } else return illegal(); 1.21 @@ -947,8 +947,8 @@ 1.22 break; 1.23 } else { 1.24 nextToken(); 1.25 - mode = EXPR | TYPE; 1.26 - t = term3NoParams(); 1.27 + mode = EXPR | TYPE | NOPARAMS; 1.28 + t = term3(); 1.29 if ((mode & TYPE) != 0 && token.kind == LT) { 1.30 // Could be a cast to a parameterized type 1.31 JCTree.Tag op = JCTree.Tag.LT; 1.32 @@ -1011,7 +1011,7 @@ 1.33 lastmode = mode; 1.34 mode = EXPR; 1.35 if ((lastmode & EXPR) == 0) { 1.36 - JCExpression t1 = term3NoParams(); 1.37 + JCExpression t1 = term3(); 1.38 return F.at(pos).TypeCast(t, t1); 1.39 } else if ((lastmode & TYPE) != 0) { 1.40 switch (token.kind) { 1.41 @@ -1024,7 +1024,7 @@ 1.42 case NEW: case IDENTIFIER: case ASSERT: case ENUM: 1.43 case BYTE: case SHORT: case CHAR: case INT: 1.44 case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID: 1.45 - JCExpression t1 = term3NoParams(); 1.46 + JCExpression t1 = term3(); 1.47 return F.at(pos).TypeCast(t, t1); 1.48 } 1.49 } 1.50 @@ -1143,49 +1143,35 @@ 1.51 // typeArgs saved for next loop iteration. 1.52 t = toP(F.at(pos).Select(t, ident())); 1.53 break; 1.54 -// case LT: 1.55 -// if ((mode & (TYPE | NOPARAMS)) == 0) { 1.56 -// //could be an unbound method reference whose qualifier 1.57 -// //is a generic type i.e. A<S>#m 1.58 -// mode = EXPR | TYPE; 1.59 -// JCTree.Tag op = JCTree.Tag.LT; 1.60 -// int pos1 = token.pos; 1.61 -// nextToken(); 1.62 -// mode |= EXPR | TYPE | TYPEARG; 1.63 -// JCExpression t1 = term3(); 1.64 -// if ((mode & TYPE) != 0 && 1.65 -// (token.kind == COMMA || token.kind == GT)) { 1.66 -// mode = TYPE; 1.67 -// ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); 1.68 -// args.append(t1); 1.69 -// while (token.kind == COMMA) { 1.70 -// nextToken(); 1.71 -// args.append(typeArgument()); 1.72 -// } 1.73 -// accept(GT); 1.74 -// t = toP(F.at(pos1).TypeApply(t, args.toList())); 1.75 -// checkGenerics(); 1.76 -// while (token.kind == DOT) { 1.77 -// nextToken(); 1.78 -// mode = TYPE; 1.79 -// t = toP(F.at(token.pos).Select(t, ident())); 1.80 -// t = typeArgumentsOpt(t); 1.81 -// } 1.82 -// if (token.kind != HASH) { 1.83 -// //method reference expected here 1.84 -// t = illegal(); 1.85 -// } 1.86 -// mode = EXPR; 1.87 -// break; 1.88 -// } else if ((mode & EXPR) != 0) { 1.89 -// //rollback - it was a binary expression 1.90 -// mode = EXPR; 1.91 -// JCExpression e = term2Rest(t1, TreeInfo.shiftPrec); 1.92 -// t = F.at(pos1).Binary(op, t, e); 1.93 -// t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); 1.94 -// } 1.95 -// } 1.96 -// break loop; 1.97 + case LT: 1.98 + if ((mode & TYPE) == 0 && isUnboundMemberRef()) { 1.99 + //this is an unbound method reference whose qualifier 1.100 + //is a generic type i.e. A<S>#m 1.101 + int pos1 = token.pos; 1.102 + accept(LT); 1.103 + ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); 1.104 + args.append(typeArgument()); 1.105 + while (token.kind == COMMA) { 1.106 + nextToken(); 1.107 + args.append(typeArgument()); 1.108 + } 1.109 + accept(GT); 1.110 + t = toP(F.at(pos1).TypeApply(t, args.toList())); 1.111 + checkGenerics(); 1.112 + while (token.kind == DOT) { 1.113 + nextToken(); 1.114 + mode = TYPE; 1.115 + t = toP(F.at(token.pos).Select(t, ident())); 1.116 + t = typeArgumentsOpt(t); 1.117 + } 1.118 + if (token.kind != HASH) { 1.119 + //method reference expected here 1.120 + t = illegal(); 1.121 + } 1.122 + mode = EXPR; 1.123 + return term3Rest(t, typeArgs); 1.124 + } 1.125 + break loop; 1.126 default: 1.127 break loop; 1.128 } 1.129 @@ -1225,15 +1211,6 @@ 1.130 return term3Rest(t, typeArgs); 1.131 } 1.132 1.133 - JCExpression term3NoParams() { 1.134 - try { 1.135 - mode |= NOPARAMS; 1.136 - return term3(); 1.137 - } finally { 1.138 - mode &= ~NOPARAMS; 1.139 - } 1.140 - } 1.141 - 1.142 JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) { 1.143 if (typeArgs != null) illegal(); 1.144 while (true) { 1.145 @@ -1297,6 +1274,41 @@ 1.146 return toP(t); 1.147 } 1.148 1.149 + /** 1.150 + * If we see an identifier followed by a '<' it could be an unbound 1.151 + * method reference or a binary expression. To disambiguate, look for a 1.152 + * matching '>' and see if the subsequent terminal is either '.' or '#'. 1.153 + */ 1.154 + @SuppressWarnings("fallthrough") 1.155 + boolean isUnboundMemberRef() { 1.156 + int pos = 0, depth = 0; 1.157 + for (Token t = S.token(pos) ; ; t = S.token(++pos)) { 1.158 + switch (t.kind) { 1.159 + case IDENTIFIER: case QUES: case EXTENDS: case SUPER: 1.160 + case DOT: case RBRACKET: case LBRACKET: case COMMA: 1.161 + case BYTE: case SHORT: case INT: case LONG: case FLOAT: 1.162 + case DOUBLE: case BOOLEAN: case CHAR: 1.163 + break; 1.164 + case LT: 1.165 + depth++; break; 1.166 + case GTGTGT: 1.167 + depth--; 1.168 + case GTGT: 1.169 + depth--; 1.170 + case GT: 1.171 + depth--; 1.172 + if (depth == 0) { 1.173 + return 1.174 + S.token(pos + 1).kind == TokenKind.DOT || 1.175 + S.token(pos + 1).kind == TokenKind.HASH; 1.176 + } 1.177 + break; 1.178 + default: 1.179 + return false; 1.180 + } 1.181 + } 1.182 + } 1.183 + 1.184 JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) { 1.185 ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); 1.186 params.append(firstParam);
2.1 --- a/test/tools/javac/lambda/MethodReferenceParserTest.java Fri Dec 16 16:41:00 2011 -0800 2.2 +++ b/test/tools/javac/lambda/MethodReferenceParserTest.java Mon Dec 19 12:07:07 2011 +0000 2.3 @@ -24,7 +24,6 @@ 2.4 /* 2.5 * @test 2.6 * @bug 7115052 2.7 - * @ignore 7120266 2.8 * @summary Add parser support for method references 2.9 */ 2.10 2.11 @@ -45,6 +44,7 @@ 2.12 enum ReferenceKind { 2.13 METHOD_REF("#Q##Gm"), 2.14 CONSTRUCTOR_REF("#Q##Gnew"), 2.15 + FALSE_REF("min < max"), 2.16 ERR_SUPER("#Q##Gsuper"), 2.17 ERR_METH0("#Q##Gm()"), 2.18 ERR_METH1("#Q##Gm(X)"), 2.19 @@ -76,6 +76,21 @@ 2.20 } 2.21 } 2.22 2.23 + enum ContextKind { 2.24 + ASSIGN("SAM s = #E;"), 2.25 + METHOD("m(#E, i);"); 2.26 + 2.27 + String contextTemplate; 2.28 + 2.29 + ContextKind(String contextTemplate) { 2.30 + this.contextTemplate = contextTemplate; 2.31 + } 2.32 + 2.33 + String contextString(ExprKind ek, ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk) { 2.34 + return contextTemplate.replaceAll("#E", ek.expressionString(rk, qk, gk, sk)); 2.35 + } 2.36 + } 2.37 + 2.38 enum GenericKind { 2.39 NONE(""), 2.40 ONE("<X>"), 2.41 @@ -97,7 +112,10 @@ 2.42 UBOUND_SIMPLE("A"), 2.43 UNBOUND_GENERIC1("A<X>"), 2.44 UNBOUND_GENERIC2("A<X, Y>"), 2.45 - UNBOUND_GENERIC3("A<? extends X, ? super Y>"); 2.46 + UNBOUND_GENERIC3("A<? extends X, ? super Y>"), 2.47 + UNBOUND_GENERIC4("A<int[], short[][]>"), 2.48 + NESTED_GENERIC1("A<A<X,Y>, A<X,Y>>"), 2.49 + NESTED_GENERIC2("A<A<A<X,Y>,A<X,Y>>, A<A<X,Y>,A<X,Y>>>"); 2.50 2.51 String qualifier; 2.52 2.53 @@ -153,7 +171,9 @@ 2.54 for (GenericKind gk : GenericKind.values()) { 2.55 for (SubExprKind sk : SubExprKind.values()) { 2.56 for (ExprKind ek : ExprKind.values()) { 2.57 - new MethodReferenceParserTest(rk, qk, gk, sk, ek).run(comp, fm); 2.58 + for (ContextKind ck : ContextKind.values()) { 2.59 + new MethodReferenceParserTest(rk, qk, gk, sk, ek, ck).run(comp, fm); 2.60 + } 2.61 } 2.62 } 2.63 } 2.64 @@ -167,15 +187,17 @@ 2.65 GenericKind gk; 2.66 SubExprKind sk; 2.67 ExprKind ek; 2.68 + ContextKind ck; 2.69 JavaSource source; 2.70 DiagnosticChecker diagChecker; 2.71 2.72 - MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek) { 2.73 + MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek, ContextKind ck) { 2.74 this.rk = rk; 2.75 this.qk = qk; 2.76 this.gk = gk; 2.77 this.sk = sk; 2.78 this.ek = ek; 2.79 + this.ck = ck; 2.80 this.source = new JavaSource(); 2.81 this.diagChecker = new DiagnosticChecker(); 2.82 } 2.83 @@ -183,14 +205,16 @@ 2.84 class JavaSource extends SimpleJavaFileObject { 2.85 2.86 String template = "class Test {\n" + 2.87 - " SAM s = #E;\n" + 2.88 + " void test() {\n" + 2.89 + " #C\n" + 2.90 + " }" + 2.91 "}"; 2.92 2.93 String source; 2.94 2.95 public JavaSource() { 2.96 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 2.97 - source = template.replaceAll("#E", ek.expressionString(rk, qk, gk, sk)); 2.98 + source = template.replaceAll("#C", ck.contextString(ek, rk, qk, gk, sk)); 2.99 } 2.100 2.101 @Override