summaryrefslogtreecommitdiffstats
path: root/lib/Parse
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2012-04-10 01:32:12 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2012-04-10 01:32:12 +0000
commit6ee326af4e77e6f05973486097884d7431f2108d (patch)
treedef0e1b57abc33f0654ba903dec1f37715442417 /lib/Parse
parent0e9bf71c6187da074fd6d382a7b1d11fd9ddf3dd (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.cpp119
-rw-r--r--lib/Parse/ParseDeclCXX.cpp39
-rw-r--r--lib/Parse/ParseExpr.cpp10
-rw-r--r--lib/Parse/ParseExprCXX.cpp21
-rw-r--r--lib/Parse/ParseInit.cpp5
-rw-r--r--lib/Parse/ParseStmt.cpp4
-rw-r--r--lib/Parse/ParseTentative.cpp204
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() {