diff options
Diffstat (limited to 'lib/Parse/ParseDecl.cpp')
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 400 |
1 files changed, 259 insertions, 141 deletions
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 40674ee7a9..1f9f85bab7 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -55,6 +55,16 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); } + +/// isAttributeLateParsed - Return true if the attribute has arguments that +/// require late parsing. +static bool isAttributeLateParsed(const IdentifierInfo &II) { + return llvm::StringSwitch<bool>(II.getName()) +#include "clang/Parse/AttrLateParsed.inc" + .Default(false); +} + + /// ParseGNUAttributes - Parse a non-empty attributes list. /// /// [GNU] attributes: @@ -92,7 +102,8 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, /// a pressing need to implement the 2 token lookahead. void Parser::ParseGNUAttributes(ParsedAttributes &attrs, - SourceLocation *endLoc) { + SourceLocation *endLoc, + LateParsedAttrList *LateAttrs) { assert(Tok.is(tok::kw___attribute) && "Not a GNU attribute list!"); while (Tok.is(tok::kw___attribute)) { @@ -109,7 +120,6 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, // Parse the attribute-list. e.g. __attribute__(( weak, alias("__f") )) while (Tok.is(tok::identifier) || isDeclarationSpecifier() || Tok.is(tok::comma)) { - if (Tok.is(tok::comma)) { // allows for empty/non-empty attributes. ((__vector_size__(16),,,,)) ConsumeToken(); @@ -119,116 +129,26 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, IdentifierInfo *AttrName = Tok.getIdentifierInfo(); SourceLocation AttrNameLoc = ConsumeToken(); - // Availability attributes have their own grammar. - if (AttrName->isStr("availability")) - ParseAvailabilityAttribute(*AttrName, AttrNameLoc, attrs, endLoc); - // Thread safety attributes fit into the FIXME case above, so we - // just parse the arguments as a list of expressions - else if (IsThreadSafetyAttribute(AttrName->getName())) - ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, attrs, endLoc); - // check if we have a "parameterized" attribute - else if (Tok.is(tok::l_paren)) { - ConsumeParen(); // ignore the left paren loc for now - - if (Tok.is(tok::identifier)) { - IdentifierInfo *ParmName = Tok.getIdentifierInfo(); - SourceLocation ParmLoc = ConsumeToken(); - - if (Tok.is(tok::r_paren)) { - // __attribute__(( mode(byte) )) - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - ParmName, ParmLoc, 0, 0); - } else if (Tok.is(tok::comma)) { - ConsumeToken(); - // __attribute__(( format(printf, 1, 2) )) - ExprVector ArgExprs(Actions); - bool ArgExprsOk = true; - - // now parse the non-empty comma separated list of expressions - while (1) { - ExprResult ArgExpr(ParseAssignmentExpression()); - if (ArgExpr.isInvalid()) { - ArgExprsOk = false; - SkipUntil(tok::r_paren); - break; - } else { - ArgExprs.push_back(ArgExpr.release()); - } - if (Tok.isNot(tok::comma)) - break; - ConsumeToken(); // Eat the comma, move to the next argument - } - if (ArgExprsOk && Tok.is(tok::r_paren)) { - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size()); - } - } - } else { // not an identifier - switch (Tok.getKind()) { - case tok::r_paren: - // parse a possibly empty comma separated list of expressions - // __attribute__(( nonnull() )) - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0); - break; - case tok::kw_char: - case tok::kw_wchar_t: - case tok::kw_char16_t: - case tok::kw_char32_t: - case tok::kw_bool: - case tok::kw_short: - case tok::kw_int: - case tok::kw_long: - case tok::kw___int64: - case tok::kw_signed: - case tok::kw_unsigned: - case tok::kw_float: - case tok::kw_double: - case tok::kw_void: - case tok::kw_typeof: { - AttributeList *attr - = attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0); - if (attr->getKind() == AttributeList::AT_IBOutletCollection) - Diag(Tok, diag::err_iboutletcollection_builtintype); - // If it's a builtin type name, eat it and expect a rparen - // __attribute__(( vec_type_hint(char) )) - ConsumeToken(); - if (Tok.is(tok::r_paren)) - ConsumeParen(); - break; - } - default: - // __attribute__(( aligned(16) )) - ExprVector ArgExprs(Actions); - bool ArgExprsOk = true; - - // now parse the list of expressions - while (1) { - ExprResult ArgExpr(ParseAssignmentExpression()); - if (ArgExpr.isInvalid()) { - ArgExprsOk = false; - SkipUntil(tok::r_paren); - break; - } else { - ArgExprs.push_back(ArgExpr.release()); - } - if (Tok.isNot(tok::comma)) - break; - ConsumeToken(); // Eat the comma, move to the next argument - } - // Match the ')'. - if (ArgExprsOk && Tok.is(tok::r_paren)) { - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, - AttrNameLoc, 0, SourceLocation(), - ArgExprs.take(), ArgExprs.size()); - } - break; - } + if (Tok.is(tok::l_paren)) { + // handle "parameterized" attributes + if (LateAttrs && !ClassStack.empty() && + isAttributeLateParsed(*AttrName)) { + // Delayed parsing is only available for attributes that occur + // in certain locations within a class scope. + LateParsedAttribute *LA = + new LateParsedAttribute(this, *AttrName, AttrNameLoc); + LateAttrs->push_back(LA); + getCurrentClass().LateParsedDeclarations.push_back(LA); + + // consume everything up to and including the matching right parens + ConsumeAndStoreUntil(tok::r_paren, LA->Toks, true, false); + + Token Eof; + Eof.startToken(); + Eof.setLocation(Tok.getLocation()); + LA->Toks.push_back(Eof); + } else { + ParseGNUAttributeArgs(AttrName, AttrNameLoc, attrs, endLoc); } } else { attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, @@ -246,6 +166,132 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, } } + +/// Parse the arguments to a parameterized GNU attribute +void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, + SourceLocation *EndLoc) { + + assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); + + // Availability attributes have their own grammar. + if (AttrName->isStr("availability")) { + ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc); + return; + } + // Thread safety attributes fit into the FIXME case above, so we + // just parse the arguments as a list of expressions + if (IsThreadSafetyAttribute(AttrName->getName())) { + ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc); + return; + } + + ConsumeParen(); // ignore the left paren loc for now + + if (Tok.is(tok::identifier)) { + IdentifierInfo *ParmName = Tok.getIdentifierInfo(); + SourceLocation ParmLoc = ConsumeToken(); + + if (Tok.is(tok::r_paren)) { + // __attribute__(( mode(byte) )) + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + ParmName, ParmLoc, 0, 0); + } else if (Tok.is(tok::comma)) { + ConsumeToken(); + // __attribute__(( format(printf, 1, 2) )) + ExprVector ArgExprs(Actions); + bool ArgExprsOk = true; + + // now parse the non-empty comma separated list of expressions + while (1) { + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + ArgExprsOk = false; + SkipUntil(tok::r_paren); + break; + } else { + ArgExprs.push_back(ArgExpr.release()); + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + if (ArgExprsOk && Tok.is(tok::r_paren)) { + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size()); + } + } + } else { // not an identifier + switch (Tok.getKind()) { + case tok::r_paren: + // parse a possibly empty comma separated list of expressions + // __attribute__(( nonnull() )) + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + 0, SourceLocation(), 0, 0); + break; + case tok::kw_char: + case tok::kw_wchar_t: + case tok::kw_char16_t: + case tok::kw_char32_t: + case tok::kw_bool: + case tok::kw_short: + case tok::kw_int: + case tok::kw_long: + case tok::kw___int64: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw_float: + case tok::kw_double: + case tok::kw_void: + case tok::kw_typeof: { + AttributeList *attr + = Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + 0, SourceLocation(), 0, 0); + if (attr->getKind() == AttributeList::AT_IBOutletCollection) + Diag(Tok, diag::err_iboutletcollection_builtintype); + // If it's a builtin type name, eat it and expect a rparen + // __attribute__(( vec_type_hint(char) )) + ConsumeToken(); + if (Tok.is(tok::r_paren)) + ConsumeParen(); + break; + } + default: + // __attribute__(( aligned(16) )) + ExprVector ArgExprs(Actions); + bool ArgExprsOk = true; + + // now parse the list of expressions + while (1) { + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + ArgExprsOk = false; + SkipUntil(tok::r_paren); + break; + } else { + ArgExprs.push_back(ArgExpr.release()); + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + // Match the ')'. + if (ArgExprsOk && Tok.is(tok::r_paren)) { + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, + AttrNameLoc, 0, SourceLocation(), + ArgExprs.take(), ArgExprs.size()); + } + break; + } + } +} + + /// ParseMicrosoftDeclSpec - Parse an __declspec construct /// /// [MS] decl-specifier: @@ -658,6 +704,80 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, UnavailableLoc, false, false); } + +// Late Parsed Attributes: +// See other examples of late parsing in lib/Parse/ParseCXXInlineMethods + +void Parser::LateParsedDeclaration::ParseLexedAttributes() {} + +void Parser::LateParsedClass::ParseLexedAttributes() { + Self->ParseLexedAttributes(*Class); +} + +void Parser::LateParsedAttribute::ParseLexedAttributes() { + Self->ParseLexedAttribute(*this); +} + +/// Wrapper class which calls ParseLexedAttribute, after setting up the +/// scope appropriately. +void Parser::ParseLexedAttributes(ParsingClass &Class) { + // Deal with templates + // FIXME: Test cases to make sure this does the right thing for templates. + bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; + ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, + HasTemplateScope); + if (HasTemplateScope) + Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); + + // Set or update the scope flags to include Scope::ThisScope. + bool AlreadyHasClassScope = Class.TopLevelClass; + unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope|Scope::ThisScope; + ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope); + ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope); + + for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i) { + Class.LateParsedDeclarations[i]->ParseLexedAttributes(); + } +} + +/// \brief Finish parsing an attribute for which parsing was delayed. +/// This will be called at the end of parsing a class declaration +/// for each LateParsedAttribute. We consume the saved tokens and +/// create an attribute with the arguments filled in. We add this +/// to the Attribute list for the decl. +void Parser::ParseLexedAttribute(LateParsedAttribute &LA) { + // Save the current token position. + SourceLocation OrigLoc = Tok.getLocation(); + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + LA.Toks.push_back(Tok); + PP.EnterTokenStream(LA.Toks.data(), LA.Toks.size(), true, false); + // Consume the previously pushed token. + ConsumeAnyToken(); + + ParsedAttributes Attrs(AttrFactory); + SourceLocation endLoc; + + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc); + + // Late parsed attributes must be attached to Decls by hand. If the + // LA.D is not set, then this was not done properly. + assert(LA.D && "No decl attached to late parsed attribute"); + Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.D, Attrs); + + if (Tok.getLocation() != OrigLoc) { + // Due to a parsing error, we either went over the cached tokens or + // there are still cached tokens left, so we skip the leftover tokens. + // Since this is an uncommon situation that should be avoided, use the + // expensive isBeforeInTranslationUnit call. + if (PP.getSourceManager().isBeforeInTranslationUnit(Tok.getLocation(), + OrigLoc)) + while (Tok.getLocation() != OrigLoc && Tok.isNot(tok::eof)) + ConsumeAnyToken(); + } +} + /// \brief Wrapper around a case statement checking if AttrName is /// one of the thread safety attributes bool Parser::IsThreadSafetyAttribute(llvm::StringRef AttrName){ @@ -699,37 +819,35 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc) { + assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); - if (Tok.is(tok::l_paren)) { - SourceLocation LeftParenLoc = Tok.getLocation(); - ConsumeParen(); // ignore the left paren loc for now - - ExprVector ArgExprs(Actions); - bool ArgExprsOk = true; - - // now parse the list of expressions - while (1) { - ExprResult ArgExpr(ParseAssignmentExpression()); - if (ArgExpr.isInvalid()) { - ArgExprsOk = false; - MatchRHSPunctuation(tok::r_paren, LeftParenLoc); - break; - } else { - ArgExprs.push_back(ArgExpr.release()); - } - if (Tok.isNot(tok::comma)) - break; - ConsumeToken(); // Eat the comma, move to the next argument - } - // Match the ')'. - if (ArgExprsOk && Tok.is(tok::r_paren)) { - ConsumeParen(); // ignore the right paren loc for now - Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), - ArgExprs.take(), ArgExprs.size()); + SourceLocation LeftParenLoc = Tok.getLocation(); + ConsumeParen(); + + ExprVector ArgExprs(Actions); + bool ArgExprsOk = true; + + // now parse the list of expressions + while (1) { + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + ArgExprsOk = false; + MatchRHSPunctuation(tok::r_paren, LeftParenLoc); + break; + } else { + ArgExprs.push_back(ArgExpr.release()); } - } else { - Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0); + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + // Match the ')'. + if (ArgExprsOk && Tok.is(tok::r_paren)) { + if (EndLoc) + *EndLoc = Tok.getLocation(); + ConsumeParen(); + Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), + ArgExprs.take(), ArgExprs.size()); } } |