diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -26,6 +26,7 @@ #include "llvm/Frontend/OpenMP/OMPContext.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Registry.h" #include "llvm/Support/SaveAndRestore.h" #include #include @@ -2658,7 +2659,7 @@ if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrs(AttrFactory); SourceLocation endLoc; - ParseCXX11Attributes(attrs, &endLoc); + ParseCXX11Attributes(attrs, &endLoc, &D); D.takeAttributes(attrs, endLoc); } } @@ -2681,16 +2682,19 @@ } void ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, - SourceLocation *EndLoc = nullptr); + SourceLocation *EndLoc = nullptr, + Declarator *D = nullptr); void ParseCXX11Attributes(ParsedAttributesWithRange &attrs, - SourceLocation *EndLoc = nullptr); + SourceLocation *EndLoc = nullptr, + Declarator *D = nullptr); /// Parses a C++11 (or C2x)-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); + SourceLocation ScopeLoc, + Declarator *D = nullptr); IdentifierInfo *TryParseCXX11AttributeIdentifier(SourceLocation &Loc); diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -17,6 +17,7 @@ #include "clang/Basic/AttrSubjectMatchRules.h" #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" #include "clang/Sema/Ownership.h" #include "llvm/ADT/PointerUnion.h" @@ -34,14 +35,18 @@ class ASTContext; class Decl; +class Declarator; class Expr; class IdentifierInfo; class LangOptions; class ParsedAttr; +class ParsedAttributes; +class Parser; class Sema; class TargetInfo; -struct ParsedAttrInfo { +class ParsedAttrInfo { +public: /// Corresponds to the Kind enum. unsigned AttrKind : 16; /// The number of required arguments of this attribute. @@ -75,6 +80,27 @@ virtual ~ParsedAttrInfo() = default; + enum AttrHandling { NotHandled, AttributeApplied, AttributeNotApplied }; + + /// Override the default parsing logic and perform parsing of the attribute + /// payload here. This is only meaningful for plugins that register new + /// attributes. Flags such as HasCustomParsing, NumArgs, OptArgs + /// are not honored, and the plugin has to do all of the validation by hand. + /// Plugin receives an possibly-null Declarator D that provides, if available, + /// information on the in-progress Decl that the attribute is attached to. + /// + /// NotHandled defers to default parsing (accept expression + /// arguments for GNU syntax, ignore arguments for CXX11 syntax); + /// AttributeNotApplied indicates a parsing failure and AttributeApplied a + /// success. + virtual AttrHandling + parseAttributePayload(Parser *P, ParsedAttributes &Attrs, Declarator *D, + IdentifierInfo *AttrName, SourceLocation AttrNameLoc, + SourceLocation *EndLoc, IdentifierInfo *ScopeName, + SourceLocation ScopeLoc) const { + return NotHandled; + } + /// Check if this attribute appertains to D, and issue a diagnostic if not. virtual bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr, const Decl *D) const { @@ -99,11 +125,6 @@ llvm::SmallVectorImpl> &Rules, const LangOptions &LangOpts) const { } - enum AttrHandling { - NotHandled, - AttributeApplied, - AttributeNotApplied - }; /// If this ParsedAttrInfo knows how to handle this ParsedAttr applied to this /// Decl then do so and return either AttributeApplied if it was applied or /// AttributeNotApplied if it wasn't. Otherwise return NotHandled. @@ -112,6 +133,14 @@ return NotHandled; } + /// Search amongst registered plugins for a ParsedAttrInfo whose name and + /// syntax match, and return a default value otherwise. + static const ParsedAttrInfo &get(std::string FullName, + AttributeCommonInfo::Syntax SyntaxUsed); + + /// Once parsing has been performed, an AttributeCommonInfo (e.g. ParsedAttr) + /// most likely has a ParsedAttrInfo attached to it. Perform ParsedAttrInfo + /// resolution via A, falling back on get above otherwise. static const ParsedAttrInfo &get(const AttributeCommonInfo &A); }; diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -4041,18 +4041,36 @@ ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, - SourceLocation ScopeLoc) { + SourceLocation ScopeLoc, Declarator *D) { assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list"); SourceLocation LParenLoc = Tok.getLocation(); const LangOptions &LO = getLangOpts(); ParsedAttr::Syntax Syntax = LO.CPlusPlus ? ParsedAttr::AS_CXX11 : ParsedAttr::AS_C2x; - // If the attribute isn't known, we will not attempt to parse any - // arguments. if (!hasAttribute(LO.CPlusPlus ? AttrSyntax::CXX : AttrSyntax::C, ScopeName, AttrName, getTargetInfo(), getLangOpts())) { - // Eat the left paren, then skip to the ending right paren. + std::string FullName; + FullName += ScopeName->getNameStart(); + FullName += "::"; + FullName += AttrName->getNameStart(); + const ParsedAttrInfo &PluginInfo = ParsedAttrInfo::get(FullName, Syntax); + if (PluginInfo.AttrKind != AttributeCommonInfo::UnknownAttribute) { + // A plugin exists for this attribute. See if the plugin wants to opt into + // custom parsing. + ParsedAttrInfo::AttrHandling H = PluginInfo.parseAttributePayload( + this, Attrs, D, AttrName, AttrNameLoc, EndLoc, ScopeName, ScopeLoc); + if (H == ParsedAttrInfo::AttributeApplied) + return true; + else if (H == ParsedAttrInfo::AttributeNotApplied) + return false; + else + ; // fall-through + } + + // Unknown attribute, or plugin declined to handle it. We do not attempt to + // parse its arguments. Eat the left paren, then skip to the ending right + // paren. ConsumeParen(); SkipUntil(tok::r_paren); return false; @@ -4126,7 +4144,8 @@ /// [C++11] attribute-namespace: /// identifier void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, - SourceLocation *endLoc) { + SourceLocation *endLoc, + Declarator *D) { if (Tok.is(tok::kw_alignas)) { Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas); ParseAlignmentSpecifier(attrs, endLoc); @@ -4207,7 +4226,7 @@ // Parse attribute arguments if (Tok.is(tok::l_paren)) AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, attrs, endLoc, - ScopeName, ScopeLoc); + ScopeName, ScopeLoc, D); if (!AttrParsed) attrs.addNew( @@ -4234,7 +4253,7 @@ /// attribute-specifier-seq: /// attribute-specifier-seq[opt] attribute-specifier void Parser::ParseCXX11Attributes(ParsedAttributesWithRange &attrs, - SourceLocation *endLoc) { + SourceLocation *endLoc, Declarator *D) { assert(standardAttributesAllowed()); SourceLocation StartLoc = Tok.getLocation(), Loc; @@ -4242,7 +4261,7 @@ endLoc = &Loc; do { - ParseCXX11AttributeSpecifier(attrs, endLoc); + ParseCXX11AttributeSpecifier(attrs, endLoc, D); } while (isCXX11AttributeSpecifier()); attrs.Range = SourceRange(StartLoc, *endLoc); diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp --- a/clang/lib/Sema/ParsedAttr.cpp +++ b/clang/lib/Sema/ParsedAttr.cpp @@ -109,18 +109,10 @@ } // namespace -const ParsedAttrInfo &ParsedAttrInfo::get(const AttributeCommonInfo &A) { - // If we have a ParsedAttrInfo for this ParsedAttr then return that. - if ((size_t)A.getParsedKind() < llvm::array_lengthof(AttrInfoMap)) - return *AttrInfoMap[A.getParsedKind()]; - - // If this is an ignored attribute then return an appropriate ParsedAttrInfo. - static const ParsedAttrInfo IgnoredParsedAttrInfo( - AttributeCommonInfo::IgnoredAttribute); - if (A.getParsedKind() == AttributeCommonInfo::IgnoredAttribute) - return IgnoredParsedAttrInfo; - - // Otherwise this may be an attribute defined by a plugin. First instantiate +const ParsedAttrInfo & +ParsedAttrInfo::get(std::string FullName, + AttributeCommonInfo::Syntax SyntaxUsed) { + // This may be an attribute defined by a plugin. First instantiate // all plugin attributes if we haven't already done so. static llvm::ManagedStatic>> PluginAttrInstances; @@ -128,14 +120,11 @@ for (auto It : ParsedAttrInfoRegistry::entries()) PluginAttrInstances->emplace_back(It.instantiate()); - // Search for a ParsedAttrInfo whose name and syntax match. - std::string FullName = A.getNormalizedFullName(); - AttributeCommonInfo::Syntax SyntaxUsed = A.getSyntax(); if (SyntaxUsed == AttributeCommonInfo::AS_ContextSensitiveKeyword) SyntaxUsed = AttributeCommonInfo::AS_Keyword; for (auto &Ptr : *PluginAttrInstances) - for (auto &S : Ptr->Spellings) + for (const auto &S : Ptr->Spellings) if (S.Syntax == SyntaxUsed && S.NormalizedFullName == FullName) return *Ptr; @@ -145,6 +134,21 @@ return DefaultParsedAttrInfo; } +const ParsedAttrInfo &ParsedAttrInfo::get(const AttributeCommonInfo &A) { + // If we have a ParsedAttrInfo for this ParsedAttr then return that. + if ((size_t)A.getParsedKind() < llvm::array_lengthof(AttrInfoMap)) + return *AttrInfoMap[A.getParsedKind()]; + + // If this is an ignored attribute then return an appropriate ParsedAttrInfo. + static const ParsedAttrInfo IgnoredParsedAttrInfo( + AttributeCommonInfo::IgnoredAttribute); + if (A.getParsedKind() == AttributeCommonInfo::IgnoredAttribute) + return IgnoredParsedAttrInfo; + + // Search for a ParsedAttrInfo whose name and syntax match. + return get(A.getNormalizedFullName(), A.getSyntax()); +} + unsigned ParsedAttr::getMinArgs() const { return getInfo().NumArgs; } unsigned ParsedAttr::getMaxArgs() const {