From 65d1a1961c4157e4b80bb09bade30c3245f4d2b9 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 31 Mar 2014 17:32:39 +0000 Subject: [PATCH] Introduced an attribute syntax-neutral method for parsing attribute arguments that is currently being used by GNU and C++-style attributes. This allows C++11 attributes with argument lists to be handled properly, fixing the "deprecated", "type_visibility", and capability-related attributes with arguments. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@205226 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Parser.h | 17 +++++ lib/Parse/ParseDecl.cpp | 113 +++++++++++++++++-------------- lib/Parse/ParseDeclCXX.cpp | 80 ++++++++++++++-------- test/Parser/cxx0x-attributes.cpp | 17 ++--- 4 files changed, 139 insertions(+), 88 deletions(-) diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index b9b441f0a6..87a230f7e5 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1996,6 +1996,16 @@ private: /// locations where attributes are not allowed. void DiagnoseAndSkipCXX11Attributes(); + /// \brief Parses syntax-generic attribute arguments for attributes which are + /// known to the implementation, and adds them to the given ParsedAttributes + /// list with the given attribute syntax. + void ParseAttributeArgsCommon(IdentifierInfo *AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, SourceLocation *EndLoc, + IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, + AttributeList::Syntax Syntax); + void MaybeParseGNUAttributes(Declarator &D, LateParsedAttrList *LateAttrs = 0) { if (Tok.is(tok::kw___attribute)) { @@ -2053,6 +2063,13 @@ private: SourceLocation *EndLoc = 0); void ParseCXX11Attributes(ParsedAttributesWithRange &attrs, SourceLocation *EndLoc = 0); + /// \brief Parses a C++-style attribute argument list. Returns true if this + /// results in adding an attribute to the ParsedAttributes list. + bool ParseCXX11AttributeArgs(IdentifierInfo *AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, SourceLocation *EndLoc, + IdentifierInfo *ScopeName, + SourceLocation ScopeLoc); IdentifierInfo *TryParseCXX11AttributeIdentifier(SourceLocation &Loc); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 552a50218f..540bc8ef60 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -259,6 +259,65 @@ void Parser::ParseAttributeWithTypeArg(IdentifierInfo &AttrName, 0, AttrNameLoc, 0, 0, AttributeList::AS_GNU); } +void Parser::ParseAttributeArgsCommon( + IdentifierInfo *AttrName, SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, AttributeList::Syntax Syntax) { + // Ignore the left paren location for now. + ConsumeParen(); + + ArgsVector ArgExprs; + if (Tok.is(tok::identifier)) { + // If this attribute wants an 'identifier' argument, make it so. + bool IsIdentifierArg = attributeHasIdentifierArg(*AttrName); + AttributeList::Kind AttrKind = + AttributeList::getKind(AttrName, ScopeName, Syntax); + + // If we don't know how to parse this attribute, but this is the only + // token in this argument, assume it's meant to be an identifier. + if (AttrKind == AttributeList::UnknownAttribute || + AttrKind == AttributeList::IgnoredAttribute) { + const Token &Next = NextToken(); + IsIdentifierArg = Next.is(tok::r_paren) || Next.is(tok::comma); + } + + if (IsIdentifierArg) + ArgExprs.push_back(ParseIdentifierLoc()); + } + + if (!ArgExprs.empty() ? Tok.is(tok::comma) : Tok.isNot(tok::r_paren)) { + // Eat the comma. + if (!ArgExprs.empty()) + ConsumeToken(); + + // Parse the non-empty comma-separated list of expressions. + do { + std::unique_ptr Unevaluated; + if (attributeParsedArgsUnevaluated(*AttrName)) + Unevaluated.reset( + new EnterExpressionEvaluationContext(Actions, Sema::Unevaluated)); + + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + ArgExprs.push_back(ArgExpr.release()); + // Eat the comma, move to the next argument + } while (TryConsumeToken(tok::comma)); + } + + SourceLocation RParen = Tok.getLocation(); + if (!ExpectAndConsume(tok::r_paren)) { + SourceLocation AttrLoc = ScopeLoc.isValid() ? ScopeLoc : AttrNameLoc; + Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc, + ArgExprs.data(), ArgExprs.size(), Syntax); + } + + if (EndLoc) + *EndLoc = RParen; +} + /// Parse the arguments to a parameterized GNU attribute or /// a C++11 attribute in "gnu" namespace. void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, @@ -314,58 +373,8 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, } } - // Ignore the left paren location for now. - ConsumeParen(); - - ArgsVector ArgExprs; - - if (Tok.is(tok::identifier)) { - // If this attribute wants an 'identifier' argument, make it so. - bool IsIdentifierArg = attributeHasIdentifierArg(*AttrName); - - // If we don't know how to parse this attribute, but this is the only - // token in this argument, assume it's meant to be an identifier. - if (AttrKind == AttributeList::UnknownAttribute || - AttrKind == AttributeList::IgnoredAttribute) { - const Token &Next = NextToken(); - IsIdentifierArg = Next.is(tok::r_paren) || Next.is(tok::comma); - } - - if (IsIdentifierArg) - ArgExprs.push_back(ParseIdentifierLoc()); - } - - if (!ArgExprs.empty() ? Tok.is(tok::comma) : Tok.isNot(tok::r_paren)) { - // Eat the comma. - if (!ArgExprs.empty()) - ConsumeToken(); - - // Parse the non-empty comma-separated list of expressions. - do { - std::unique_ptr Unevaluated; - if (attributeParsedArgsUnevaluated(*AttrName)) - Unevaluated.reset(new EnterExpressionEvaluationContext(Actions, - Sema::Unevaluated)); - - ExprResult ArgExpr(ParseAssignmentExpression()); - if (ArgExpr.isInvalid()) { - SkipUntil(tok::r_paren, StopAtSemi); - return; - } - ArgExprs.push_back(ArgExpr.release()); - // Eat the comma, move to the next argument - } while (TryConsumeToken(tok::comma)); - } - - SourceLocation RParen = Tok.getLocation(); - if (!ExpectAndConsume(tok::r_paren)) { - SourceLocation AttrLoc = ScopeLoc.isValid() ? ScopeLoc : AttrNameLoc; - Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc, - ArgExprs.data(), ArgExprs.size(), Syntax); - } - - if (EndLoc) - *EndLoc = RParen; + ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, + ScopeLoc, Syntax); } /// \brief Parses a single argument for a declspec, including the diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index c7c8625f2c..c84bdcbf35 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -15,7 +15,9 @@ #include "RAIIObjectsForParser.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" +#include "clang/Basic/Attributes.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Sema/DeclSpec.h" @@ -3197,8 +3199,50 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName, } } -/// ParseCXX11AttributeSpecifier - Parse a C++11 attribute-specifier. Currently -/// only parses standard attributes. +/// ParseCXX11AttributeArgs -- Parse a C++11 attribute-argument-clause. +/// +/// [C++11] attribute-argument-clause: +/// '(' balanced-token-seq ')' +/// +/// [C++11] balanced-token-seq: +/// balanced-token +/// balanced-token-seq balanced-token +/// +/// [C++11] balanced-token: +/// '(' balanced-token-seq ')' +/// '[' balanced-token-seq ']' +/// '{' balanced-token-seq '}' +/// any token but '(', ')', '[', ']', '{', or '}' +bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, + SourceLocation *EndLoc, + IdentifierInfo *ScopeName, + SourceLocation ScopeLoc) { + assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list"); + + // If the attribute isn't known, we will not attempt to parse any + // arguments. + if (!hasAttribute(AttrSyntax::CXX, ScopeName, AttrName, + getTargetInfo().getTriple(), getLangOpts())) { + // Eat the left paren, then skip to the ending right paren. + ConsumeParen(); + SkipUntil(tok::r_paren); + return false; + } + + if (ScopeName && ScopeName->getName() == "gnu") + // GNU-scoped attributes have some special cases to handle GNU-specific + // behaviors. + ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, + ScopeLoc, AttributeList::AS_CXX11, 0); + else + ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, + ScopeLoc, AttributeList::AS_CXX11); + return true; +} + +/// ParseCXX11AttributeSpecifier - Parse a C++11 attribute-specifier. /// /// [C++11] attribute-specifier: /// '[' '[' attribute-list ']' ']' @@ -3222,19 +3266,6 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName, /// /// [C++11] attribute-namespace: /// identifier -/// -/// [C++11] attribute-argument-clause: -/// '(' balanced-token-seq ')' -/// -/// [C++11] balanced-token-seq: -/// balanced-token -/// balanced-token-seq balanced-token -/// -/// [C++11] balanced-token: -/// '(' balanced-token-seq ')' -/// '[' balanced-token-seq ']' -/// '{' balanced-token-seq '}' -/// any token but '(', ')', '[', ']', '{', or '}' void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, SourceLocation *endLoc) { if (Tok.is(tok::kw_alignas)) { @@ -3279,29 +3310,22 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, } } - bool StandardAttr = IsBuiltInOrStandardCXX11Attribute(AttrName,ScopeName); + bool StandardAttr = IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName); bool AttrParsed = false; if (StandardAttr && !SeenAttrs.insert(std::make_pair(AttrName, AttrLoc)).second) Diag(AttrLoc, diag::err_cxx11_attribute_repeated) - << AttrName << SourceRange(SeenAttrs[AttrName]); + << AttrName << SourceRange(SeenAttrs[AttrName]); // Parse attribute arguments if (Tok.is(tok::l_paren)) { - if (ScopeName && ScopeName->getName() == "gnu") { - ParseGNUAttributeArgs(AttrName, AttrLoc, attrs, endLoc, - ScopeName, ScopeLoc, AttributeList::AS_CXX11, 0); - AttrParsed = true; - } else { - if (StandardAttr) - Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments) + if (StandardAttr) + Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments) << AttrName->getName(); - // FIXME: handle other formats of c++11 attribute arguments - ConsumeParen(); - SkipUntil(tok::r_paren); - } + AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, attrs, endLoc, + ScopeName, ScopeLoc); } if (!AttrParsed) diff --git a/test/Parser/cxx0x-attributes.cpp b/test/Parser/cxx0x-attributes.cpp index 5d3b825069..1b9e477004 100644 --- a/test/Parser/cxx0x-attributes.cpp +++ b/test/Parser/cxx0x-attributes.cpp @@ -48,13 +48,13 @@ int array_attr [1] [[]]; alignas(8) int aligned_attr; [[test::valid(for 42 [very] **** '+' symbols went on a trip and had a "good"_time; the end.)]] int garbage_attr; // expected-warning {{unknown attribute 'valid' ignored}} [[,,,static, class, namespace,, inline, constexpr, mutable,, bitand, bitor::compl(!.*_ Cx.!U^*R),,,]] int more_garbage_attr; // expected-warning {{unknown attribute 'static' ignored}} \ - // expected-warning {{unknown attribute 'class' ignored}} \ - // expected-warning {{unknown attribute 'namespace' ignored}} \ - // expected-warning {{unknown attribute 'inline' ignored}} \ - // expected-warning {{unknown attribute 'constexpr' ignored}} \ - // expected-warning {{unknown attribute 'mutable' ignored}} \ - // expected-warning {{unknown attribute 'bitand' ignored}} \ - // expected-warning {{unknown attribute 'compl' ignored}} + // expected-warning {{unknown attribute 'class' ignored}} \ + // expected-warning {{unknown attribute 'namespace' ignored}} \ + // expected-warning {{unknown attribute 'inline' ignored}} \ + // expected-warning {{unknown attribute 'constexpr' ignored}} \ + // expected-warning {{unknown attribute 'mutable' ignored}} \ + // expected-warning {{unknown attribute 'bitand' ignored}} \ + // expected-warning {{unknown attribute 'compl' ignored}} [[u8"invalid!"]] int invalid_string_attr; // expected-error {{expected ']'}} void fn_attr () [[]]; void noexcept_fn_attr () noexcept [[]]; @@ -281,7 +281,8 @@ enum class [[]] EvenMoreSecrets {}; namespace arguments { void f[[gnu::format(printf, 1, 2)]](const char*, ...); - void g() [[unknown::foo(arguments of attributes from unknown namespace other than 'gnu' namespace are ignored... blah...)]]; // expected-warning {{unknown attribute 'foo' ignored}} + void g() [[unknown::foo(ignore arguments for unknown attributes, even with symbols!)]]; // expected-warning {{unknown attribute 'foo' ignored}} + [[deprecated("with argument")]] int i; } // Forbid attributes on decl specifiers. -- 2.40.0