changeset 1710:2e8ccb7ba243

Conformance fix: adjust parsing of '_' as an identifier *) '_' cannot be used as a lambda parameter name *) use of '_' as an identifier will cause compile-time warnings
author mcimadamore
date Tue, 08 Jan 2013 13:31:41 +0100
parents d78663601eb9
children 839d5feb156c
files src/share/classes/com/sun/tools/javac/parser/JavacParser.java src/share/classes/com/sun/tools/javac/parser/Tokens.java src/share/classes/com/sun/tools/javac/resources/compiler.properties test/tools/javac/diags/examples.not-yet.txt test/tools/javac/lambda/LambdaParserTest.java test/tools/javac/lambda/WarnUnderscoreAsIdent.java test/tools/javac/lambda/WarnUnderscoreAsIdent.out
diffstat 7 files changed, 165 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Mon Jan 07 18:45:59 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Jan 08 13:31:41 2013 +0100
@@ -568,6 +568,11 @@
                 nextToken();
                 return name;
             }
+        } else if (token.kind == UNDERSCORE) {
+            warning(token.pos, "underscore.as.identifier");
+            Name name = token.name();
+            nextToken();
+            return name;
         } else {
             accept(IDENTIFIER);
             return names.error;
@@ -1078,7 +1083,7 @@
                 typeArgs = null;
             } else return illegal();
             break;
-        case IDENTIFIER: case ASSERT: case ENUM:
+        case UNDERSCORE: case IDENTIFIER: case ASSERT: case ENUM:
             if (typeArgs != null) return illegal();
             if ((mode & EXPR) != 0 && peekToken(ARROW)) {
                 t = lambdaExpressionOrStatement(false, false, pos);
@@ -1372,7 +1377,7 @@
                         case INTLITERAL: case LONGLITERAL: case FLOATLITERAL:
                         case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL:
                         case TRUE: case FALSE: case NULL:
-                            case NEW: case IDENTIFIER: case ASSERT: case ENUM:
+                        case NEW: case IDENTIFIER: case ASSERT: case ENUM: case UNDERSCORE:
                         case BYTE: case SHORT: case CHAR: case INT:
                         case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
                             return ParensResult.CAST;
@@ -1380,22 +1385,30 @@
                             return ParensResult.PARENS;
                     }
                 case IDENTIFIER:
