diff -r 5c956be64b9e -r 71f35e4b93a5 src/share/classes/com/sun/tools/javac/parser/JavacParser.java --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Jan 23 20:57:40 2013 +0000 +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Jan 23 13:27:24 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -88,6 +88,41 @@ /** End position mappings container */ private final AbstractEndPosTable endPosTable; + // Because of javac's limited lookahead, some contexts are ambiguous in + // the presence of type annotations even though they are not ambiguous + // in the absence of type annotations. Consider this code: + // void m(String [] m) { } + // void m(String ... m) { } + // After parsing "String", javac calls bracketsOpt which immediately + // returns if the next character is not '['. Similarly, javac can see + // if the next token is ... and in that case parse an ellipsis. But in + // the presence of type annotations: + // void m(String @A [] m) { } + // void m(String @A ... m) { } + // no finite lookahead is enough to determine whether to read array + // levels or an ellipsis. Furthermore, if you call bracketsOpt, then + // bracketsOpt first reads all the leading annotations and only then + // discovers that it needs to fail. bracketsOpt needs a way to push + // back the extra annotations that it read. (But, bracketsOpt should + // not *always* be allowed to push back extra annotations that it finds + // -- in most contexts, any such extra annotation is an error. + // + // The following two variables permit type annotations that have + // already been read to be stored for later use. Alternate + // implementations are possible but would cause much larger changes to + // the parser. + + /** Type annotations that have already been read but have not yet been used. **/ + private List typeAnnotationsPushedBack = List.nil(); + + /** + * If the parser notices extra annotations, then it either immediately + * issues an error (if this variable is false) or places the extra + * annotations in variable typeAnnotationsPushedBack (if this variable + * is true). + */ + private boolean permitTypeAnnotationsPushBack = false; + interface ErrorRecoveryAction { JCTree doRecover(JavacParser parser); } @@ -126,6 +161,7 @@ this.allowDefaultMethods = source.allowDefaultMethods(); this.allowStaticInterfaceMethods = source.allowStaticInterfaceMethods(); this.allowIntersectionTypesInCast = source.allowIntersectionTypesInCast(); + this.allowTypeAnnotations = source.allowTypeAnnotations(); this.keepDocComments = keepDocComments; docComments = newDocCommentTable(keepDocComments, fac); this.keepLineMap = keepLineMap; @@ -215,6 +251,20 @@ */ boolean keepLineMap; + /** Switch: should we recognize type annotations? + */ + boolean allowTypeAnnotations; + + /** Switch: is "this" allowed as an identifier? + * This is needed to parse receiver types. + */ + boolean allowThisIdent; + + /** The type of the method receiver, as specified by a first "this" parameter. + */ + JCVariableDecl receiverParam; + + /** When terms are parsed, the mode determines which is expected: * mode = EXPR : an expression * mode = TYPE : a type @@ -558,6 +608,18 @@ nextToken(); return name; } + } else if (token.kind == THIS) { + if (allowThisIdent) { + // Make sure we're using a supported source version. + checkTypeAnnotations(); + Name name = token.name(); + nextToken(); + return name; + } else { + error(token.pos, "this.as.identifier"); + nextToken(); + return names.error; + } } else if (token.kind == UNDERSCORE) { warning(token.pos, "underscore.as.identifier"); Name name = token.name(); @@ -570,14 +632,21 @@ } /** - * Qualident = Ident { DOT Ident } + * Qualident = Ident { DOT [Annotations] Ident } */ - public JCExpression qualident() { + public JCExpression qualident(boolean allowAnnos) { JCExpression t = toP(F.at(token.pos).Ident(ident())); while (token.kind == DOT) { int pos = token.pos; nextToken(); + List 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; } @@ -686,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); @@ -706,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); } @@ -864,7 +960,7 @@ opStackSupply.add(opStack); return t; } -//where + //where /** Construct a binary or type test node. */ private JCExpression makeOp(int pos, @@ -943,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 ) * ] @@ -1067,6 +1163,34 @@ typeArgs = null; } else return illegal(); break; + 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)) { @@ -1075,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(); @@ -1082,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); @@ -1096,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; @@ -1136,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 @@ -1212,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) { @@ -1225,6 +1387,9 @@ mode = EXPR; continue; } + if (annos.nonEmpty()) { + t = toP(F.at(pos1).AnnotatedType(annos, t)); + } return t; } mode = oldmode; @@ -1253,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; } @@ -1263,6 +1436,12 @@ accept(COLCOL); t = memberReferenceSuffix(pos1, t); } else { + if (!annos.isEmpty()) { + if (permitTypeAnnotationsPushBack) + typeAnnotationsPushedBack = annos; + else + return illegal(annos.head.pos); + } break; } } @@ -1321,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: @@ -1382,9 +1561,36 @@ 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, LAX_IDENTIFIER)) { // '[', ']', Identifier/'_'/'assert'/'enum' -> explicit lambda @@ -1618,25 +1824,27 @@ /** * {@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); + 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); @@ -1644,11 +1852,15 @@ 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) { @@ -1657,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 @@ -1686,7 +1927,7 @@ accept(CLASS); if (token.pos == endPosTable.errorEndPos) { // error recovery - Name name = null; + Name name; if (LAX_IDENTIFIER.accepts(token.kind)) { name = token.name(); nextToken(); @@ -1724,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; @@ -1737,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; @@ -1766,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(); @@ -1775,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"); @@ -1796,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); @@ -1805,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(); @@ -1818,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.of(t), "array.dimension.missing"); } } else { ListBuffer dims = new ListBuffer(); + + // maintain array dimension type annotations + ListBuffer> dimAnnotations = ListBuffer.lb(); + dimAnnotations.append(annos); + dims.append(parseExpression()); accept(RBRACKET); - while (token.kind == LBRACKET) { + while (token.kind == LBRACKET + || token.kind == MONKEYS_AT) { + List maybeDimAnnos = typeAnnotationsOpt(); int pos = token.pos; nextToken(); if (token.kind == RBRACKET) { - elemtype = bracketsOptCont(elemtype, pos); + elemtype = bracketsOptCont(elemtype, pos, maybeDimAnnos); } else { - dims.append(parseExpression()); - accept(RBRACKET); + if (token.kind == RBRACKET) { // no dimension + elemtype = bracketsOptCont(elemtype, pos, maybeDimAnnos); + } else { + dimAnnotations.append(maybeDimAnnos); + dims.append(parseExpression()); + accept(RBRACKET); + } } } - return toP(F.at(newpos).NewArray(elemtype, dims.toList(), null)); + + JCNewArray na = toP(F.at(newpos).NewArray(elemtype, dims.toList(), null)); + na.dimAnnotations = dimAnnotations.toList(); + return na; } } @@ -2254,6 +2559,7 @@ } /** CatchClause = CATCH "(" FormalParameter ")" Block + * TODO: the "FormalParameter" is not correct, it uses the special "catchTypes" rule below. */ protected JCCatch catchClause() { int pos = token.pos; @@ -2276,7 +2582,9 @@ while (token.kind == BAR) { checkMulticatch(); nextToken(); - catchTypes.add(qualident()); + // Instead of qualident this is now parseType. + // But would that allow too much, e.g. arrays or generics? + catchTypes.add(parseType()); } return catchTypes.toList(); } @@ -2377,16 +2685,28 @@ } /** AnnotationsOpt = { '@' Annotation } + * + * @param kind Whether to parse an ANNOTATION or TYPE_ANNOTATION */ - List annotationsOpt() { + List annotationsOpt(Tag kind) { if (token.kind != MONKEYS_AT) return List.nil(); // optimization ListBuffer buf = new ListBuffer(); + int prevmode = mode; while (token.kind == MONKEYS_AT) { int pos = token.pos; nextToken(); - buf.append(annotation(pos)); + buf.append(annotation(pos, kind)); } - return buf.toList(); + lastmode = mode; + mode = prevmode; + List annotations = buf.toList(); + + return annotations; + } + + List typeAnnotationsOpt() { + List annotations = annotationsOpt(Tag.TYPE_ANNOTATION); + return annotations; } /** ModifiersOpt = { Modifier } @@ -2412,7 +2732,7 @@ if (token.deprecatedFlag()) { flags |= Flags.DEPRECATED; } - int lastPos = Position.NOPOS; + int lastPos; loop: while (true) { long flag; @@ -2439,12 +2759,11 @@ if (flag == Flags.ANNOTATION) { checkAnnotations(); if (token.kind != INTERFACE) { - JCAnnotation ann = annotation(lastPos); + JCAnnotation ann = annotation(lastPos, Tag.ANNOTATION); // if first modifier is an annotation, set pos to annotation's. if (flags == 0 && annotations.isEmpty()) pos = ann.pos; annotations.append(ann); - lastPos = ann.pos; flag = 0; } } @@ -2468,14 +2787,27 @@ } /** Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ] + * * @param pos position of "@" token + * @param kind Whether to parse an ANNOTATION or TYPE_ANNOTATION */ - JCAnnotation annotation(int pos) { + JCAnnotation annotation(int pos, Tag kind) { // accept(AT); // AT consumed by caller checkAnnotations(); - JCTree ident = qualident(); + if (kind == Tag.TYPE_ANNOTATION) { + checkTypeAnnotations(); + } + JCTree ident = qualident(false); List fieldValues = annotationFieldValuesOpt(); - JCAnnotation ann = F.at(pos).Annotation(ident, fieldValues); + JCAnnotation ann; + if (kind == Tag.ANNOTATION) { + ann = F.at(pos).Annotation(ident, fieldValues); + } else if (kind == Tag.TYPE_ANNOTATION) { + ann = F.at(pos).TypeAnnotation(ident, fieldValues); + } else { + throw new AssertionError("Unhandled annotation kind: " + kind); + } + storeEnd(ann, S.prevToken().endPos); return ann; } @@ -2528,7 +2860,7 @@ case MONKEYS_AT: pos = token.pos; nextToken(); - return annotation(pos); + return annotation(pos, Tag.ANNOTATION); case LBRACE: pos = token.pos; accept(LBRACE); @@ -2683,7 +3015,7 @@ mods = null; } nextToken(); - pid = qualident(); + pid = qualident(false); accept(SEMI); } ListBuffer defs = new ListBuffer(); @@ -2934,7 +3266,7 @@ flags |= Flags.DEPRECATED; } int pos = token.pos; - List annotations = annotationsOpt(); + List annotations = annotationsOpt(Tag.ANNOTATION); JCModifiers mods = F.at(annotations.isEmpty() ? Position.NOPOS : pos).Modifiers(flags, annotations); List typeArgs = typeArgumentsOpt(); int identPos = token.pos; @@ -3037,15 +3369,25 @@ mods.pos = pos; storeEnd(mods, pos); } + List annosAfterParams = annotationsOpt(Tag.ANNOTATION); + Token tk = token; pos = token.pos; JCExpression type; boolean isVoid = token.kind == VOID; if (isVoid) { + if (annosAfterParams.nonEmpty()) + illegal(annosAfterParams.head.pos); type = to(F.at(pos).TypeIdent(TypeTag.VOID)); nextToken(); } else { - type = parseType(); + if (annosAfterParams.nonEmpty()) { + mods.annotations = mods.annotations.appendList(annosAfterParams); + if (mods.pos == Position.NOPOS) + mods.pos = mods.annotations.head.pos; + } + // method returns types are un-annotated types + type = unannotatedType(); } if (token.kind == LPAREN && !isInterface && type.hasTag(IDENT)) { if (isInterface || tk.name() != className) @@ -3101,51 +3443,68 @@ if (isInterface && (mods.flags & Flags.STATIC) != 0) { checkStaticInterfaceMethods(); } - List params = formalParameters(); - if (!isVoid) type = bracketsOpt(type); - List thrown = List.nil(); - if (token.kind == THROWS) { - nextToken(); - thrown = qualidentList(); - } - JCBlock body = null; - JCExpression defaultValue; - if (token.kind == LBRACE) { - body = block(); - defaultValue = null; - } else { - if (token.kind == DEFAULT) { - accept(DEFAULT); - defaultValue = annotationValue(); + JCVariableDecl prevReceiverParam = this.receiverParam; + try { + this.receiverParam = null; + // Parsing formalParameters sets the receiverParam, if present + List params = formalParameters(); + if (!isVoid) type = bracketsOpt(type); + List thrown = List.nil(); + if (token.kind == THROWS) { + nextToken(); + thrown = qualidentList(); + } + JCBlock body = null; + JCExpression defaultValue; + if (token.kind == LBRACE) { + body = block(); + defaultValue = null; } else { - defaultValue = null; - } - accept(SEMI); - if (token.pos <= endPosTable.errorEndPos) { - // error recovery - skip(false, true, false, false); - if (token.kind == LBRACE) { - body = block(); + if (token.kind == DEFAULT) { + accept(DEFAULT); + defaultValue = annotationValue(); + } else { + defaultValue = null; + } + accept(SEMI); + if (token.pos <= endPosTable.errorEndPos) { + // error recovery + skip(false, true, false, false); + if (token.kind == LBRACE) { + body = block(); + } } } + + JCMethodDecl result = + toP(F.at(pos).MethodDef(mods, name, type, typarams, + receiverParam, params, thrown, + body, defaultValue)); + attach(result, dc); + return result; + } finally { + this.receiverParam = prevReceiverParam; } - - JCMethodDecl result = - toP(F.at(pos).MethodDef(mods, name, type, typarams, - params, thrown, - body, defaultValue)); - attach(result, dc); - return result; } - /** QualidentList = Qualident {"," Qualident} + /** QualidentList = [Annotations] Qualident {"," [Annotations] Qualident} */ List qualidentList() { ListBuffer ts = new ListBuffer(); - ts.append(qualident()); + + List typeAnnos = typeAnnotationsOpt(); + if (!typeAnnos.isEmpty()) + ts.append(toP(F.at(typeAnnos.head.pos).AnnotatedType(typeAnnos, qualident(true)))); + else + ts.append(qualident(true)); while (token.kind == COMMA) { nextToken(); - ts.append(qualident()); + + typeAnnos = typeAnnotationsOpt(); + if (!typeAnnos.isEmpty()) + ts.append(toP(F.at(typeAnnos.head.pos).AnnotatedType(typeAnnos, qualident(true)))); + else + ts.append(qualident(true)); } return ts.toList(); } @@ -3174,13 +3533,14 @@ /** * {@literal - * TypeParameter = TypeVariable [TypeParameterBound] + * TypeParameter = [Annotations] TypeVariable [TypeParameterBound] * TypeParameterBound = EXTENDS Type {"&" Type} * TypeVariable = Ident * } */ JCTypeParameter typeParameter() { int pos = token.pos; + List annos = typeAnnotationsOpt(); Name name = ident(); ListBuffer bounds = new ListBuffer(); if (token.kind == EXTENDS) { @@ -3191,7 +3551,7 @@ bounds.append(parseType()); } } - return toP(F.at(pos).TypeParameter(name, bounds.toList())); + return toP(F.at(pos).TypeParameter(name, bounds.toList(), annos)); } /** FormalParameters = "(" [ FormalParameterList ] ")" @@ -3203,10 +3563,17 @@ } List formalParameters(boolean lambdaParameters) { ListBuffer params = new ListBuffer(); - JCVariableDecl lastParam = null; + JCVariableDecl lastParam; accept(LPAREN); if (token.kind != RPAREN) { - params.append(lastParam = formalParameter(lambdaParameters)); + this.allowThisIdent = true; + lastParam = formalParameter(lambdaParameters); + if (lastParam.name.contentEquals(TokenKind.THIS.name)) { + this.receiverParam = lastParam; + } else { + params.append(lastParam); + } + this.allowThisIdent = false; while ((lastParam.mods.flags & Flags.VARARGS) == 0 && token.kind == COMMA) { nextToken(); params.append(lastParam = formalParameter(lambdaParameters)); @@ -3241,6 +3608,77 @@ return mods; } + /** + * Inserts the annotations (and possibly a new array level) + * to the left-most type in an array or nested type. + * + * When parsing a type like {@code @B Outer.Inner @A []}, the + * {@code @A} annotation should target the array itself, while + * {@code @B} targets the nested type {@code Outer}. + * + * Currently the parser parses the annotation first, then + * the array, and then inserts the annotation to the left-most + * nested type. + * + * When {@code createNewLevel} is true, then a new array + * level is inserted as the most inner type, and have the + * annotations target it. This is useful in the case of + * varargs, e.g. {@code String @A [] @B ...}, as the parser + * first parses the type {@code String @A []} then inserts + * a new array level with {@code @B} annotation. + */ + private JCExpression insertAnnotationsToMostInner( + JCExpression type, List annos, + boolean createNewLevel) { + int origEndPos = getEndPos(type); + JCExpression mostInnerType = type; + JCArrayTypeTree mostInnerArrayType = null; + while (TreeInfo.typeIn(mostInnerType).hasTag(TYPEARRAY)) { + mostInnerArrayType = (JCArrayTypeTree) TreeInfo.typeIn(mostInnerType); + mostInnerType = mostInnerArrayType.elemtype; + } + + if (createNewLevel) { + mostInnerType = to(F.at(token.pos).TypeArray(mostInnerType)); + } + + JCExpression mostInnerTypeToReturn = mostInnerType; + if (annos.nonEmpty()) { + JCExpression lastToModify = mostInnerType; + + while (TreeInfo.typeIn(mostInnerType).hasTag(SELECT) || + TreeInfo.typeIn(mostInnerType).hasTag(TYPEAPPLY)) { + while (TreeInfo.typeIn(mostInnerType).hasTag(SELECT)) { + lastToModify = mostInnerType; + mostInnerType = ((JCFieldAccess) TreeInfo.typeIn(mostInnerType)).getExpression(); + } + while (TreeInfo.typeIn(mostInnerType).hasTag(TYPEAPPLY)) { + lastToModify = mostInnerType; + mostInnerType = ((JCTypeApply) TreeInfo.typeIn(mostInnerType)).clazz; + } + } + + mostInnerType = F.at(annos.head.pos).AnnotatedType(annos, mostInnerType); + + if (TreeInfo.typeIn(lastToModify).hasTag(TYPEAPPLY)) { + ((JCTypeApply) TreeInfo.typeIn(lastToModify)).clazz = mostInnerType; + } else if (TreeInfo.typeIn(lastToModify).hasTag(SELECT)) { + ((JCFieldAccess) TreeInfo.typeIn(lastToModify)).selected = mostInnerType; + } else { + // We never saw a SELECT or TYPEAPPLY, return the annotated type. + mostInnerTypeToReturn = mostInnerType; + } + } + + if (mostInnerArrayType == null) { + return mostInnerTypeToReturn; + } else { + mostInnerArrayType.elemtype = mostInnerTypeToReturn; + storeEnd(type, origEndPos); + return type; + } + } + /** FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId * LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter */ @@ -3249,12 +3687,27 @@ } protected JCVariableDecl formalParameter(boolean lambdaParameter) { JCModifiers mods = optFinal(Flags.PARAMETER); + // need to distinguish between vararg annos and array annos + // look at typeAnnotationsPushedBack comment + this.permitTypeAnnotationsPushBack = true; JCExpression type = parseType(); + this.permitTypeAnnotationsPushBack = false; + if (token.kind == ELLIPSIS) { + List varargsAnnos = typeAnnotationsPushedBack; + typeAnnotationsPushedBack = List.nil(); checkVarargs(); mods.flags |= Flags.VARARGS; - type = to(F.at(token.pos).TypeArray(type)); + // insert var arg type annotations + type = insertAnnotationsToMostInner(type, varargsAnnos, true); nextToken(); + } else { + // if not a var arg, then typeAnnotationsPushedBack should be null + if (typeAnnotationsPushedBack.nonEmpty()) { + reportSyntaxError(typeAnnotationsPushedBack.head.pos, + "illegal.start.of.type"); + } + typeAnnotationsPushedBack = List.nil(); } return variableDeclaratorId(mods, type, lambdaParameter); } @@ -3508,6 +3961,12 @@ allowStaticInterfaceMethods = true; } } + void checkTypeAnnotations() { + if (!allowTypeAnnotations) { + log.error(token.pos, "type.annotations.not.supported.in.source", source.name); + allowTypeAnnotations = true; + } + } /* * a functional source tree and end position mappings