forked from OSchip/llvm-project
Add an attribute registry so plugins can add attributes
When constructing a ParsedAttr the ParsedAttrInfo gets looked up in the AttrInfoMap, which is auto-generated using tablegen. If that lookup fails then we look through the ParsedAttrInfos that plugins have added to the registry and check if any has a spelling that matches. Differential Revision: https://reviews.llvm.org/D31338
This commit is contained in:
parent
ddd11273d9
commit
75d4d4bd02
|
@ -134,6 +134,11 @@ public:
|
|||
const IdentifierInfo *getScopeName() const { return ScopeName; }
|
||||
SourceLocation getScopeLoc() const { return ScopeLoc; }
|
||||
|
||||
/// Gets the normalized full name, which consists of both scope and name and
|
||||
/// with surrounding underscores removed as appropriate (e.g.
|
||||
/// __gnu__::__attr__ will be normalized to gnu::attr).
|
||||
std::string getNormalizedFullName() const;
|
||||
|
||||
bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; }
|
||||
bool isMicrosoftAttribute() const { return SyntaxUsed == AS_Microsoft; }
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Registry.h"
|
||||
#include "llvm/Support/VersionTuple.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
@ -37,6 +38,72 @@ class Decl;
|
|||
class Expr;
|
||||
class IdentifierInfo;
|
||||
class LangOptions;
|
||||
class ParsedAttr;
|
||||
class Sema;
|
||||
|
||||
struct ParsedAttrInfo {
|
||||
/// Corresponds to the Kind enum.
|
||||
unsigned AttrKind : 16;
|
||||
/// The number of required arguments of this attribute.
|
||||
unsigned NumArgs : 4;
|
||||
/// The number of optional arguments of this attributes.
|
||||
unsigned OptArgs : 4;
|
||||
/// True if the parsing does not match the semantic content.
|
||||
unsigned HasCustomParsing : 1;
|
||||
/// True if this attribute is only available for certain targets.
|
||||
unsigned IsTargetSpecific : 1;
|
||||
/// True if this attribute applies to types.
|
||||
unsigned IsType : 1;
|
||||
/// True if this attribute applies to statements.
|
||||
unsigned IsStmt : 1;
|
||||
/// True if this attribute has any spellings that are known to gcc.
|
||||
unsigned IsKnownToGCC : 1;
|
||||
/// True if this attribute is supported by #pragma clang attribute.
|
||||
unsigned IsSupportedByPragmaAttribute : 1;
|
||||
/// The syntaxes supported by this attribute and how they're spelled.
|
||||
struct Spelling {
|
||||
AttributeCommonInfo::Syntax Syntax;
|
||||
std::string NormalizedFullName;
|
||||
};
|
||||
std::vector<Spelling> Spellings;
|
||||
|
||||
ParsedAttrInfo(AttributeCommonInfo::Kind AttrKind =
|
||||
AttributeCommonInfo::UnknownAttribute)
|
||||
: AttrKind(AttrKind), NumArgs(0), OptArgs(0), HasCustomParsing(0),
|
||||
IsTargetSpecific(0), IsType(0), IsStmt(0), IsKnownToGCC(0),
|
||||
IsSupportedByPragmaAttribute(0) {}
|
||||
|
||||
virtual ~ParsedAttrInfo() = default;
|
||||
|
||||
/// 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 {
|
||||
return true;
|
||||
}
|
||||
/// Check if this attribute is allowed by the language we are compiling, and
|
||||
/// issue a diagnostic if not.
|
||||
virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const {
|
||||
return true;
|
||||
}
|
||||
/// Check if this attribute is allowed when compiling for the given target.
|
||||
virtual bool existsInTarget(const TargetInfo &Target) const {
|
||||
return true;
|
||||
}
|
||||
/// Convert the spelling index of Attr to a semantic spelling enum value.
|
||||
virtual unsigned
|
||||
spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const {
|
||||
return UINT_MAX;
|
||||
}
|
||||
/// Populate Rules with the match rules of this attribute.
|
||||
virtual void getPragmaAttributeMatchRules(
|
||||
llvm::SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>> &Rules,
|
||||
const LangOptions &LangOpts) const {
|
||||
}
|
||||
|
||||
static const ParsedAttrInfo &get(const AttributeCommonInfo &A);
|
||||
};
|
||||
|
||||
typedef llvm::Registry<ParsedAttrInfo> ParsedAttrInfoRegistry;
|
||||
|
||||
/// Represents information about a change in availability for
|
||||
/// an entity, which is part of the encoding of the 'availability'
|
||||
|
@ -181,6 +248,8 @@ private:
|
|||
|
||||
const Expr *MessageExpr;
|
||||
|
||||
const ParsedAttrInfo &Info;
|
||||
|
||||
ArgsUnion *getArgsBuffer() { return getTrailingObjects<ArgsUnion>(); }
|
||||
ArgsUnion const *getArgsBuffer() const {
|
||||
return getTrailingObjects<ArgsUnion>();
|
||||
|
@ -207,7 +276,8 @@ private:
|
|||
EllipsisLoc(ellipsisLoc), NumArgs(numArgs), Invalid(false),
|
||||
UsedAsTypeAttr(false), IsAvailability(false),
|
||||
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
|
||||
HasProcessingCache(false), IsPragmaClangAttribute(false) {
|
||||
HasProcessingCache(false), IsPragmaClangAttribute(false),
|
||||
Info(ParsedAttrInfo::get(*this)) {
|
||||
if (numArgs)
|
||||
memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion));
|
||||
}
|
||||
|
@ -225,7 +295,8 @@ private:
|
|||
NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
|
||||
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
|
||||
HasProcessingCache(false), IsPragmaClangAttribute(false),
|
||||
UnavailableLoc(unavailable), MessageExpr(messageExpr) {
|
||||
UnavailableLoc(unavailable), MessageExpr(messageExpr),
|
||||
Info(ParsedAttrInfo::get(*this)) {
|
||||
ArgsUnion PVal(Parm);
|
||||
memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
|
||||
new (getAvailabilityData()) detail::AvailabilityData(
|
||||
|
@ -242,7 +313,7 @@ private:
|
|||
NumArgs(3), Invalid(false), UsedAsTypeAttr(false),
|
||||
IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
|
||||
HasParsedType(false), HasProcessingCache(false),
|
||||
IsPragmaClangAttribute(false) {
|
||||
IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
|
||||
ArgsUnion *Args = getArgsBuffer();
|
||||
Args[0] = Parm1;
|
||||
Args[1] = Parm2;
|
||||
|
@ -259,7 +330,7 @@ private:
|
|||
NumArgs(1), Invalid(false), UsedAsTypeAttr(false),
|
||||
IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false),
|
||||
HasParsedType(false), HasProcessingCache(false),
|
||||
IsPragmaClangAttribute(false) {
|
||||
IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
|
||||
ArgsUnion PVal(ArgKind);
|
||||
memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
|
||||
detail::TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot();
|
||||
|
@ -277,7 +348,7 @@ private:
|
|||
NumArgs(0), Invalid(false), UsedAsTypeAttr(false),
|
||||
IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
|
||||
HasParsedType(true), HasProcessingCache(false),
|
||||
IsPragmaClangAttribute(false) {
|
||||
IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
|
||||
new (&getTypeBuffer()) ParsedType(typeArg);
|
||||
}
|
||||
|
||||
|
@ -291,7 +362,7 @@ private:
|
|||
NumArgs(0), Invalid(false), UsedAsTypeAttr(false),
|
||||
IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true),
|
||||
HasParsedType(false), HasProcessingCache(false),
|
||||
IsPragmaClangAttribute(false) {
|
||||
IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
|
||||
new (&getPropertyDataBuffer()) detail::PropertyData(getterId, setterId);
|
||||
}
|
||||
|
||||
|
@ -534,7 +605,10 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
AttributeCommonInfo::Kind getKind() const { return getParsedKind(); }
|
||||
AttributeCommonInfo::Kind getKind() const {
|
||||
return AttributeCommonInfo::Kind(Info.AttrKind);
|
||||
}
|
||||
const ParsedAttrInfo &getInfo() const { return Info; }
|
||||
};
|
||||
|
||||
class AttributePool;
|
||||
|
|
|
@ -36,10 +36,14 @@ const char *attr::getSubjectMatchRuleSpelling(attr::SubjectMatchRule Rule) {
|
|||
}
|
||||
|
||||
static StringRef
|
||||
normalizeAttrScopeName(StringRef ScopeName,
|
||||
normalizeAttrScopeName(const IdentifierInfo *Scope,
|
||||
AttributeCommonInfo::Syntax SyntaxUsed) {
|
||||
if (!Scope)
|
||||
return "";
|
||||
|
||||
// Normalize the "__gnu__" scope name to be "gnu" and the "_Clang" scope name
|
||||
// to be "clang".
|
||||
StringRef ScopeName = Scope->getName();
|
||||
if (SyntaxUsed == AttributeCommonInfo::AS_CXX11 ||
|
||||
SyntaxUsed == AttributeCommonInfo::AS_C2x) {
|
||||
if (ScopeName == "__gnu__")
|
||||
|
@ -50,7 +54,7 @@ normalizeAttrScopeName(StringRef ScopeName,
|
|||
return ScopeName;
|
||||
}
|
||||
|
||||
static StringRef normalizeAttrName(StringRef AttrName,
|
||||
static StringRef normalizeAttrName(const IdentifierInfo *Name,
|
||||
StringRef NormalizedScopeName,
|
||||
AttributeCommonInfo::Syntax SyntaxUsed) {
|
||||
// Normalize the attribute name, __foo__ becomes foo. This is only allowable
|
||||
|
@ -61,6 +65,7 @@ static StringRef normalizeAttrName(StringRef AttrName,
|
|||
SyntaxUsed == AttributeCommonInfo::AS_C2x) &&
|
||||
(NormalizedScopeName.empty() || NormalizedScopeName == "gnu" ||
|
||||
NormalizedScopeName == "clang"));
|
||||
StringRef AttrName = Name->getName();
|
||||
if (ShouldNormalize && AttrName.size() >= 4 && AttrName.startswith("__") &&
|
||||
AttrName.endswith("__"))
|
||||
AttrName = AttrName.slice(2, AttrName.size() - 2);
|
||||
|
@ -74,35 +79,41 @@ bool AttributeCommonInfo::isGNUScope() const {
|
|||
|
||||
#include "clang/Sema/AttrParsedAttrKinds.inc"
|
||||
|
||||
static SmallString<64> normalizeName(const IdentifierInfo *Name,
|
||||
const IdentifierInfo *Scope,
|
||||
AttributeCommonInfo::Syntax SyntaxUsed) {
|
||||
StringRef ScopeName = normalizeAttrScopeName(Scope, SyntaxUsed);
|
||||
StringRef AttrName = normalizeAttrName(Name, ScopeName, SyntaxUsed);
|
||||
|
||||
// Ensure that in the case of C++11 attributes, we look for '::foo' if it is
|
||||
// unscoped.
|
||||
SmallString<64> FullName = ScopeName;
|
||||
if (Scope || SyntaxUsed == AttributeCommonInfo::AS_CXX11 ||
|
||||
SyntaxUsed == AttributeCommonInfo::AS_C2x)
|
||||
FullName += "::";
|
||||
FullName += AttrName;
|
||||
|
||||
return FullName;
|
||||
}
|
||||
|
||||
AttributeCommonInfo::Kind
|
||||
AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name,
|
||||
const IdentifierInfo *ScopeName,
|
||||
Syntax SyntaxUsed) {
|
||||
StringRef AttrName = Name->getName();
|
||||
return ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed);
|
||||
}
|
||||
|
||||
SmallString<64> FullName;
|
||||
if (ScopeName)
|
||||
FullName += normalizeAttrScopeName(ScopeName->getName(), SyntaxUsed);
|
||||
|
||||
AttrName = normalizeAttrName(AttrName, FullName, SyntaxUsed);
|
||||
|
||||
// Ensure that in the case of C++11 attributes, we look for '::foo' if it is
|
||||
// unscoped.
|
||||
if (ScopeName || SyntaxUsed == AS_CXX11 || SyntaxUsed == AS_C2x)
|
||||
FullName += "::";
|
||||
FullName += AttrName;
|
||||
|
||||
return ::getAttrKind(FullName, SyntaxUsed);
|
||||
std::string AttributeCommonInfo::getNormalizedFullName() const {
|
||||
return static_cast<std::string>(
|
||||
normalizeName(getAttrName(), getScopeName(), getSyntax()));
|
||||
}
|
||||
|
||||
unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const {
|
||||
// Both variables will be used in tablegen generated
|
||||
// attribute spell list index matching code.
|
||||
auto Syntax = static_cast<AttributeCommonInfo::Syntax>(getSyntax());
|
||||
StringRef Scope =
|
||||
getScopeName() ? normalizeAttrScopeName(getScopeName()->getName(), Syntax)
|
||||
: "";
|
||||
StringRef Name = normalizeAttrName(getAttrName()->getName(), Scope, Syntax);
|
||||
StringRef Scope = normalizeAttrScopeName(getScopeName(), Syntax);
|
||||
StringRef Name = normalizeAttrName(getAttrName(), Scope, Syntax);
|
||||
|
||||
#include "clang/Sema/AttrSpellingListIndex.inc"
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
using namespace clang;
|
||||
|
||||
LLVM_INSTANTIATE_REGISTRY(ParsedAttrInfoRegistry)
|
||||
|
||||
IdentifierLoc *IdentifierLoc::create(ASTContext &Ctx, SourceLocation Loc,
|
||||
IdentifierInfo *Ident) {
|
||||
IdentifierLoc *Result = new (Ctx) IdentifierLoc;
|
||||
|
@ -100,66 +102,58 @@ void AttributePool::takePool(AttributePool &pool) {
|
|||
pool.Attrs.clear();
|
||||
}
|
||||
|
||||
struct ParsedAttrInfo {
|
||||
unsigned NumArgs : 4;
|
||||
unsigned OptArgs : 4;
|
||||
unsigned HasCustomParsing : 1;
|
||||
unsigned IsTargetSpecific : 1;
|
||||
unsigned IsType : 1;
|
||||
unsigned IsStmt : 1;
|
||||
unsigned IsKnownToGCC : 1;
|
||||
unsigned IsSupportedByPragmaAttribute : 1;
|
||||
|
||||
virtual ~ParsedAttrInfo() = default;
|
||||
|
||||
virtual bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr,
|
||||
const Decl *) const {
|
||||
return true;
|
||||
}
|
||||
virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const {
|
||||
return true;
|
||||
}
|
||||
virtual bool existsInTarget(const TargetInfo &Target) const {
|
||||
return true;
|
||||
}
|
||||
virtual unsigned
|
||||
spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const {
|
||||
return UINT_MAX;
|
||||
}
|
||||
virtual void getPragmaAttributeMatchRules(
|
||||
llvm::SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>> &Rules,
|
||||
const LangOptions &LangOpts) const {
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
#include "clang/Sema/AttrParsedAttrImpl.inc"
|
||||
|
||||
} // namespace
|
||||
|
||||
static const ParsedAttrInfo &getInfo(const ParsedAttr &A) {
|
||||
// If we have a ParsedAttrInfo for this ParsedAttr then return that,
|
||||
// otherwise return a default ParsedAttrInfo.
|
||||
if (A.getKind() < llvm::array_lengthof(AttrInfoMap))
|
||||
return *AttrInfoMap[A.getKind()];
|
||||
const ParsedAttrInfo &ParsedAttrInfo::get(const AttributeCommonInfo &A) {
|
||||
// If we have a ParsedAttrInfo for this ParsedAttr then return that.
|
||||
if (A.getParsedKind() < llvm::array_lengthof(AttrInfoMap))
|
||||
return *AttrInfoMap[A.getParsedKind()];
|
||||
|
||||
// If this is an ignored attribute then return an appropriate ParsedAttrInfo.
|
||||
static ParsedAttrInfo IgnoredParsedAttrInfo(
|
||||
AttributeCommonInfo::IgnoredAttribute);
|
||||
if (A.getParsedKind() == AttributeCommonInfo::IgnoredAttribute)
|
||||
return IgnoredParsedAttrInfo;
|
||||
|
||||
// Otherwise this may be an attribute defined by a plugin. First instantiate
|
||||
// all plugin attributes if we haven't already done so.
|
||||
static std::list<std::unique_ptr<ParsedAttrInfo>> PluginAttrInstances;
|
||||
if (PluginAttrInstances.empty())
|
||||
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)
|
||||
if (S.Syntax == SyntaxUsed && S.NormalizedFullName == FullName)
|
||||
return *Ptr;
|
||||
|
||||
// If we failed to find a match then return a default ParsedAttrInfo.
|
||||
static ParsedAttrInfo DefaultParsedAttrInfo;
|
||||
return DefaultParsedAttrInfo;
|
||||
}
|
||||
|
||||
unsigned ParsedAttr::getMinArgs() const { return getInfo(*this).NumArgs; }
|
||||
unsigned ParsedAttr::getMinArgs() const { return getInfo().NumArgs; }
|
||||
|
||||
unsigned ParsedAttr::getMaxArgs() const {
|
||||
return getMinArgs() + getInfo(*this).OptArgs;
|
||||
return getMinArgs() + getInfo().OptArgs;
|
||||
}
|
||||
|
||||
bool ParsedAttr::hasCustomParsing() const {
|
||||
return getInfo(*this).HasCustomParsing;
|
||||
return getInfo().HasCustomParsing;
|
||||
}
|
||||
|
||||
bool ParsedAttr::diagnoseAppertainsTo(Sema &S, const Decl *D) const {
|
||||
return getInfo(*this).diagAppertainsToDecl(S, *this, D);
|
||||
return getInfo().diagAppertainsToDecl(S, *this, D);
|
||||
}
|
||||
|
||||
bool ParsedAttr::appliesToDecl(const Decl *D,
|
||||
|
@ -171,33 +165,33 @@ void ParsedAttr::getMatchRules(
|
|||
const LangOptions &LangOpts,
|
||||
SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>> &MatchRules)
|
||||
const {
|
||||
return getInfo(*this).getPragmaAttributeMatchRules(MatchRules, LangOpts);
|
||||
return getInfo().getPragmaAttributeMatchRules(MatchRules, LangOpts);
|
||||
}
|
||||
|
||||
bool ParsedAttr::diagnoseLangOpts(Sema &S) const {
|
||||
return getInfo(*this).diagLangOpts(S, *this);
|
||||
return getInfo().diagLangOpts(S, *this);
|
||||
}
|
||||
|
||||
bool ParsedAttr::isTargetSpecificAttr() const {
|
||||
return getInfo(*this).IsTargetSpecific;
|
||||
return getInfo().IsTargetSpecific;
|
||||
}
|
||||
|
||||
bool ParsedAttr::isTypeAttr() const { return getInfo(*this).IsType; }
|
||||
bool ParsedAttr::isTypeAttr() const { return getInfo().IsType; }
|
||||
|
||||
bool ParsedAttr::isStmtAttr() const { return getInfo(*this).IsStmt; }
|
||||
bool ParsedAttr::isStmtAttr() const { return getInfo().IsStmt; }
|
||||
|
||||
bool ParsedAttr::existsInTarget(const TargetInfo &Target) const {
|
||||
return getInfo(*this).existsInTarget(Target);
|
||||
return getInfo().existsInTarget(Target);
|
||||
}
|
||||
|
||||
bool ParsedAttr::isKnownToGCC() const { return getInfo(*this).IsKnownToGCC; }
|
||||
bool ParsedAttr::isKnownToGCC() const { return getInfo().IsKnownToGCC; }
|
||||
|
||||
bool ParsedAttr::isSupportedByPragmaAttribute() const {
|
||||
return getInfo(*this).IsSupportedByPragmaAttribute;
|
||||
return getInfo().IsSupportedByPragmaAttribute;
|
||||
}
|
||||
|
||||
unsigned ParsedAttr::getSemanticSpelling() const {
|
||||
return getInfo(*this).spellingIndexToSemanticSpelling(*this);
|
||||
return getInfo().spellingIndexToSemanticSpelling(*this);
|
||||
}
|
||||
|
||||
bool ParsedAttr::hasVariadicArg() const {
|
||||
|
@ -205,5 +199,5 @@ bool ParsedAttr::hasVariadicArg() const {
|
|||
// claim that as being variadic. If we someday get an attribute that
|
||||
// legitimately bumps up against that maximum, we can use another bit to track
|
||||
// whether it's truly variadic or not.
|
||||
return getInfo(*this).OptArgs == 15;
|
||||
return getInfo().OptArgs == 15;
|
||||
}
|
||||
|
|
|
@ -3625,9 +3625,11 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|||
|
||||
// We need to generate struct instances based off ParsedAttrInfo from
|
||||
// ParsedAttr.cpp.
|
||||
const std::string &AttrName = I->first;
|
||||
const Record &Attr = *I->second;
|
||||
OS << "struct ParsedAttrInfo" << I->first << " : public ParsedAttrInfo {\n";
|
||||
OS << " ParsedAttrInfo" << I->first << "() {\n";
|
||||
OS << " AttrKind = ParsedAttr::AT_" << AttrName << ";\n";
|
||||
emitArgInfo(Attr, OS);
|
||||
OS << " HasCustomParsing = ";
|
||||
OS << Attr.getValueAsBit("HasCustomParsing") << ";\n";
|
||||
|
@ -3642,6 +3644,20 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|||
OS << IsKnownToGCC(Attr) << ";\n";
|
||||
OS << " IsSupportedByPragmaAttribute = ";
|
||||
OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ";\n";
|
||||
for (const auto &S : GetFlattenedSpellings(Attr)) {
|
||||
const std::string &RawSpelling = S.name();
|
||||
std::string Spelling;
|
||||
if (S.variety() == "CXX11" || S.variety() == "C2x") {
|
||||
Spelling += S.nameSpace();
|
||||
Spelling += "::";
|
||||
}
|
||||
if (S.variety() == "GNU")
|
||||
Spelling += NormalizeGNUAttrSpelling(RawSpelling);
|
||||
else
|
||||
Spelling += RawSpelling;
|
||||
OS << " Spellings.push_back({AttributeCommonInfo::AS_" << S.variety();
|
||||
OS << ",\"" << Spelling << "\"});\n";
|
||||
}
|
||||
OS << " }\n";
|
||||
GenerateAppertainsTo(Attr, OS);
|
||||
GenerateLangOptRequirements(Attr, OS);
|
||||
|
|
Loading…
Reference in New Issue