tyannos = null;
+ if (allowAnnos) {
+ tyannos = typeAnnotationsOpt();
+ }
t = toP(F.at(pos).Select(t, ident()));
+ if (tyannos != null && tyannos.nonEmpty()) {
+ t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+ }
}
return t;
}
@@ -675,7 +755,7 @@
nextToken();
return t;
}
-//where
+ //where
boolean isZero(String s) {
char[] cs = s.toCharArray();
int base = ((cs.length > 1 && Character.toLowerCase(cs[1]) == 'x') ? 16 : 10);
@@ -695,7 +775,34 @@
return term(EXPR);
}
+ /**
+ * parses (optional) type annotations followed by a type. If the
+ * annotations are present before the type and are not consumed during array
+ * parsing, this method returns a {@link JCAnnotatedType} consisting of
+ * these annotations and the underlying type. Otherwise, it returns the
+ * underlying type.
+ *
+ *
+ *
+ * Note that this method sets {@code mode} to {@code TYPE} first, before
+ * parsing annotations.
+ */
public JCExpression parseType() {
+ List annotations = typeAnnotationsOpt();
+ return parseType(annotations);
+ }
+
+ public JCExpression parseType(List annotations) {
+ JCExpression result = unannotatedType();
+
+ if (annotations.nonEmpty()) {
+ result = insertAnnotationsToMostInner(result, annotations, false);
+ }
+
+ return result;
+ }
+
+ public JCExpression unannotatedType() {
return term(TYPE);
}
@@ -853,7 +960,7 @@
opStackSupply.add(opStack);
return t;
}
-//where
+ //where
/** Construct a binary or type test node.
*/
private JCExpression makeOp(int pos,
@@ -932,9 +1039,9 @@
* | NEW [TypeArguments] Creator
* | "(" Arguments ")" "->" ( Expression | Block )
* | Ident "->" ( Expression | Block )
- * | Ident { "." Ident }
+ * | [Annotations] Ident { "." [Annotations] Ident }
* | Expression3 MemberReferenceSuffix
- * [ "[" ( "]" BracketsOpt "." CLASS | Expression "]" )
+ * [ [Annotations] "[" ( "]" BracketsOpt "." CLASS | Expression "]" )
* | Arguments
* | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator )
* ]
@@ -1056,7 +1163,35 @@
typeArgs = null;
} else return illegal();
break;
- case IDENTIFIER: case ASSERT: case ENUM:
+ case MONKEYS_AT:
+ // Only annotated cast types are valid
+ List typeAnnos = typeAnnotationsOpt();
+ if (typeAnnos.isEmpty()) {
+ // else there would be no '@'
+ throw new AssertionError("Expected type annotations, but found none!");
+ }
+
+ JCExpression expr = term3();
+
+ if ((mode & TYPE) == 0) {
+ // Type annotations on class literals no longer legal
+ if (!expr.hasTag(Tag.SELECT)) {
+ return illegal(typeAnnos.head.pos);
+ }
+ JCFieldAccess sel = (JCFieldAccess)expr;
+
+ if (sel.name != names._class) {
+ return illegal();
+ } else {
+ log.error(token.pos, "no.annotations.on.dot.class");
+ return expr;
+ }
+ } else {
+ // Type annotations targeting a cast
+ t = insertAnnotationsToMostInner(expr, typeAnnos, false);
+ }
+ break;
+ case UNDERSCORE: case IDENTIFIER: case ASSERT: case ENUM:
if (typeArgs != null) return illegal();
if ((mode & EXPR) != 0 && peekToken(ARROW)) {
t = lambdaExpressionOrStatement(false, false, pos);
@@ -1064,6 +1199,13 @@
t = toP(F.at(token.pos).Ident(ident()));
loop: while (true) {
pos = token.pos;
+ final List annos = typeAnnotationsOpt();
+
+ // need to report an error later if LBRACKET is for array
+ // index access rather than array creation level
+ if (!annos.isEmpty() && token.kind != LBRACKET && token.kind != ELLIPSIS)
+ return illegal(annos.head.pos);
+
switch (token.kind) {
case LBRACKET:
nextToken();
@@ -1071,11 +1213,23 @@
nextToken();
t = bracketsOpt(t);
t = toP(F.at(pos).TypeArray(t));
- t = bracketsSuffix(t);
+ if (annos.nonEmpty()) {
+ t = toP(F.at(pos).AnnotatedType(annos, t));
+ }
+ // .class is only allowed if there were no annotations
+ JCExpression nt = bracketsSuffix(t);
+ if (nt != t && (annos.nonEmpty() || TreeInfo.containsTypeAnnotation(t))) {
+ // t and nt are different if bracketsSuffix parsed a .class.
+ // The check for nonEmpty covers the case when the whole array is annotated.
+ // Helper method isAnnotated looks for annos deeply within t.
+ syntaxError("no.annotations.on.dot.class");
+ }
+ t = nt;
} else {
if ((mode & EXPR) != 0) {
mode = EXPR;
JCExpression t1 = term();
+ if (!annos.isEmpty()) t = illegal(annos.head.pos);
t = to(F.at(pos).Indexed(t, t1));
}
accept(RBRACKET);
@@ -1085,6 +1239,7 @@
if ((mode & EXPR) != 0) {
mode = EXPR;
t = arguments(typeArgs, t);
+ if (!annos.isEmpty()) t = illegal(annos.head.pos);
typeArgs = null;
}
break loop;
@@ -1125,9 +1280,25 @@
break loop;
}
}
+
+ List tyannos = null;
+ if ((mode & TYPE) != 0 && token.kind == MONKEYS_AT) {
+ tyannos = typeAnnotationsOpt();
+ }
// typeArgs saved for next loop iteration.
t = toP(F.at(pos).Select(t, ident()));
+ if (tyannos != null && tyannos.nonEmpty()) {
+ t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+ }
break;
+ case ELLIPSIS:
+ if (this.permitTypeAnnotationsPushBack) {
+ this.typeAnnotationsPushedBack = annos;
+ } else if (annos.nonEmpty()) {
+ // Don't return here -- error recovery attempt
+ illegal(annos.head.pos);
+ }
+ break loop;
case LT:
if ((mode & TYPE) == 0 && isUnboundMemberRef()) {
//this is an unbound method reference whose qualifier
@@ -1201,6 +1372,8 @@
if (typeArgs != null) illegal();
while (true) {
int pos1 = token.pos;
+ final List annos = typeAnnotationsOpt();
+
if (token.kind == LBRACKET) {
nextToken();
if ((mode & TYPE) != 0) {
@@ -1214,6 +1387,9 @@
mode = EXPR;
continue;
}
+ if (annos.nonEmpty()) {
+ t = toP(F.at(pos1).AnnotatedType(annos, t));
+ }
return t;
}
mode = oldmode;
@@ -1242,7 +1418,15 @@
t = innerCreator(pos2, typeArgs, t);
typeArgs = null;
} else {
+ List tyannos = null;
+ if ((mode & TYPE) != 0 && token.kind == MONKEYS_AT) {
+ // is the mode check needed?
+ tyannos = typeAnnotationsOpt();
+ }
t = toP(F.at(pos1).Select(t, ident()));
+ if (tyannos != null && tyannos.nonEmpty()) {
+ t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+ }
t = argumentsOpt(typeArgs, typeArgumentsOpt(t));
typeArgs = null;
}
@@ -1252,6 +1436,12 @@
accept(COLCOL);
t = memberReferenceSuffix(pos1, t);
} else {
+ if (!annos.isEmpty()) {
+ if (permitTypeAnnotationsPushBack)
+ typeAnnotationsPushedBack = annos;
+ else
+ return illegal(annos.head.pos);
+ }
break;
}
}
@@ -1274,7 +1464,7 @@
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 IDENTIFIER: case UNDERSCORE: 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:
@@ -1310,7 +1500,7 @@
ParensResult analyzeParens() {
int depth = 0;
boolean type = false;
- for (int lookahead = 0 ; ; lookahead++) {
+ outer: for (int lookahead = 0 ; ; lookahead++) {
TokenKind tk = S.token(lookahead).kind;
switch (tk) {
case EXTENDS: case SUPER: case COMMA:
@@ -1323,8 +1513,8 @@
if (peekToken(lookahead, RPAREN)) {
//Type, ')' -> cast
return ParensResult.CAST;
- } else if (peekToken(lookahead, IDENTIFIER)) {
- //Type, 'Identifier -> explicit lambda
+ } else if (peekToken(lookahead, LAX_IDENTIFIER)) {
+ //Type, Identifier/'_'/'assert'/'enum' -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
}
break;
@@ -1350,16 +1540,19 @@
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;
default:
return ParensResult.PARENS;
}
+ case UNDERSCORE:
+ case ASSERT:
+ case ENUM:
case IDENTIFIER:
- if (peekToken(lookahead, IDENTIFIER)) {
- // Identifier, Identifier -> explicit lambda
+ if (peekToken(lookahead, LAX_IDENTIFIER)) {
+ // Identifier, Identifier/'_'/'assert'/'enum' -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
} else if (peekToken(lookahead, RPAREN, ARROW)) {
// Identifier, ')' '->' -> implicit lambda
@@ -1368,12 +1561,39 @@
break;
case FINAL:
case ELLIPSIS:
- case MONKEYS_AT:
//those can only appear in explicit lambdas
return ParensResult.EXPLICIT_LAMBDA;
+ case MONKEYS_AT:
+ type = true;
+ lookahead += 1; //skip '@'
+ while (peekToken(lookahead, DOT)) {
+ lookahead += 2;
+ }
+ if (peekToken(lookahead, LPAREN)) {
+ lookahead++;
+ //skip annotation values
+ int nesting = 0;
+ for (; ; lookahead++) {
+ TokenKind tk2 = S.token(lookahead).kind;
+ switch (tk2) {
+ case EOF:
+ return ParensResult.PARENS;
+ case LPAREN:
+ nesting++;
+ break;
+ case RPAREN:
+ nesting--;
+ if (nesting == 0) {
+ continue outer;
+ }
+ break;
+ }
+ }
+ }
+ break;
case LBRACKET:
- if (peekToken(lookahead, RBRACKET, IDENTIFIER)) {
- // '[', ']', Identifier -> explicit lambda
+ if (peekToken(lookahead, RBRACKET, LAX_IDENTIFIER)) {
+ // '[', ']', Identifier/'_'/'assert'/'enum' -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
} else if (peekToken(lookahead, RBRACKET, RPAREN) ||
peekToken(lookahead, RBRACKET, AMP)) {
@@ -1402,11 +1622,11 @@
// '>', ')' -> cast
// '>', '&' -> cast
return ParensResult.CAST;
- } else if (peekToken(lookahead, IDENTIFIER, COMMA) ||
- peekToken(lookahead, IDENTIFIER, RPAREN, ARROW) ||
+ } else if (peekToken(lookahead, LAX_IDENTIFIER, COMMA) ||
+ peekToken(lookahead, LAX_IDENTIFIER, RPAREN, ARROW) ||
peekToken(lookahead, ELLIPSIS)) {
- // '>', Identifier, ',' -> explicit lambda
- // '>', Identifier, ')', '->' -> explicit lambda
+ // '>', Identifier/'_'/'assert'/'enum', ',' -> explicit lambda
+ // '>', Identifier/'_'/'assert'/'enum', ')', '->' -> explicit lambda
// '>', '...' -> explicit lambda
return ParensResult.EXPLICIT_LAMBDA;
}
@@ -1426,6 +1646,13 @@
}
}
+ /** Accepts all identifier-like tokens */
+ Filter LAX_IDENTIFIER = new Filter() {
+ public boolean accepts(TokenKind t) {
+ return t == IDENTIFIER || t == UNDERSCORE || t == ASSERT || t == ENUM;
+ }
+ };
+
enum ParensResult {
CAST,
EXPLICIT_LAMBDA,
@@ -1433,21 +1660,9 @@
PARENS;
}
- JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) {
- ListBuffer params = new ListBuffer();
- 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 params = explicitParams ?
- formalParameters() :
+ formalParameters(true) :
implicitParameters(hasParens);
return lambdaExpressionOrStatementRest(params, pos);
@@ -1609,37 +1824,43 @@
/**
* {@literal
* TypeArgument = Type
- * | "?"
- * | "?" EXTENDS Type {"&" Type}
- * | "?" SUPER Type
+ * | [Annotations] "?"
+ * | [Annotations] "?" EXTENDS Type {"&" Type}
+ * | [Annotations] "?" SUPER Type
* }
*/
JCExpression typeArgument() {
- if (token.kind != QUES) return parseType();
+ List annotations = typeAnnotationsOpt();
+ if (token.kind != QUES) return parseType(annotations);
int pos = token.pos;
nextToken();
+ JCExpression result;
if (token.kind == EXTENDS) {
TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.EXTENDS));
nextToken();
JCExpression bound = parseType();
- return F.at(pos).Wildcard(t, bound);
+ result = F.at(pos).Wildcard(t, bound);
} else if (token.kind == SUPER) {
TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.SUPER));
nextToken();
JCExpression bound = parseType();
- return F.at(pos).Wildcard(t, bound);
- } else if (token.kind == IDENTIFIER) {
+ result = F.at(pos).Wildcard(t, bound);
+ } else if (LAX_IDENTIFIER.accepts(token.kind)) {
//error recovery
TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND);
JCExpression wc = toP(F.at(pos).Wildcard(t, null));
JCIdent id = toP(F.at(token.pos).Ident(ident()));
JCErroneous err = F.at(pos).Erroneous(List.of(wc, id));
reportSyntaxError(err, "expected3", GT, EXTENDS, SUPER);
- return err;
+ result = err;
} else {
TypeBoundKind t = toP(F.at(pos).TypeBoundKind(BoundKind.UNBOUND));
- return toP(F.at(pos).Wildcard(t, null));
+ result = toP(F.at(pos).Wildcard(t, null));
}
+ if (!annotations.isEmpty()) {
+ result = toP(F.at(annotations.head.pos).AnnotatedType(annotations,result));
+ }
+ return result;
}
JCTypeApply typeArguments(JCExpression t, boolean diamondAllowed) {
@@ -1648,22 +1869,51 @@
return toP(F.at(pos).TypeApply(t, args));
}
- /** BracketsOpt = {"[" "]"}
+ /**
+ * BracketsOpt = { [Annotations] "[" "]" }*
+ *
+ *
+ *
+ * annotations
is the list of annotations targeting
+ * the expression t
.
*/
- private JCExpression bracketsOpt(JCExpression t) {
+ private JCExpression bracketsOpt(JCExpression t,
+ List annotations) {
+ List nextLevelAnnotations = typeAnnotationsOpt();
+
if (token.kind == LBRACKET) {
int pos = token.pos;
nextToken();
- t = bracketsOptCont(t, pos);
- F.at(pos);
+ t = bracketsOptCont(t, pos, nextLevelAnnotations);
+ } else if (!nextLevelAnnotations.isEmpty()) {
+ if (permitTypeAnnotationsPushBack) {
+ this.typeAnnotationsPushedBack = nextLevelAnnotations;
+ } else {
+ return illegal(nextLevelAnnotations.head.pos);
+ }
+ }
+
+ if (!annotations.isEmpty()) {
+ t = toP(F.at(token.pos).AnnotatedType(annotations, t));
}
return t;
}
- private JCArrayTypeTree bracketsOptCont(JCExpression t, int pos) {
+ /** BracketsOpt = [ "[" "]" { [Annotations] "[" "]"} ]
+ */
+ private JCExpression bracketsOpt(JCExpression t) {
+ return bracketsOpt(t, List.nil());
+ }
+
+ private JCExpression bracketsOptCont(JCExpression t, int pos,
+ List annotations) {
accept(RBRACKET);
t = bracketsOpt(t);
- return toP(F.at(pos).TypeArray(t));
+ t = toP(F.at(pos).TypeArray(t));
+ if (annotations.nonEmpty()) {
+ t = toP(F.at(pos).AnnotatedType(annotations, t));
+ }
+ return t;
}
/** BracketsSuffixExpr = "." CLASS
@@ -1677,8 +1927,8 @@
accept(CLASS);
if (token.pos == endPosTable.errorEndPos) {
// error recovery
- Name name = null;
- if (token.kind == IDENTIFIER) {
+ Name name;
+ if (LAX_IDENTIFIER.accepts(token.kind)) {
name = token.name();
nextToken();
} else {
@@ -1715,8 +1965,8 @@
if (token.kind == LT) {
typeArgs = typeArguments(false);
}
- Name refName = null;
- ReferenceMode refMode = null;
+ Name refName;
+ ReferenceMode refMode;
if (token.kind == NEW) {
refMode = ReferenceMode.NEW;
refName = names.init;
@@ -1728,18 +1978,31 @@
return toP(F.at(t.getStartPosition()).Reference(refMode, refName, t, typeArgs));
}
- /** Creator = Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
+ /** Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
*/
JCExpression creator(int newpos, List typeArgs) {
+ List newAnnotations = typeAnnotationsOpt();
+
switch (token.kind) {
case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
case DOUBLE: case BOOLEAN:
- if (typeArgs == null)
- return arrayCreatorRest(newpos, basicType());
+ if (typeArgs == null) {
+ if (newAnnotations.isEmpty()) {
+ return arrayCreatorRest(newpos, basicType());
+ } else {
+ return arrayCreatorRest(newpos, toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, basicType())));
+ }
+ }
break;
default:
}
- JCExpression t = qualident();
+ JCExpression t = qualident(true);
+
+ // handle type annotations for non primitive arrays
+ if (newAnnotations.nonEmpty()) {
+ t = insertAnnotationsToMostInner(t, newAnnotations, false);
+ }
+
int oldmode = mode;
mode = TYPE;
boolean diamondFound = false;
@@ -1757,7 +2020,13 @@
}
int pos = token.pos;
nextToken();
+ List tyannos = typeAnnotationsOpt();
t = toP(F.at(pos).Select(t, ident()));
+
+ if (tyannos != null && tyannos.nonEmpty()) {
+ t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+ }
+
if (token.kind == LT) {
lastTypeargsPos = token.pos;
checkGenerics();
@@ -1766,7 +2035,7 @@
}
}
mode = oldmode;
- if (token.kind == LBRACKET) {
+ if (token.kind == LBRACKET || token.kind == MONKEYS_AT) {
JCExpression e = arrayCreatorRest(newpos, t);
if (diamondFound) {
reportSyntaxError(lastTypeargsPos, "cannot.create.array.with.diamond");
@@ -1787,7 +2056,15 @@
}
return e;
} else if (token.kind == LPAREN) {
- return classCreatorRest(newpos, null, typeArgs, t);
+ JCNewClass newClass = classCreatorRest(newpos, null, typeArgs, t);
+ if (newClass.def != null) {
+ assert newClass.def.mods.annotations.isEmpty();
+ if (newAnnotations.nonEmpty()) {
+ newClass.def.mods.pos = earlier(newClass.def.mods.pos, newAnnotations.head.pos);
+ newClass.def.mods.annotations = List.convert(JCAnnotation.class, newAnnotations);
+ }
+ }
+ return newClass;
} else {
setErrorEndPos(token.pos);
reportSyntaxError(token.pos, "expected2", LPAREN, LBRACKET);
@@ -1796,10 +2073,17 @@
}
}
- /** InnerCreator = Ident [TypeArguments] ClassCreatorRest
+ /** InnerCreator = [Annotations] Ident [TypeArguments] ClassCreatorRest
*/
JCExpression innerCreator(int newpos, List typeArgs, JCExpression encl) {
+ List newAnnotations = typeAnnotationsOpt();
+
JCExpression t = toP(F.at(token.pos).Ident(ident()));
+
+ if (newAnnotations.nonEmpty()) {
+ t = toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, t));
+ }
+
if (token.kind == LT) {
int oldmode = mode;
checkGenerics();
@@ -1809,35 +2093,65 @@
return classCreatorRest(newpos, encl, typeArgs, t);
}
- /** ArrayCreatorRest = "[" ( "]" BracketsOpt ArrayInitializer
- * | Expression "]" {"[" Expression "]"} BracketsOpt )
+ /** ArrayCreatorRest = [Annotations] "[" ( "]" BracketsOpt ArrayInitializer
+ * | Expression "]" {[Annotations] "[" Expression "]"} BracketsOpt )
*/
JCExpression arrayCreatorRest(int newpos, JCExpression elemtype) {
+ List annos = typeAnnotationsOpt();
+
accept(LBRACKET);
if (token.kind == RBRACKET) {
accept(RBRACKET);
- elemtype = bracketsOpt(elemtype);
+ elemtype = bracketsOpt(elemtype, annos);
if (token.kind == LBRACE) {
- return arrayInitializer(newpos, elemtype);
+ JCNewArray na = (JCNewArray)arrayInitializer(newpos, elemtype);
+ if (annos.nonEmpty()) {
+ // when an array initializer is present then
+ // the parsed annotations should target the
+ // new array tree
+ // bracketsOpt inserts the annotation in
+ // elemtype, and it needs to be corrected
+ //
+ JCAnnotatedType annotated = (JCAnnotatedType)elemtype;
+ assert annotated.annotations == annos;
+ na.annotations = annotated.annotations;
+ na.elemtype = annotated.underlyingType;
+ }
+ return na;
} else {
JCExpression t = toP(F.at(newpos).NewArray(elemtype, List.nil(), null));
return syntaxError(token.pos, List.