-                    if (peekToken(lookahead, IDENTIFIER)) {
-                        // Identifier, Identifier -> explicit lambda
+                    if (peekToken(lookahead, IDENTIFIER) ||
+                            peekToken(lookahead, UNDERSCORE)) {
+                        // Identifier, Identifier/'_' -> explicit lambda
                         return ParensResult.EXPLICIT_LAMBDA;
                     } else if (peekToken(lookahead, RPAREN, ARROW)) {
                         // Identifier, ')' '->' -> implicit lambda
                         return ParensResult.IMPLICIT_LAMBDA;
                     }
                     break;
+                case UNDERSCORE:
+                    if (peekToken(lookahead, RPAREN, ARROW)) {
+                        // '_', ')' '->' -> implicit lambda
+                        return ParensResult.IMPLICIT_LAMBDA;
+                    }
+                    break;
                 case FINAL:
                 case ELLIPSIS:
                 case MONKEYS_AT:
                     //those can only appear in explicit lambdas
                     return ParensResult.EXPLICIT_LAMBDA;
                 case LBRACKET:
-                    if (peekToken(lookahead, RBRACKET, IDENTIFIER)) {
-                        // '[', ']', Identifier -> explicit lambda
+                    if (peekToken(lookahead, RBRACKET, IDENTIFIER) ||
+                            peekToken(lookahead, RBRACKET, UNDERSCORE)) {
+                        // '[', ']', Identifier/'_' -> explicit lambda
                         return ParensResult.EXPLICIT_LAMBDA;
                     } else if (peekToken(lookahead, RBRACKET, RPAREN) ||
                             peekToken(lookahead, RBRACKET, AMP)) {
@@ -1425,10 +1438,12 @@
                             // '>', '&' -> cast
                             return ParensResult.CAST;
                         } else if (peekToken(lookahead, IDENTIFIER, COMMA) ||
+                                peekToken(lookahead, UNDERSCORE, COMMA) ||
                                 peekToken(lookahead, IDENTIFIER, RPAREN, ARROW) ||
+                                peekToken(lookahead, UNDERSCORE, RPAREN, ARROW) ||
                                 peekToken(lookahead, ELLIPSIS)) {
-                            // '>', Identifier, ',' -> explicit lambda
-                            // '>', Identifier, ')', '->' -> explicit lambda
+                            // '>', Identifier/'_', ',' -> explicit lambda
+                            // '>', Identifier/'_', ')', '->' -> explicit lambda
                             // '>', '...' -> explicit lambda
                             return ParensResult.EXPLICIT_LAMBDA;
                         }
@@ -1455,21 +1470,9 @@
         PARENS;
     }
 
-    JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) {
-        ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
-        params.append(firstParam);
-        JCVariableDecl lastParam = firstParam;
-        while ((lastParam.mods.flags & Flags.VARARGS) == 0 && token.kind == COMMA) {
-            nextToken();
-            params.append(lastParam = formalParameter());
-        }
-        accept(RPAREN);
-        return lambdaExpressionOrStatementRest(params.toList(), pos);
-    }
-
     JCExpression lambdaExpressionOrStatement(boolean hasParens, boolean explicitParams, int pos) {
         List<JCVariableDecl> params = explicitParams ?
-                formalParameters() :
+                formalParameters(true) :
                 implicitParameters(hasParens);
 
         return lambdaExpressionOrStatementRest(params, pos);
@@ -2205,14 +2208,14 @@
         }
         case BREAK: {
             nextToken();
-            Name label = (token.kind == IDENTIFIER || token.kind == ASSERT || token.kind == ENUM) ? ident() : null;
+            Name label = (token.kind == IDENTIFIER || token.kind == ASSERT || token.kind == ENUM || token.kind == UNDERSCORE) ? ident() : null;
             JCBreak t = to(F.at(pos).Break(label));
             accept(SEMI);
             return t;
         }
         case CONTINUE: {
             nextToken();
-            Name label = (token.kind == IDENTIFIER || token.kind == ASSERT || token.kind == ENUM) ? ident() : null;
+            Name label = (token.kind == IDENTIFIER || token.kind == ASSERT || token.kind == ENUM || token.kind == UNDERSCORE) ? ident() : null;
             JCContinue t =  to(F.at(pos).Continue(label));
             accept(SEMI);
             return t;
@@ -2375,7 +2378,7 @@
             JCExpression t = term(EXPR | TYPE);
             if ((lastmode & TYPE) != 0 &&
                 (token.kind == IDENTIFIER || token.kind == ASSERT ||
-                 token.kind == ENUM)) {
+                 token.kind == ENUM || token.kind == UNDERSCORE)) {
                 return variableDeclarators(modifiersOpt(), t, stats).toList();
             } else if ((lastmode & TYPE) != 0 && token.kind == COLON) {
                 error(pos, "bad.initializer", "for-loop");
@@ -2635,8 +2638,18 @@
     /** VariableDeclaratorId = Ident BracketsOpt
      */
     JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type) {
+        return variableDeclaratorId(mods, type, false);
+    }
+    //where
+    JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean lambdaParameter) {
         int pos = token.pos;
-        Name name = ident();
+        Name name;
+        if (lambdaParameter && token.kind == UNDERSCORE) {
+            syntaxError(pos, "expected", IDENTIFIER);
+            name = token.name();
+        } else {
+            name = ident();
+        }
         if ((mods.flags & Flags.VARARGS) != 0 &&
                 token.kind == LBRACKET) {
             log.error(token.pos, "varargs.and.old.array.syntax");
@@ -3218,11 +3231,14 @@
      *  FormalParameterListNovarargs = [ FormalParameterListNovarargs , ] FormalParameter
      */
     List<JCVariableDecl> formalParameters() {
+        return formalParameters(false);
+    }
+    List<JCVariableDecl> formalParameters(boolean lambdaParameters) {
         ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
         JCVariableDecl lastParam = null;
         accept(LPAREN);
         if (token.kind != RPAREN) {
-            params.append(lastParam = formalParameter());
+            params.append(lastParam = formalParameter(lambdaParameters));
             while ((lastParam.mods.flags & Flags.VARARGS) == 0 && token.kind == COMMA) {
                 nextToken();
                 params.append(lastParam = formalParameter());
@@ -3261,6 +3277,9 @@
      *  LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter
      */
     protected JCVariableDecl formalParameter() {
+        return formalParameter(false);
+    }
+    protected JCVariableDecl formalParameter(boolean lambdaParameter) {
         JCModifiers mods = optFinal(Flags.PARAMETER);
         JCExpression type = parseType();
         if (token.kind == ELLIPSIS) {
@@ -3269,12 +3288,12 @@
             type = to(F.at(token.pos).TypeArray(type));
             nextToken();
         }
-        return variableDeclaratorId(mods, type);
+        return variableDeclaratorId(mods, type, lambdaParameter);
     }
 
     protected JCVariableDecl implicitParameter() {
         JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
-        return variableDeclaratorId(mods, null);
+        return variableDeclaratorId(mods, null, true);
     }
 
 /* ---------- auxiliary methods -------------- */
--- a/src/share/classes/com/sun/tools/javac/parser/Tokens.java	Mon Jan 07 18:45:59 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/parser/Tokens.java	Tue Jan 08 13:31:41 2013 +0100
@@ -74,7 +74,6 @@
     protected Tokens(Context context) {
         context.put(tokensKey, this);
         names = Names.instance(context);
-
         for (TokenKind t : TokenKind.values()) {
             if (t.name != null)
                 enterKeyword(t.name, t);
@@ -176,6 +175,7 @@
         TRUE("true", Tag.NAMED),
         FALSE("false", Tag.NAMED),
         NULL("null", Tag.NAMED),
+        UNDERSCORE("_", Tag.NAMED),
         ARROW("->"),
         COLCOL("::"),
         LPAREN("("),
--- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Jan 07 18:45:59 2013 +0100
+++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Tue Jan 08 13:31:41 2013 +0100
@@ -2135,6 +2135,10 @@
     as of release 1.4, ''assert'' is a keyword, and may not be used as an identifier\n\
     (use -source 1.4 or higher to use ''assert'' as a keyword)
 
+compiler.warn.underscore.as.identifier=\
+    ''_'' used as an identifier\n\
+    (use of ''_'' as an identifier might not be supported in future releases)
+
 compiler.err.enum.as.identifier=\
     as of release 5, ''enum'' is a keyword, and may not be used as an identifier\n\
     (use -source 1.4 or lower to use ''enum'' as an identifier)
--- a/test/tools/javac/diags/examples.not-yet.txt	Mon Jan 07 18:45:59 2013 +0100
+++ b/test/tools/javac/diags/examples.not-yet.txt	Tue Jan 08 13:31:41 2013 +0100
@@ -111,3 +111,4 @@
 compiler.err.static.intf.methods.not.supported.in.source                         #LAMBDA
 compiler.err.private.intf.methods.not.supported.in.source                        #LAMBDA
 compiler.err.pckge.mod.not.supported.in.source                                   #LAMBDA
+compiler.warn.underscore.as.identifier                                           #LAMBDA
--- a/test/tools/javac/lambda/LambdaParserTest.java	Mon Jan 07 18:45:59 2013 +0100
+++ b/test/tools/javac/lambda/LambdaParserTest.java	Tue Jan 08 13:31:41 2013 +0100
@@ -27,7 +27,7 @@
  * @bug 8003280
  * @summary Add lambda tests
  *  Add parser support for lambda expressions
- * @run main/timeout=360 LambdaParserTest
+ * @run main/timeout=720 LambdaParserTest
  */
 
 import com.sun.source.util.JavacTask;
@@ -39,6 +39,7 @@
 import javax.tools.SimpleJavaFileObject;
 import javax.tools.StandardJavaFileManager;
 import javax.tools.ToolProvider;
+import sun.net.www.content.image.png;
 
 public class LambdaParserTest {
 
@@ -47,12 +48,12 @@
     enum LambdaKind {
         NILARY_EXPR("()->x"),
         NILARY_STMT("()->{ return x; }"),
-        ONEARY_SHORT_EXPR("x->x"),
-        ONEARY_SHORT_STMT("x->{ return x; }"),
-        ONEARY_EXPR("(#A1 #M1 #T1 x)->x"),
-        ONEARY_STMT("(#A1 #M1 #T1 x)->{ return x; }"),
-        TWOARY_EXPR("(#A1 #M1 #T1 x, #A2 #M2 #T2 y)->x"),
-        TWOARY_STMT("(#A1 #M1 #T1 x, #A2 #M2 #T2 y)->{ return x; }");
+        ONEARY_SHORT_EXPR("#PN->x"),
+        ONEARY_SHORT_STMT("#PN->{ return x; }"),
+        ONEARY_EXPR("(#A1 #M1 #T1 #PN)->x"),
+        ONEARY_STMT("(#A1 #M1 #T1 #PN)->{ return x; }"),
+        TWOARY_EXPR("(#A1 #M1 #T1 #PN, #A2 #M2 #T2 y)->x"),
+        TWOARY_STMT("(#A1 #M1 #T1 #PN, #A2 #M2 #T2 y)->{ return x; }");
 
         String lambdaTemplate;
 
@@ -61,13 +62,15 @@
         }
 
         String getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2,
-                ModifierKind mk1, AnnotationKind ak1, ModifierKind mk2, AnnotationKind ak2) {
+                ModifierKind mk1, AnnotationKind ak1, ModifierKind mk2, AnnotationKind ak2,
+                LambdaParameterName pn) {
             return lambdaTemplate.replaceAll("#M1", mk1.modifier)
                     .replaceAll("#A1", ak1.anno)
                     .replaceAll("#M2", mk2.modifier)
                     .replaceAll("#A2", ak2.anno)
                     .replaceAll("#T1", pk1.parameterType)
-                    .replaceAll("#T2", pk2.parameterType);
+                    .replaceAll("#T2", pk2.parameterType)
+                    .replaceAll("#PN", pn.nameStr);
         }
 
         int arity() {
@@ -89,6 +92,17 @@
                     this == ONEARY_SHORT_STMT;
         }
     }
+    
+    enum LambdaParameterName {
+        IDENT("x"),
+        UNDERSCORE("_");
+        
+        String nameStr;
+
+        LambdaParameterName(String nameStr) {
+            this.nameStr = nameStr;
+        }
+    }
 
     enum LambdaParameterKind {
         IMPLICIT(""),
@@ -171,8 +185,8 @@
 
         String expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2,
                 ModifierKind mk1, AnnotationKind ak1, ModifierKind mk2, AnnotationKind ak2,
-                LambdaKind lk, SubExprKind sk) {
-            return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, ak1, mk2, ak2))
+                LambdaKind lk, LambdaParameterName pn, SubExprKind sk) {
+            return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, ak1, mk2, ak2, pn))
                     .replaceAll("#S", sk.subExpression);
         }
     }
@@ -199,21 +213,23 @@
         StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
 
         for (LambdaKind lk : LambdaKind.values()) {
-            for (LambdaParameterKind pk1 : LambdaParameterKind.values()) {
-                if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT) continue;
-                for (LambdaParameterKind pk2 : LambdaParameterKind.values()) {
-                    if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT) continue;
-                    for (ModifierKind mk1 : ModifierKind.values()) {
-                        if (mk1 != ModifierKind.NONE && lk.isShort()) continue;
-                        for (AnnotationKind ak1 : AnnotationKind.values()) {
-                            if (lk.arity() < 1 && mk1 != ModifierKind.NONE) continue;
-                            for (ModifierKind mk2 : ModifierKind.values()) {
-                                for (AnnotationKind ak2 : AnnotationKind.values()) {
-                                    if (lk.arity() < 2 && mk2 != ModifierKind.NONE) continue;
-                                    for (SubExprKind sk : SubExprKind.values()) {
-                                        for (ExprKind ek : ExprKind.values()) {
-                                            new LambdaParserTest(pk1, pk2, mk1, ak1, mk2, ak2, lk, sk, ek)
-                                                    .run(comp, fm);
+            for (LambdaParameterName pn : LambdaParameterName.values()) {
+                for (LambdaParameterKind pk1 : LambdaParameterKind.values()) {
+                    if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT) continue;
+                    for (LambdaParameterKind pk2 : LambdaParameterKind.values()) {
+                        if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT) continue;
+                        for (ModifierKind mk1 : ModifierKind.values()) {
+                            if (mk1 != ModifierKind.NONE && lk.isShort()) continue;
+                            for (AnnotationKind ak1 : AnnotationKind.values()) {
+                                if (lk.arity() < 1 && mk1 != ModifierKind.NONE) continue;
+                                for (ModifierKind mk2 : ModifierKind.values()) {
+                                    for (AnnotationKind ak2 : AnnotationKind.values()) {
+                                        if (lk.arity() < 2 && mk2 != ModifierKind.NONE) continue;
+                                        for (SubExprKind sk : SubExprKind.values()) {
+                                            for (ExprKind ek : ExprKind.values()) {
+                                                new LambdaParserTest(pk1, pk2, mk1, ak1, mk2, ak2, lk, pn, sk, ek)
+                                                        .run(comp, fm);
+                                            }
                                         }
                                     }
                                 }
@@ -231,6 +247,7 @@
     ModifierKind mk1, mk2;
     AnnotationKind ak1, ak2;
     LambdaKind lk;
+    LambdaParameterName pn;
     SubExprKind sk;
     ExprKind ek;
     JavaSource source;
@@ -238,7 +255,7 @@
 
     LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1,
             AnnotationKind ak1, ModifierKind mk2, AnnotationKind ak2, LambdaKind lk,
-            SubExprKind sk, ExprKind ek) {
+            LambdaParameterName pn, SubExprKind sk, ExprKind ek) {
         this.pk1 = pk1;
         this.pk2 = pk2;
         this.mk1 = mk1;
@@ -246,6 +263,7 @@
         this.mk2 = mk2;
         this.ak2 = ak2;
         this.lk = lk;
+        this.pn = pn;
         this.sk = sk;
         this.ek = ek;
         this.source = new JavaSource();
@@ -263,7 +281,7 @@
 
         public JavaSource() {
             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
-            source = template.replaceAll("#E", ek.expressionString(pk1, pk2, mk1, ak1, mk2, ak2, lk, sk));
+            source = template.replaceAll("#E", ek.expressionString(pk1, pk2, mk1, ak1, mk2, ak2, lk, pn, sk));
         }
 
         @Override
@@ -292,6 +310,9 @@
         errorExpected |= !lk.isShort() && ((lk.arity() > 0 && !ak1.compatibleWith(pk1)) ||
                 (lk.arity() > 1 && !ak2.compatibleWith(pk2)));
         
+        errorExpected |= pn == LambdaParameterName.UNDERSCORE &&
+                lk.arity() > 0;
+        
         if (lk.arity() == 2 &&
                 (pk1.explicit() != pk2.explicit() ||
                 pk1.isVarargs())) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/WarnUnderscoreAsIdent.java	Tue Jan 08 13:31:41 2013 +0100
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Check usages of underscore as identifier generate warnings
+ * @compile/fail/ref=WarnUnderscoreAsIdent.out -XDrawDiagnostics -Werror WarnUnderscoreAsIdent.java
+ */
+class _ {
+    String _ = null;
+    void _(String _) { }
+    void testFor() {
+        for (int _ = 0; _ < 10; _++);
+    }
+    void testTry() {
+        try { } catch (Throwable _) { }
+    }
+    void testLabel() {
+        _:
+        for (;;) {
+            break _;
+        }
+        _:
+        for (;;) {
+            continue _;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/WarnUnderscoreAsIdent.out	Tue Jan 08 13:31:41 2013 +0100
@@ -0,0 +1,15 @@
+WarnUnderscoreAsIdent.java:29:7: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:30:12: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:31:10: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:31:19: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:33:18: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:33:25: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:33:33: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:36:34: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:39:9: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:41:19: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:43:9: compiler.warn.underscore.as.identifier
+WarnUnderscoreAsIdent.java:45:22: compiler.warn.underscore.as.identifier
+- compiler.err.warnings.and.werror
+1 error
+12 warnings