diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-04-10 01:32:12 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-04-10 01:32:12 +0000 |
commit | 6ee326af4e77e6f05973486097884d7431f2108d (patch) | |
tree | def0e1b57abc33f0654ba903dec1f37715442417 /lib/Parse | |
parent | 0e9bf71c6187da074fd6d382a7b1d11fd9ddf3dd (diff) |
Disambiguation of '[[':
* In C++11, '[[' is ill-formed unless it starts an attribute-specifier. Reject
array sizes and array indexes which begin with a lambda-expression. Recover by
parsing the lambda as a lambda.
* In Objective-C++11, either '[' could be the start of a message-send.
Fully disambiguate this case: it turns out that the grammars of message-sends,
lambdas and attributes do not actually overlap. Accept any occurrence of '[['
where either '[' starts a message send, but reject a lambda in an array index
just like in C++11 mode.
Implement a couple of changes to the attribute wording which occurred after our
attributes implementation landed:
* In a function-declaration, the attributes go after the exception specification,
not after the right paren.
* A reference type can have attributes applied.
* An 'identifier' in an attribute can also be a keyword. Support for alternative
tokens (iso646 keywords) in attributes to follow.
And some bug fixes:
* Parse attributes after declarator-ids, even if they are not simple identifiers.
* Do not accept attributes after a parenthesized declarator.
* Accept attributes after an array size in a new-type-id.
* Partially disamiguate 'delete' followed by a lambda. More work is required
here for the case where the lambda-introducer is '[]'.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154369 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Parse')
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 119 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 39 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 10 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 21 | ||||
-rw-r--r-- | lib/Parse/ParseInit.cpp | 5 | ||||
-rw-r--r-- | lib/Parse/ParseStmt.cpp | 4 | ||||
-rw-r--r-- | lib/Parse/ParseTentative.cpp | 204 |
7 files changed, 280 insertions, 122 deletions
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b5ce193521..9bafa92d7f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -906,6 +906,39 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, *EndLoc = T.getCloseLocation(); } +/// DiagnoseProhibitedCXX11Attribute - We have found the opening square brackets +/// of a C++11 attribute-specifier in a location where an attribute is not +/// permitted. By C++11 [dcl.attr.grammar]p6, this is ill-formed. Diagnose this +/// situation. +/// +/// \return \c true if we skipped an attribute-like chunk of tokens, \c false if +/// this doesn't appear to actually be an attribute-specifier, and the caller +/// should try to parse it. +bool Parser::DiagnoseProhibitedCXX11Attribute() { + assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square)); + + switch (isCXX11AttributeSpecifier(/*Disambiguate*/true)) { + case CAK_NotAttributeSpecifier: + // No diagnostic: we're in Obj-C++11 and this is not actually an attribute. + return false; + + case CAK_InvalidAttributeSpecifier: + Diag(Tok.getLocation(), diag::err_l_square_l_square_not_attribute); + return false; + + case CAK_AttributeSpecifier: + // Parse and discard the attributes. + SourceLocation BeginLoc = ConsumeBracket(); + ConsumeBracket(); + SkipUntil(tok::r_square, /*StopAtSemi*/ false); + assert(Tok.is(tok::r_square) && "isCXX11AttributeSpecifier lied"); + SourceLocation EndLoc = ConsumeBracket(); + Diag(BeginLoc, diag::err_attributes_not_allowed) + << SourceRange(BeginLoc, EndLoc); + return true; + } +} + void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) { Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed) << attrs.Range; @@ -3454,14 +3487,11 @@ bool Parser::isConstructorDeclarator() { void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, bool VendorAttributesAllowed, bool CXX0XAttributesAllowed) { - if (getLangOpts().CPlusPlus0x && isCXX0XAttributeSpecifier()) { - SourceLocation Loc = Tok.getLocation(); + if (getLangOpts().CPlusPlus0x && CXX0XAttributesAllowed && + isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrs(AttrFactory); ParseCXX0XAttributes(attrs); - if (CXX0XAttributesAllowed) - DS.takeAttributesFrom(attrs); - else - Diag(Loc, diag::err_attributes_not_allowed); + DS.takeAttributesFrom(attrs); } SourceLocation EndLoc; @@ -3654,6 +3684,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, // Is a pointer. DeclSpec DS(AttrFactory); + // FIXME: GNU attributes are not allowed here in a new-type-id. ParseTypeQualifierListOpt(DS); D.ExtendWithDeclSpec(DS); @@ -3684,16 +3715,13 @@ void Parser::ParseDeclaratorInternal(Declarator &D, diag::warn_cxx98_compat_rvalue_reference : diag::ext_rvalue_reference); + // GNU-style and C++11 attributes are allowed here, as is restrict. + ParseTypeQualifierListOpt(DS); + D.ExtendWithDeclSpec(DS); + // C++ 8.3.2p1: cv-qualified references are ill-formed except when the // cv-qualifiers are introduced through the use of a typedef or of a // template type argument, in which case the cv-qualifiers are ignored. - // - // [GNU] Retricted references are allowed. - // [GNU] Attributes on references are allowed. - // [C++0x] Attributes on references are not allowed. - ParseTypeQualifierListOpt(DS, true, false); - D.ExtendWithDeclSpec(DS); - if (DS.getTypeQualifiers() != DeclSpec::TQ_unspecified) { if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(DS.getConstSpecLoc(), @@ -3754,13 +3782,19 @@ static void diagnoseMisplacedEllipsis(Parser &P, Declarator &D, /// [C99] direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']' /// [C99] direct-declarator '[' type-qual-list 'static' assignment-expr ']' /// [C99] direct-declarator '[' type-qual-list[opt] '*' ']' +/// [C++11] direct-declarator '[' constant-expression[opt] ']' +/// attribute-specifier-seq[opt] /// direct-declarator '(' parameter-type-list ')' /// direct-declarator '(' identifier-list[opt] ')' /// [GNU] direct-declarator '(' parameter-forward-declarations /// parameter-type-list[opt] ')' /// [C++] direct-declarator '(' parameter-declaration-clause ')' /// cv-qualifier-seq[opt] exception-specification[opt] +/// [C++11] direct-declarator '(' parameter-declaration-clause ')' +/// attribute-specifier-seq[opt] cv-qualifier-seq[opt] +/// ref-qualifier[opt] exception-specification[opt] /// [C++] declarator-id +/// [C++11] declarator-id attribute-specifier-seq[opt] /// /// declarator-id: [C++ 8] /// '...'[opt] id-expression @@ -3908,8 +3942,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) { assert(D.isPastIdentifier() && "Haven't past the location of the identifier yet?"); - // Don't parse attributes unless we have an identifier. - if (D.getIdentifier()) + // Don't parse attributes unless we have parsed an unparenthesized name. + if (D.hasName() && !D.getNumTypeObjects()) MaybeParseCXX0XAttributes(D); while (1) { @@ -4055,22 +4089,23 @@ void Parser::ParseParenDeclarator(Declarator &D) { /// declarator D up to a paren, which indicates that we are parsing function /// arguments. /// -/// If attrs is non-null, then the caller parsed those arguments immediately -/// after the open paren - they should be considered to be the first argument of -/// a parameter. If RequiresArg is true, then the first argument of the -/// function is required to be present and required to not be an identifier -/// list. +/// If FirstArgAttrs is non-null, then the caller parsed those arguments +/// immediately after the open paren - they should be considered to be the +/// first argument of a parameter. +/// +/// If RequiresArg is true, then the first argument of the function is required +/// to be present and required to not be an identifier list. /// -/// For C++, after the parameter-list, it also parses cv-qualifier-seq[opt], -/// (C++0x) ref-qualifier[opt], exception-specification[opt], and -/// (C++0x) trailing-return-type[opt]. +/// For C++, after the parameter-list, it also parses the cv-qualifier-seq[opt], +/// (C++11) ref-qualifier[opt], exception-specification[opt], +/// (C++11) attribute-specifier-seq[opt], and (C++11) trailing-return-type[opt]. /// -/// [C++0x] exception-specification: +/// [C++11] exception-specification: /// dynamic-exception-specification /// noexcept-specification /// void Parser::ParseFunctionDeclarator(Declarator &D, - ParsedAttributes &attrs, + ParsedAttributes &FirstArgAttrs, BalancedDelimiterTracker &Tracker, bool RequiresArg) { assert(getCurScope()->isFunctionPrototypeScope() && @@ -4096,8 +4131,9 @@ void Parser::ParseFunctionDeclarator(Declarator &D, SmallVector<ParsedType, 2> DynamicExceptions; SmallVector<SourceRange, 2> DynamicExceptionRanges; ExprResult NoexceptExpr; + ParsedAttributes FnAttrs(AttrFactory); ParsedType TrailingReturnType; - + Actions.ActOnStartFunctionDeclarator(); SourceLocation EndLoc; @@ -4111,7 +4147,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, EndLoc = Tracker.getCloseLocation(); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, attrs, ParamInfo, EllipsisLoc); + ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -4122,22 +4158,24 @@ void Parser::ParseFunctionDeclarator(Declarator &D, EndLoc = Tracker.getCloseLocation(); if (getLangOpts().CPlusPlus) { - MaybeParseCXX0XAttributes(attrs); + // FIXME: Accept these components in any order, and produce fixits to + // correct the order if the user gets it wrong. Ideally we should deal + // with the virt-specifier-seq and pure-specifier in the same way. // Parse cv-qualifier-seq[opt]. - ParseTypeQualifierListOpt(DS, false /*no attributes*/); - if (!DS.getSourceRange().getEnd().isInvalid()) { - EndLoc = DS.getSourceRange().getEnd(); - ConstQualifierLoc = DS.getConstSpecLoc(); - VolatileQualifierLoc = DS.getVolatileSpecLoc(); - } + ParseTypeQualifierListOpt(DS, false /*no attributes*/, false); + if (!DS.getSourceRange().getEnd().isInvalid()) { + EndLoc = DS.getSourceRange().getEnd(); + ConstQualifierLoc = DS.getConstSpecLoc(); + VolatileQualifierLoc = DS.getVolatileSpecLoc(); + } // Parse ref-qualifier[opt]. if (Tok.is(tok::amp) || Tok.is(tok::ampamp)) { Diag(Tok, getLangOpts().CPlusPlus0x ? diag::warn_cxx98_compat_ref_qualifier : diag::ext_ref_qualifier); - + RefQualifierIsLValueRef = Tok.is(tok::amp); RefQualifierLoc = ConsumeToken(); EndLoc = RefQualifierLoc; @@ -4151,6 +4189,10 @@ void Parser::ParseFunctionDeclarator(Declarator &D, if (ESpecType != EST_None) EndLoc = ESpecRange.getEnd(); + // Parse attribute-specifier-seq[opt]. Per DR 979 and DR 1297, this goes + // after the exception-specification. + MaybeParseCXX0XAttributes(FnAttrs); + // Parse trailing-return-type[opt]. if (getLangOpts().CPlusPlus0x && Tok.is(tok::arrow)) { Diag(Tok, diag::warn_cxx98_compat_trailing_return_type); @@ -4181,7 +4223,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, Tracker.getOpenLocation(), EndLoc, D, TrailingReturnType), - attrs, EndLoc); + FnAttrs, EndLoc); Actions.ActOnEndFunctionDeclarator(); } @@ -4455,7 +4497,12 @@ void Parser::ParseParameterDeclarationClause( /// [C99] direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']' /// [C99] direct-declarator '[' type-qual-list 'static' assignment-expr ']' /// [C99] direct-declarator '[' type-qual-list[opt] '*' ']' +/// [C++11] direct-declarator '[' constant-expression[opt] ']' +/// attribute-specifier-seq[opt] void Parser::ParseBracketDeclarator(Declarator &D) { + if (CheckProhibitedCXX11Attribute()) + return; + BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 9321c137fb..10ed3b1516 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -2736,38 +2736,38 @@ void Parser::PopParsingClass(Sema::ParsingClassState state) { Victim->TemplateScope = getCurScope()->getParent()->isTemplateParamScope(); } -/// ParseCXX0XAttributeSpecifier - Parse a C++0x attribute-specifier. Currently +/// ParseCXX0XAttributeSpecifier - Parse a C++11 attribute-specifier. Currently /// only parses standard attributes. /// -/// [C++0x] attribute-specifier: +/// [C++11] attribute-specifier: /// '[' '[' attribute-list ']' ']' /// alignment-specifier /// -/// [C++0x] attribute-list: +/// [C++11] attribute-list: /// attribute[opt] /// attribute-list ',' attribute[opt] /// -/// [C++0x] attribute: +/// [C++11] attribute: /// attribute-token attribute-argument-clause[opt] /// -/// [C++0x] attribute-token: +/// [C++11] attribute-token: /// identifier /// attribute-scoped-token /// -/// [C++0x] attribute-scoped-token: +/// [C++11] attribute-scoped-token: /// attribute-namespace '::' identifier /// -/// [C++0x] attribute-namespace: +/// [C++11] attribute-namespace: /// identifier /// -/// [C++0x] attribute-argument-clause: +/// [C++11] attribute-argument-clause: /// '(' balanced-token-seq ')' /// -/// [C++0x] balanced-token-seq: +/// [C++11] balanced-token-seq: /// balanced-token /// balanced-token-seq balanced-token /// -/// [C++0x] balanced-token: +/// [C++11] balanced-token: /// '(' balanced-token-seq ')' /// '[' balanced-token-seq ']' /// '{' balanced-token-seq '}' @@ -2781,7 +2781,7 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, } assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square) - && "Not a C++0x attribute list"); + && "Not a C++11 attribute list"); Diag(Tok.getLocation(), diag::warn_cxx98_compat_attribute); @@ -2793,7 +2793,11 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, ConsumeToken(); } - while (Tok.is(tok::identifier) || Tok.is(tok::comma)) { + // C++11 [dcl.attr.grammar]p3: If a keyword or an alternative token that + // satisfies the syntactic requirements of an identifier is contained in an + // attribute-token, it is considered an identifier. + // FIXME: Support alternative tokens here. + while (Tok.getIdentifierInfo() || Tok.is(tok::comma)) { // attribute not present if (Tok.is(tok::comma)) { ConsumeToken(); @@ -2807,7 +2811,7 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, if (Tok.is(tok::coloncolon)) { ConsumeToken(); - if (!Tok.is(tok::identifier)) { + if (!Tok.getIdentifierInfo()) { Diag(Tok.getLocation(), diag::err_expected_ident); SkipUntil(tok::r_square, tok::comma, true, true); continue; @@ -2824,8 +2828,7 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, // No scoped names are supported; ideally we could put all non-standard // attributes into namespaces. if (!ScopeName) { - switch(AttributeList::getKind(AttrName)) - { + switch (AttributeList::getKind(AttrName)) { // No arguments case AttributeList::AT_carries_dependency: case AttributeList::AT_noreturn: { @@ -2852,6 +2855,9 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, // SkipUntil maintains the balancedness of tokens. SkipUntil(tok::r_paren, false); } + + if (Tok.is(tok::ellipsis)) + ConsumeToken(); } if (ExpectAndConsume(tok::r_square, diag::err_expected_rsquare)) @@ -2874,7 +2880,7 @@ void Parser::ParseCXX0XAttributes(ParsedAttributesWithRange &attrs, do { ParseCXX0XAttributeSpecifier(attrs, endLoc); - } while (isCXX0XAttributeSpecifier()); + } while (isCXX11AttributeSpecifier()); attrs.Range = SourceRange(StartLoc, *endLoc); } @@ -2892,6 +2898,7 @@ void Parser::ParseMicrosoftAttributes(ParsedAttributes &attrs, assert(Tok.is(tok::l_square) && "Not a Microsoft attribute list"); while (Tok.is(tok::l_square)) { + // FIXME: If this is actually a C++11 attribute, parse it as one. ConsumeBracket(); SkipUntil(tok::r_square, true, true); if (endLoc) *endLoc = Tok.getLocation(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index f68359b8d2..7f3a815edc 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1287,7 +1287,12 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { if (getLangOpts().ObjC1 && Tok.isAtStartOfLine() && isSimpleObjCMessageExpression()) return move(LHS); - + + // Reject array indices starting with a lambda-expression. '[[' is + // reserved for attributes. + if (CheckProhibitedCXX11Attribute()) + return ExprError(); + BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); Loc = T.getOpenLocation(); @@ -1756,6 +1761,9 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { Comps.back().LocEnd = ConsumeToken(); } else if (Tok.is(tok::l_square)) { + if (CheckProhibitedCXX11Attribute()) + return ExprError(); + // offsetof-member-designator: offsetof-member-design '[' expression ']' Comps.push_back(Sema::OffsetOfComponent()); Comps.back().isBrackets = true; diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 0b7b2d9668..2af74824eb 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -1817,7 +1817,9 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, bool isNew = Tok.getKind() == tok::kw_new; // Consume the 'new' or 'delete'. SymbolLocations[SymbolIdx++] = ConsumeToken(); - if (Tok.is(tok::l_square)) { + // Check for array new/delete. + if (Tok.is(tok::l_square) && + (!getLangOpts().CPlusPlus0x || NextToken().isNot(tok::l_square))) { // Consume the '[' and ']'. BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); @@ -2346,6 +2348,10 @@ void Parser::ParseDirectNewDeclarator(Declarator &D) { // Parse the array dimensions. bool first = true; while (Tok.is(tok::l_square)) { + // An array-size expression can't start with a lambda. + if (CheckProhibitedCXX11Attribute()) + continue; + BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); @@ -2360,13 +2366,16 @@ void Parser::ParseDirectNewDeclarator(Declarator &D) { T.consumeClose(); - ParsedAttributes attrs(AttrFactory); + // Attributes here appertain to the array type. C++11 [expr.new]p5. + ParsedAttributes Attrs(AttrFactory); + MaybeParseCXX0XAttributes(Attrs); + D.AddTypeInfo(DeclaratorChunk::getArray(0, /*static=*/false, /*star=*/false, Size.release(), T.getOpenLocation(), T.getCloseLocation()), - attrs, T.getCloseLocation()); + Attrs, T.getCloseLocation()); if (T.getCloseLocation().isInvalid()) return; @@ -2418,7 +2427,11 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { // Array delete? bool ArrayDelete = false; - if (Tok.is(tok::l_square)) { + if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) { + // FIXME: This could be the start of a lambda-expression. We should + // disambiguate this, but that will require arbitrary lookahead if + // the next token is '(': + // delete [](int*){ /* ... */ ArrayDelete = true; BalancedDelimiterTracker T(*this, tok::l_square); diff --git a/lib/Parse/ParseInit.cpp b/lib/Parse/ParseInit.cpp index d4fc9056e4..1c349fddbd 100644 --- a/lib/Parse/ParseInit.cpp +++ b/lib/Parse/ParseInit.cpp @@ -211,6 +211,11 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() { // [foo ... bar] -> array designator // [4][foo bar] -> obsolete GNU designation with objc message send. // + // We do not need to check for an expression starting with [[ here. If it + // contains an Objective-C message send, then it is not an ill-formed + // attribute. If it is a lambda-expression within an array-designator, then + // it will be rejected because a constant-expression cannot begin with a + // lambda-expression. InMessageExpressionRAIIObject InMessage(*this, true); BalancedDelimiterTracker T(*this, tok::l_square); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 79b53c3b24..9f6a3a03da 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -84,7 +84,7 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement, ParenBraceBracketBalancer BalancerRAIIObj(*this); ParsedAttributesWithRange attrs(AttrFactory); - MaybeParseCXX0XAttributes(attrs); + MaybeParseCXX0XAttributes(attrs, 0, /*MightBeObjCMessageSend*/ true); // Cases in this switch statement should fall through if the parser expects // the token to end in a semicolon (in which case SemiError should be set), @@ -789,7 +789,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { ConsumeToken(); ParsedAttributesWithRange attrs(AttrFactory); - MaybeParseCXX0XAttributes(attrs); + MaybeParseCXX0XAttributes(attrs, 0, /*MightBeObjCMessageSend*/ true); // If this is the start of a declaration, parse it as such. if (isDeclarationStatement()) { diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index b650169b25..18bc0ddc5b 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -374,91 +374,167 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { return TPR == TPResult::True(); } -/// isCXX0XAttributeSpecifier - returns true if this is a C++0x -/// attribute-specifier. By default, unless in Obj-C++, only a cursory check is -/// performed that will simply return true if a [[ is seen. Currently C++ has no -/// syntactical ambiguities from this check, but it may inhibit error recovery. -/// If CheckClosing is true, a check is made for closing ]] brackets. +/// \brief Returns true if this is a C++11 attribute-specifier. Per +/// C++11 [dcl.attr.grammar]p6, two consecutive left square bracket tokens +/// always introduce an attribute. In Objective-C++11, this rule does not +/// apply if either '[' begins a message-send. /// -/// If given, After is set to the token after the attribute-specifier so that -/// appropriate parsing decisions can be made; it is left untouched if false is -/// returned. +/// If Disambiguate is true, we try harder to determine whether a '[[' starts +/// an attribute-specifier, and return CAK_InvalidAttributeSpecifier if not. /// -/// FIXME: If an error is in the closing ]] brackets, the program assumes -/// the absence of an attribute-specifier, which can cause very yucky errors -/// to occur. +/// If OuterMightBeMessageSend is true, we assume the outer '[' is either an +/// Obj-C message send or the start of an attribute. Otherwise, we assume it +/// is not an Obj-C message send. /// -/// [C++0x] attribute-specifier: +/// C++11 [dcl.attr.grammar]: +/// +/// attribute-specifier: /// '[' '[' attribute-list ']' ']' /// alignment-specifier /// -/// [C++0x] attribute-list: +/// attribute-list: /// attribute[opt] /// attribute-list ',' attribute[opt] +/// attribute '...' +/// attribute-list ',' attribute '...' /// -/// [C++0x] attribute: +/// attribute: /// attribute-token attribute-argument-clause[opt] /// -/// [C++0x] attribute-token: -/// identifier -/// attribute-scoped-token -/// -/// [C++0x] attribute-scoped-token: -/// attribute-namespace '::' identifier -/// -/// [C++0x] attribute-namespace: +/// attribute-token: /// identifier +/// identifier '::' identifier /// -/// [C++0x] attribute-argument-clause: +/// attribute-argument-clause: /// '(' balanced-token-seq ')' -/// -/// [C++0x] balanced-token-seq: -/// balanced-token -/// balanced-token-seq balanced-token -/// -/// [C++0x] balanced-token: -/// '(' balanced-token-seq ')' -/// '[' balanced-token-seq ']' -/// '{' balanced-token-seq '}' -/// any token but '(', ')', '[', ']', '{', or '}' -bool Parser::isCXX0XAttributeSpecifier (bool CheckClosing, - tok::TokenKind *After) { +Parser::CXX11AttributeKind +Parser::isCXX11AttributeSpecifier(bool Disambiguate, + bool OuterMightBeMessageSend) { if (Tok.is(tok::kw_alignas)) - return true; + return CAK_AttributeSpecifier; if (Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) - return false; - - // No tentative parsing if we don't need to look for ]] - if (!CheckClosing && !getLangOpts().ObjC1) - return true; - - struct TentativeReverter { - TentativeParsingAction PA; + return CAK_NotAttributeSpecifier; - TentativeReverter (Parser& P) - : PA(P) - {} - ~TentativeReverter () { - PA.Revert(); - } - } R(*this); + // No tentative parsing if we don't need to look for ']]' or a lambda. + if (!Disambiguate && !getLangOpts().ObjC1) + return CAK_AttributeSpecifier; + + TentativeParsingAction PA(*this); // Opening brackets were checked for above. ConsumeBracket(); - ConsumeBracket(); - // SkipUntil will handle balanced tokens, which are guaranteed in attributes. - SkipUntil(tok::r_square, false); + // Outside Obj-C++11, treat anything with a matching ']]' as an attribute. + if (!getLangOpts().ObjC1) { + ConsumeBracket(); + + bool IsAttribute = SkipUntil(tok::r_square, false); + IsAttribute &= Tok.is(tok::r_square); + + PA.Revert(); + + return IsAttribute ? CAK_AttributeSpecifier : CAK_InvalidAttributeSpecifier; + } + + // In Obj-C++11, we need to distinguish four situations: + // 1a) int x[[attr]]; C++11 attribute. + // 1b) [[attr]]; C++11 statement attribute. + // 2) int x[[obj](){ return 1; }()]; Lambda in array size/index. + // 3a) int x[[obj get]]; Message send in array size/index. + // 3b) [[Class alloc] init]; Message send in message send. + // 4) [[obj]{ return self; }() doStuff]; Lambda in message send. + // (1) is an attribute, (2) is ill-formed, and (3) and (4) are accepted. + + // If we have a lambda-introducer, then this is definitely not a message send. + // FIXME: If this disambiguation is too slow, fold the tentative lambda parse + // into the tentative attribute parse below. + LambdaIntroducer Intro; + if (!TryParseLambdaIntroducer(Intro)) { + // A lambda cannot end with ']]', and an attribute must. + bool IsAttribute = Tok.is(tok::r_square); + + PA.Revert(); + + if (IsAttribute) + // Case 1: C++11 attribute. + return CAK_AttributeSpecifier; + + if (OuterMightBeMessageSend) + // Case 4: Lambda in message send. + return CAK_NotAttributeSpecifier; + + // Case 2: Lambda in array size / index. + return CAK_InvalidAttributeSpecifier; + } - if (Tok.isNot(tok::r_square)) - return false; ConsumeBracket(); - if (After) - *After = Tok.getKind(); + // If we don't have a lambda-introducer, then we have an attribute or a + // message-send. + bool IsAttribute = true; + while (Tok.isNot(tok::r_square)) { + if (Tok.is(tok::comma)) { + // Case 1: Stray commas can only occur in attributes. + PA.Revert(); + return CAK_AttributeSpecifier; + } + + // Parse the attribute-token, if present. + // C++11 [dcl.attr.grammar]: + // If a keyword or an alternative token that satisfies the syntactic + // requirements of an identifier is contained in an attribute-token, + // it is considered an identifier. + if (!Tok.getIdentifierInfo()) { + IsAttribute = false; + break; + } + ConsumeToken(); + if (Tok.is(tok::coloncolon)) { + ConsumeToken(); + if (!Tok.getIdentifierInfo()) { + IsAttribute = false; + break; + } + ConsumeToken(); + } + + // Parse the attribute-argument-clause, if present. + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + if (!SkipUntil(tok::r_paren, false)) { + IsAttribute = false; + break; + } + } + + if (Tok.is(tok::ellipsis)) + ConsumeToken(); + + if (Tok.isNot(tok::comma)) + break; + + ConsumeToken(); + } + + // An attribute must end ']]'. + if (IsAttribute) { + if (Tok.is(tok::r_square)) { + ConsumeBracket(); + IsAttribute = Tok.is(tok::r_square); + } else { + IsAttribute = false; + } + } + + PA.Revert(); + + if (IsAttribute) + // Case 1: C++11 statement attribute. + return CAK_AttributeSpecifier; - return true; + // Case 3: Message send. + return CAK_NotAttributeSpecifier; } /// declarator: @@ -1217,11 +1293,13 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { /// parameter-declaration-list ',' parameter-declaration /// /// parameter-declaration: -/// decl-specifier-seq declarator attributes[opt] -/// decl-specifier-seq declarator attributes[opt] '=' assignment-expression -/// decl-specifier-seq abstract-declarator[opt] attributes[opt] -/// decl-specifier-seq abstract-declarator[opt] attributes[opt] +/// attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt] +/// attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt] /// '=' assignment-expression +/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] +/// attributes[opt] +/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] +/// attributes[opt] '=' assignment-expression /// Parser::TPResult Parser::TryParseParameterDeclarationClause() { |