
Almost all attributes currently marked `Undocumented` are user-facing attributes which _ought_ to be documented, but nobody has written it yet. This change ensures that we at least acknowledge that these attributes exist in the documentation, even if we have no description of their semantics. A new category, `InternalOnly` has been added for those few attributes which are not user-facing, and should remain omitted from the docs.
4787 lines
172 KiB
C++
4787 lines
172 KiB
C++
//===- ClangAttrEmitter.cpp - Generate Clang attribute handling =-*- C++ -*--=//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// These tablegen backends emit Clang attribute processing code
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TableGenBackends.h"
|
|
#include "ASTTableGen.h"
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/TableGen/StringMatcher.h"
|
|
#include "llvm/TableGen/TableGenBackend.h"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
class FlattenedSpelling {
|
|
std::string V, N, NS;
|
|
bool K = false;
|
|
|
|
public:
|
|
FlattenedSpelling(const std::string &Variety, const std::string &Name,
|
|
const std::string &Namespace, bool KnownToGCC) :
|
|
V(Variety), N(Name), NS(Namespace), K(KnownToGCC) {}
|
|
explicit FlattenedSpelling(const Record &Spelling)
|
|
: V(std::string(Spelling.getValueAsString("Variety"))),
|
|
N(std::string(Spelling.getValueAsString("Name"))) {
|
|
assert(V != "GCC" && V != "Clang" &&
|
|
"Given a GCC spelling, which means this hasn't been flattened!");
|
|
if (V == "CXX11" || V == "C2x" || V == "Pragma")
|
|
NS = std::string(Spelling.getValueAsString("Namespace"));
|
|
}
|
|
|
|
const std::string &variety() const { return V; }
|
|
const std::string &name() const { return N; }
|
|
const std::string &nameSpace() const { return NS; }
|
|
bool knownToGCC() const { return K; }
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static std::vector<FlattenedSpelling>
|
|
GetFlattenedSpellings(const Record &Attr) {
|
|
std::vector<Record *> Spellings = Attr.getValueAsListOfDefs("Spellings");
|
|
std::vector<FlattenedSpelling> Ret;
|
|
|
|
for (const auto &Spelling : Spellings) {
|
|
StringRef Variety = Spelling->getValueAsString("Variety");
|
|
StringRef Name = Spelling->getValueAsString("Name");
|
|
if (Variety == "GCC") {
|
|
Ret.emplace_back("GNU", std::string(Name), "", true);
|
|
Ret.emplace_back("CXX11", std::string(Name), "gnu", true);
|
|
if (Spelling->getValueAsBit("AllowInC"))
|
|
Ret.emplace_back("C2x", std::string(Name), "gnu", true);
|
|
} else if (Variety == "Clang") {
|
|
Ret.emplace_back("GNU", std::string(Name), "", false);
|
|
Ret.emplace_back("CXX11", std::string(Name), "clang", false);
|
|
if (Spelling->getValueAsBit("AllowInC"))
|
|
Ret.emplace_back("C2x", std::string(Name), "clang", false);
|
|
} else
|
|
Ret.push_back(FlattenedSpelling(*Spelling));
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static std::string ReadPCHRecord(StringRef type) {
|
|
return StringSwitch<std::string>(type)
|
|
.EndsWith("Decl *", "Record.GetLocalDeclAs<" +
|
|
std::string(type.data(), 0, type.size() - 1) +
|
|
">(Record.readInt())")
|
|
.Case("TypeSourceInfo *", "Record.readTypeSourceInfo()")
|
|
.Case("Expr *", "Record.readExpr()")
|
|
.Case("IdentifierInfo *", "Record.readIdentifier()")
|
|
.Case("StringRef", "Record.readString()")
|
|
.Case("ParamIdx", "ParamIdx::deserialize(Record.readInt())")
|
|
.Case("OMPTraitInfo *", "Record.readOMPTraitInfo()")
|
|
.Default("Record.readInt()");
|
|
}
|
|
|
|
// Get a type that is suitable for storing an object of the specified type.
|
|
static StringRef getStorageType(StringRef type) {
|
|
return StringSwitch<StringRef>(type)
|
|
.Case("StringRef", "std::string")
|
|
.Default(type);
|
|
}
|
|
|
|
// Assumes that the way to get the value is SA->getname()
|
|
static std::string WritePCHRecord(StringRef type, StringRef name) {
|
|
return "Record." +
|
|
StringSwitch<std::string>(type)
|
|
.EndsWith("Decl *", "AddDeclRef(" + std::string(name) + ");\n")
|
|
.Case("TypeSourceInfo *",
|
|
"AddTypeSourceInfo(" + std::string(name) + ");\n")
|
|
.Case("Expr *", "AddStmt(" + std::string(name) + ");\n")
|
|
.Case("IdentifierInfo *",
|
|
"AddIdentifierRef(" + std::string(name) + ");\n")
|
|
.Case("StringRef", "AddString(" + std::string(name) + ");\n")
|
|
.Case("ParamIdx",
|
|
"push_back(" + std::string(name) + ".serialize());\n")
|
|
.Case("OMPTraitInfo *",
|
|
"writeOMPTraitInfo(" + std::string(name) + ");\n")
|
|
.Default("push_back(" + std::string(name) + ");\n");
|
|
}
|
|
|
|
// Normalize attribute name by removing leading and trailing
|
|
// underscores. For example, __foo, foo__, __foo__ would
|
|
// become foo.
|
|
static StringRef NormalizeAttrName(StringRef AttrName) {
|
|
AttrName.consume_front("__");
|
|
AttrName.consume_back("__");
|
|
return AttrName;
|
|
}
|
|
|
|
// Normalize the name by removing any and all leading and trailing underscores.
|
|
// This is different from NormalizeAttrName in that it also handles names like
|
|
// _pascal and __pascal.
|
|
static StringRef NormalizeNameForSpellingComparison(StringRef Name) {
|
|
return Name.trim("_");
|
|
}
|
|
|
|
// Normalize the spelling of a GNU attribute (i.e. "x" in "__attribute__((x))"),
|
|
// removing "__" if it appears at the beginning and end of the attribute's name.
|
|
static StringRef NormalizeGNUAttrSpelling(StringRef AttrSpelling) {
|
|
if (AttrSpelling.startswith("__") && AttrSpelling.endswith("__")) {
|
|
AttrSpelling = AttrSpelling.substr(2, AttrSpelling.size() - 4);
|
|
}
|
|
|
|
return AttrSpelling;
|
|
}
|
|
|
|
typedef std::vector<std::pair<std::string, const Record *>> ParsedAttrMap;
|
|
|
|
static ParsedAttrMap getParsedAttrList(const RecordKeeper &Records,
|
|
ParsedAttrMap *Dupes = nullptr) {
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
std::set<std::string> Seen;
|
|
ParsedAttrMap R;
|
|
for (const auto *Attr : Attrs) {
|
|
if (Attr->getValueAsBit("SemaHandler")) {
|
|
std::string AN;
|
|
if (Attr->isSubClassOf("TargetSpecificAttr") &&
|
|
!Attr->isValueUnset("ParseKind")) {
|
|
AN = std::string(Attr->getValueAsString("ParseKind"));
|
|
|
|
// If this attribute has already been handled, it does not need to be
|
|
// handled again.
|
|
if (Seen.find(AN) != Seen.end()) {
|
|
if (Dupes)
|
|
Dupes->push_back(std::make_pair(AN, Attr));
|
|
continue;
|
|
}
|
|
Seen.insert(AN);
|
|
} else
|
|
AN = NormalizeAttrName(Attr->getName()).str();
|
|
|
|
R.push_back(std::make_pair(AN, Attr));
|
|
}
|
|
}
|
|
return R;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class Argument {
|
|
std::string lowerName, upperName;
|
|
StringRef attrName;
|
|
bool isOpt;
|
|
bool Fake;
|
|
|
|
public:
|
|
Argument(StringRef Arg, StringRef Attr)
|
|
: lowerName(std::string(Arg)), upperName(lowerName), attrName(Attr),
|
|
isOpt(false), Fake(false) {
|
|
if (!lowerName.empty()) {
|
|
lowerName[0] = std::tolower(lowerName[0]);
|
|
upperName[0] = std::toupper(upperName[0]);
|
|
}
|
|
// Work around MinGW's macro definition of 'interface' to 'struct'. We
|
|
// have an attribute argument called 'Interface', so only the lower case
|
|
// name conflicts with the macro definition.
|
|
if (lowerName == "interface")
|
|
lowerName = "interface_";
|
|
}
|
|
Argument(const Record &Arg, StringRef Attr)
|
|
: Argument(Arg.getValueAsString("Name"), Attr) {}
|
|
virtual ~Argument() = default;
|
|
|
|
StringRef getLowerName() const { return lowerName; }
|
|
StringRef getUpperName() const { return upperName; }
|
|
StringRef getAttrName() const { return attrName; }
|
|
|
|
bool isOptional() const { return isOpt; }
|
|
void setOptional(bool set) { isOpt = set; }
|
|
|
|
bool isFake() const { return Fake; }
|
|
void setFake(bool fake) { Fake = fake; }
|
|
|
|
// These functions print the argument contents formatted in different ways.
|
|
virtual void writeAccessors(raw_ostream &OS) const = 0;
|
|
virtual void writeAccessorDefinitions(raw_ostream &OS) const {}
|
|
virtual void writeASTVisitorTraversal(raw_ostream &OS) const {}
|
|
virtual void writeCloneArgs(raw_ostream &OS) const = 0;
|
|
virtual void writeTemplateInstantiationArgs(raw_ostream &OS) const = 0;
|
|
virtual void writeTemplateInstantiation(raw_ostream &OS) const {}
|
|
virtual void writeCtorBody(raw_ostream &OS) const {}
|
|
virtual void writeCtorInitializers(raw_ostream &OS) const = 0;
|
|
virtual void writeCtorDefaultInitializers(raw_ostream &OS) const = 0;
|
|
virtual void writeCtorParameters(raw_ostream &OS) const = 0;
|
|
virtual void writeDeclarations(raw_ostream &OS) const = 0;
|
|
virtual void writePCHReadArgs(raw_ostream &OS) const = 0;
|
|
virtual void writePCHReadDecls(raw_ostream &OS) const = 0;
|
|
virtual void writePCHWrite(raw_ostream &OS) const = 0;
|
|
virtual std::string getIsOmitted() const { return "false"; }
|
|
virtual void writeValue(raw_ostream &OS) const = 0;
|
|
virtual void writeDump(raw_ostream &OS) const = 0;
|
|
virtual void writeDumpChildren(raw_ostream &OS) const {}
|
|
virtual void writeHasChildren(raw_ostream &OS) const { OS << "false"; }
|
|
|
|
virtual bool isEnumArg() const { return false; }
|
|
virtual bool isVariadicEnumArg() const { return false; }
|
|
virtual bool isVariadic() const { return false; }
|
|
|
|
virtual void writeImplicitCtorArgs(raw_ostream &OS) const {
|
|
OS << getUpperName();
|
|
}
|
|
};
|
|
|
|
class SimpleArgument : public Argument {
|
|
std::string type;
|
|
|
|
public:
|
|
SimpleArgument(const Record &Arg, StringRef Attr, std::string T)
|
|
: Argument(Arg, Attr), type(std::move(T)) {}
|
|
|
|
std::string getType() const { return type; }
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
OS << " " << type << " get" << getUpperName() << "() const {\n";
|
|
OS << " return " << getLowerName() << ";\n";
|
|
OS << " }";
|
|
}
|
|
|
|
void writeCloneArgs(raw_ostream &OS) const override {
|
|
OS << getLowerName();
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
OS << "A->get" << getUpperName() << "()";
|
|
}
|
|
|
|
void writeCtorInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "(" << getUpperName() << ")";
|
|
}
|
|
|
|
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "()";
|
|
}
|
|
|
|
void writeCtorParameters(raw_ostream &OS) const override {
|
|
OS << type << " " << getUpperName();
|
|
}
|
|
|
|
void writeDeclarations(raw_ostream &OS) const override {
|
|
OS << type << " " << getLowerName() << ";";
|
|
}
|
|
|
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
|
std::string read = ReadPCHRecord(type);
|
|
OS << " " << type << " " << getLowerName() << " = " << read << ";\n";
|
|
}
|
|
|
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
|
OS << getLowerName();
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << " "
|
|
<< WritePCHRecord(type,
|
|
"SA->get" + std::string(getUpperName()) + "()");
|
|
}
|
|
|
|
std::string getIsOmitted() const override {
|
|
if (type == "IdentifierInfo *")
|
|
return "!get" + getUpperName().str() + "()";
|
|
if (type == "TypeSourceInfo *")
|
|
return "!get" + getUpperName().str() + "Loc()";
|
|
if (type == "ParamIdx")
|
|
return "!get" + getUpperName().str() + "().isValid()";
|
|
return "false";
|
|
}
|
|
|
|
void writeValue(raw_ostream &OS) const override {
|
|
if (type == "FunctionDecl *")
|
|
OS << "\" << get" << getUpperName()
|
|
<< "()->getNameInfo().getAsString() << \"";
|
|
else if (type == "IdentifierInfo *")
|
|
// Some non-optional (comma required) identifier arguments can be the
|
|
// empty string but are then recorded as a nullptr.
|
|
OS << "\" << (get" << getUpperName() << "() ? get" << getUpperName()
|
|
<< "()->getName() : \"\") << \"";
|
|
else if (type == "VarDecl *")
|
|
OS << "\" << get" << getUpperName() << "()->getName() << \"";
|
|
else if (type == "TypeSourceInfo *")
|
|
OS << "\" << get" << getUpperName() << "().getAsString() << \"";
|
|
else if (type == "ParamIdx")
|
|
OS << "\" << get" << getUpperName() << "().getSourceIndex() << \"";
|
|
else
|
|
OS << "\" << get" << getUpperName() << "() << \"";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {
|
|
if (StringRef(type).endswith("Decl *")) {
|
|
OS << " OS << \" \";\n";
|
|
OS << " dumpBareDeclRef(SA->get" << getUpperName() << "());\n";
|
|
} else if (type == "IdentifierInfo *") {
|
|
// Some non-optional (comma required) identifier arguments can be the
|
|
// empty string but are then recorded as a nullptr.
|
|
OS << " if (SA->get" << getUpperName() << "())\n"
|
|
<< " OS << \" \" << SA->get" << getUpperName()
|
|
<< "()->getName();\n";
|
|
} else if (type == "TypeSourceInfo *") {
|
|
if (isOptional())
|
|
OS << " if (SA->get" << getUpperName() << "Loc())";
|
|
OS << " OS << \" \" << SA->get" << getUpperName()
|
|
<< "().getAsString();\n";
|
|
} else if (type == "bool") {
|
|
OS << " if (SA->get" << getUpperName() << "()) OS << \" "
|
|
<< getUpperName() << "\";\n";
|
|
} else if (type == "int" || type == "unsigned") {
|
|
OS << " OS << \" \" << SA->get" << getUpperName() << "();\n";
|
|
} else if (type == "ParamIdx") {
|
|
if (isOptional())
|
|
OS << " if (SA->get" << getUpperName() << "().isValid())\n ";
|
|
OS << " OS << \" \" << SA->get" << getUpperName()
|
|
<< "().getSourceIndex();\n";
|
|
} else if (type == "OMPTraitInfo *") {
|
|
OS << " OS << \" \" << SA->get" << getUpperName() << "();\n";
|
|
} else {
|
|
llvm_unreachable("Unknown SimpleArgument type!");
|
|
}
|
|
}
|
|
};
|
|
|
|
class DefaultSimpleArgument : public SimpleArgument {
|
|
int64_t Default;
|
|
|
|
public:
|
|
DefaultSimpleArgument(const Record &Arg, StringRef Attr,
|
|
std::string T, int64_t Default)
|
|
: SimpleArgument(Arg, Attr, T), Default(Default) {}
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
SimpleArgument::writeAccessors(OS);
|
|
|
|
OS << "\n\n static const " << getType() << " Default" << getUpperName()
|
|
<< " = ";
|
|
if (getType() == "bool")
|
|
OS << (Default != 0 ? "true" : "false");
|
|
else
|
|
OS << Default;
|
|
OS << ";";
|
|
}
|
|
};
|
|
|
|
class StringArgument : public Argument {
|
|
public:
|
|
StringArgument(const Record &Arg, StringRef Attr)
|
|
: Argument(Arg, Attr)
|
|
{}
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
OS << " llvm::StringRef get" << getUpperName() << "() const {\n";
|
|
OS << " return llvm::StringRef(" << getLowerName() << ", "
|
|
<< getLowerName() << "Length);\n";
|
|
OS << " }\n";
|
|
OS << " unsigned get" << getUpperName() << "Length() const {\n";
|
|
OS << " return " << getLowerName() << "Length;\n";
|
|
OS << " }\n";
|
|
OS << " void set" << getUpperName()
|
|
<< "(ASTContext &C, llvm::StringRef S) {\n";
|
|
OS << " " << getLowerName() << "Length = S.size();\n";
|
|
OS << " this->" << getLowerName() << " = new (C, 1) char ["
|
|
<< getLowerName() << "Length];\n";
|
|
OS << " if (!S.empty())\n";
|
|
OS << " std::memcpy(this->" << getLowerName() << ", S.data(), "
|
|
<< getLowerName() << "Length);\n";
|
|
OS << " }";
|
|
}
|
|
|
|
void writeCloneArgs(raw_ostream &OS) const override {
|
|
OS << "get" << getUpperName() << "()";
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
OS << "A->get" << getUpperName() << "()";
|
|
}
|
|
|
|
void writeCtorBody(raw_ostream &OS) const override {
|
|
OS << " if (!" << getUpperName() << ".empty())\n";
|
|
OS << " std::memcpy(" << getLowerName() << ", " << getUpperName()
|
|
<< ".data(), " << getLowerName() << "Length);\n";
|
|
}
|
|
|
|
void writeCtorInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "Length(" << getUpperName() << ".size()),"
|
|
<< getLowerName() << "(new (Ctx, 1) char[" << getLowerName()
|
|
<< "Length])";
|
|
}
|
|
|
|
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "Length(0)," << getLowerName() << "(nullptr)";
|
|
}
|
|
|
|
void writeCtorParameters(raw_ostream &OS) const override {
|
|
OS << "llvm::StringRef " << getUpperName();
|
|
}
|
|
|
|
void writeDeclarations(raw_ostream &OS) const override {
|
|
OS << "unsigned " << getLowerName() << "Length;\n";
|
|
OS << "char *" << getLowerName() << ";";
|
|
}
|
|
|
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
|
OS << " std::string " << getLowerName()
|
|
<< "= Record.readString();\n";
|
|
}
|
|
|
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
|
OS << getLowerName();
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << " Record.AddString(SA->get" << getUpperName() << "());\n";
|
|
}
|
|
|
|
void writeValue(raw_ostream &OS) const override {
|
|
OS << "\\\"\" << get" << getUpperName() << "() << \"\\\"";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {
|
|
OS << " OS << \" \\\"\" << SA->get" << getUpperName()
|
|
<< "() << \"\\\"\";\n";
|
|
}
|
|
};
|
|
|
|
class AlignedArgument : public Argument {
|
|
public:
|
|
AlignedArgument(const Record &Arg, StringRef Attr)
|
|
: Argument(Arg, Attr)
|
|
{}
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
OS << " bool is" << getUpperName() << "Dependent() const;\n";
|
|
OS << " bool is" << getUpperName() << "ErrorDependent() const;\n";
|
|
|
|
OS << " unsigned get" << getUpperName() << "(ASTContext &Ctx) const;\n";
|
|
|
|
OS << " bool is" << getUpperName() << "Expr() const {\n";
|
|
OS << " return is" << getLowerName() << "Expr;\n";
|
|
OS << " }\n";
|
|
|
|
OS << " Expr *get" << getUpperName() << "Expr() const {\n";
|
|
OS << " assert(is" << getLowerName() << "Expr);\n";
|
|
OS << " return " << getLowerName() << "Expr;\n";
|
|
OS << " }\n";
|
|
|
|
OS << " TypeSourceInfo *get" << getUpperName() << "Type() const {\n";
|
|
OS << " assert(!is" << getLowerName() << "Expr);\n";
|
|
OS << " return " << getLowerName() << "Type;\n";
|
|
OS << " }";
|
|
}
|
|
|
|
void writeAccessorDefinitions(raw_ostream &OS) const override {
|
|
OS << "bool " << getAttrName() << "Attr::is" << getUpperName()
|
|
<< "Dependent() const {\n";
|
|
OS << " if (is" << getLowerName() << "Expr)\n";
|
|
OS << " return " << getLowerName() << "Expr && (" << getLowerName()
|
|
<< "Expr->isValueDependent() || " << getLowerName()
|
|
<< "Expr->isTypeDependent());\n";
|
|
OS << " else\n";
|
|
OS << " return " << getLowerName()
|
|
<< "Type->getType()->isDependentType();\n";
|
|
OS << "}\n";
|
|
|
|
OS << "bool " << getAttrName() << "Attr::is" << getUpperName()
|
|
<< "ErrorDependent() const {\n";
|
|
OS << " if (is" << getLowerName() << "Expr)\n";
|
|
OS << " return " << getLowerName() << "Expr && " << getLowerName()
|
|
<< "Expr->containsErrors();\n";
|
|
OS << " return " << getLowerName()
|
|
<< "Type->getType()->containsErrors();\n";
|
|
OS << "}\n";
|
|
|
|
// FIXME: Do not do the calculation here
|
|
// FIXME: Handle types correctly
|
|
// A null pointer means maximum alignment
|
|
OS << "unsigned " << getAttrName() << "Attr::get" << getUpperName()
|
|
<< "(ASTContext &Ctx) const {\n";
|
|
OS << " assert(!is" << getUpperName() << "Dependent());\n";
|
|
OS << " if (is" << getLowerName() << "Expr)\n";
|
|
OS << " return " << getLowerName() << "Expr ? " << getLowerName()
|
|
<< "Expr->EvaluateKnownConstInt(Ctx).getZExtValue()"
|
|
<< " * Ctx.getCharWidth() : "
|
|
<< "Ctx.getTargetDefaultAlignForAttributeAligned();\n";
|
|
OS << " else\n";
|
|
OS << " return 0; // FIXME\n";
|
|
OS << "}\n";
|
|
}
|
|
|
|
void writeASTVisitorTraversal(raw_ostream &OS) const override {
|
|
StringRef Name = getUpperName();
|
|
OS << " if (A->is" << Name << "Expr()) {\n"
|
|
<< " if (!getDerived().TraverseStmt(A->get" << Name << "Expr()))\n"
|
|
<< " return false;\n"
|
|
<< " } else if (auto *TSI = A->get" << Name << "Type()) {\n"
|
|
<< " if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n"
|
|
<< " return false;\n"
|
|
<< " }\n";
|
|
}
|
|
|
|
void writeCloneArgs(raw_ostream &OS) const override {
|
|
OS << "is" << getLowerName() << "Expr, is" << getLowerName()
|
|
<< "Expr ? static_cast<void*>(" << getLowerName()
|
|
<< "Expr) : " << getLowerName()
|
|
<< "Type";
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
// FIXME: move the definition in Sema::InstantiateAttrs to here.
|
|
// In the meantime, aligned attributes are cloned.
|
|
}
|
|
|
|
void writeCtorBody(raw_ostream &OS) const override {
|
|
OS << " if (is" << getLowerName() << "Expr)\n";
|
|
OS << " " << getLowerName() << "Expr = reinterpret_cast<Expr *>("
|
|
<< getUpperName() << ");\n";
|
|
OS << " else\n";
|
|
OS << " " << getLowerName()
|
|
<< "Type = reinterpret_cast<TypeSourceInfo *>(" << getUpperName()
|
|
<< ");\n";
|
|
}
|
|
|
|
void writeCtorInitializers(raw_ostream &OS) const override {
|
|
OS << "is" << getLowerName() << "Expr(Is" << getUpperName() << "Expr)";
|
|
}
|
|
|
|
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
|
|
OS << "is" << getLowerName() << "Expr(false)";
|
|
}
|
|
|
|
void writeCtorParameters(raw_ostream &OS) const override {
|
|
OS << "bool Is" << getUpperName() << "Expr, void *" << getUpperName();
|
|
}
|
|
|
|
void writeImplicitCtorArgs(raw_ostream &OS) const override {
|
|
OS << "Is" << getUpperName() << "Expr, " << getUpperName();
|
|
}
|
|
|
|
void writeDeclarations(raw_ostream &OS) const override {
|
|
OS << "bool is" << getLowerName() << "Expr;\n";
|
|
OS << "union {\n";
|
|
OS << "Expr *" << getLowerName() << "Expr;\n";
|
|
OS << "TypeSourceInfo *" << getLowerName() << "Type;\n";
|
|
OS << "};";
|
|
}
|
|
|
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
|
OS << "is" << getLowerName() << "Expr, " << getLowerName() << "Ptr";
|
|
}
|
|
|
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
|
OS << " bool is" << getLowerName() << "Expr = Record.readInt();\n";
|
|
OS << " void *" << getLowerName() << "Ptr;\n";
|
|
OS << " if (is" << getLowerName() << "Expr)\n";
|
|
OS << " " << getLowerName() << "Ptr = Record.readExpr();\n";
|
|
OS << " else\n";
|
|
OS << " " << getLowerName()
|
|
<< "Ptr = Record.readTypeSourceInfo();\n";
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << " Record.push_back(SA->is" << getUpperName() << "Expr());\n";
|
|
OS << " if (SA->is" << getUpperName() << "Expr())\n";
|
|
OS << " Record.AddStmt(SA->get" << getUpperName() << "Expr());\n";
|
|
OS << " else\n";
|
|
OS << " Record.AddTypeSourceInfo(SA->get" << getUpperName()
|
|
<< "Type());\n";
|
|
}
|
|
|
|
std::string getIsOmitted() const override {
|
|
return "!is" + getLowerName().str() + "Expr || !" + getLowerName().str()
|
|
+ "Expr";
|
|
}
|
|
|
|
void writeValue(raw_ostream &OS) const override {
|
|
OS << "\";\n";
|
|
OS << " " << getLowerName()
|
|
<< "Expr->printPretty(OS, nullptr, Policy);\n";
|
|
OS << " OS << \"";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {
|
|
OS << " if (!SA->is" << getUpperName() << "Expr())\n";
|
|
OS << " dumpType(SA->get" << getUpperName()
|
|
<< "Type()->getType());\n";
|
|
}
|
|
|
|
void writeDumpChildren(raw_ostream &OS) const override {
|
|
OS << " if (SA->is" << getUpperName() << "Expr())\n";
|
|
OS << " Visit(SA->get" << getUpperName() << "Expr());\n";
|
|
}
|
|
|
|
void writeHasChildren(raw_ostream &OS) const override {
|
|
OS << "SA->is" << getUpperName() << "Expr()";
|
|
}
|
|
};
|
|
|
|
class VariadicArgument : public Argument {
|
|
std::string Type, ArgName, ArgSizeName, RangeName;
|
|
|
|
protected:
|
|
// Assumed to receive a parameter: raw_ostream OS.
|
|
virtual void writeValueImpl(raw_ostream &OS) const {
|
|
OS << " OS << Val;\n";
|
|
}
|
|
// Assumed to receive a parameter: raw_ostream OS.
|
|
virtual void writeDumpImpl(raw_ostream &OS) const {
|
|
OS << " OS << \" \" << Val;\n";
|
|
}
|
|
|
|
public:
|
|
VariadicArgument(const Record &Arg, StringRef Attr, std::string T)
|
|
: Argument(Arg, Attr), Type(std::move(T)),
|
|
ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"),
|
|
RangeName(std::string(getLowerName())) {}
|
|
|
|
VariadicArgument(StringRef Arg, StringRef Attr, std::string T)
|
|
: Argument(Arg, Attr), Type(std::move(T)),
|
|
ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"),
|
|
RangeName(std::string(getLowerName())) {}
|
|
|
|
const std::string &getType() const { return Type; }
|
|
const std::string &getArgName() const { return ArgName; }
|
|
const std::string &getArgSizeName() const { return ArgSizeName; }
|
|
bool isVariadic() const override { return true; }
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
std::string IteratorType = getLowerName().str() + "_iterator";
|
|
std::string BeginFn = getLowerName().str() + "_begin()";
|
|
std::string EndFn = getLowerName().str() + "_end()";
|
|
|
|
OS << " typedef " << Type << "* " << IteratorType << ";\n";
|
|
OS << " " << IteratorType << " " << BeginFn << " const {"
|
|
<< " return " << ArgName << "; }\n";
|
|
OS << " " << IteratorType << " " << EndFn << " const {"
|
|
<< " return " << ArgName << " + " << ArgSizeName << "; }\n";
|
|
OS << " unsigned " << getLowerName() << "_size() const {"
|
|
<< " return " << ArgSizeName << "; }\n";
|
|
OS << " llvm::iterator_range<" << IteratorType << "> " << RangeName
|
|
<< "() const { return llvm::make_range(" << BeginFn << ", " << EndFn
|
|
<< "); }\n";
|
|
}
|
|
|
|
void writeSetter(raw_ostream &OS) const {
|
|
OS << " void set" << getUpperName() << "(ASTContext &Ctx, ";
|
|
writeCtorParameters(OS);
|
|
OS << ") {\n";
|
|
OS << " " << ArgSizeName << " = " << getUpperName() << "Size;\n";
|
|
OS << " " << ArgName << " = new (Ctx, 16) " << getType() << "["
|
|
<< ArgSizeName << "];\n";
|
|
OS << " ";
|
|
writeCtorBody(OS);
|
|
OS << " }\n";
|
|
}
|
|
|
|
void writeCloneArgs(raw_ostream &OS) const override {
|
|
OS << ArgName << ", " << ArgSizeName;
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
// This isn't elegant, but we have to go through public methods...
|
|
OS << "A->" << getLowerName() << "_begin(), "
|
|
<< "A->" << getLowerName() << "_size()";
|
|
}
|
|
|
|
void writeASTVisitorTraversal(raw_ostream &OS) const override {
|
|
// FIXME: Traverse the elements.
|
|
}
|
|
|
|
void writeCtorBody(raw_ostream &OS) const override {
|
|
OS << " std::copy(" << getUpperName() << ", " << getUpperName() << " + "
|
|
<< ArgSizeName << ", " << ArgName << ");\n";
|
|
}
|
|
|
|
void writeCtorInitializers(raw_ostream &OS) const override {
|
|
OS << ArgSizeName << "(" << getUpperName() << "Size), "
|
|
<< ArgName << "(new (Ctx, 16) " << getType() << "["
|
|
<< ArgSizeName << "])";
|
|
}
|
|
|
|
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
|
|
OS << ArgSizeName << "(0), " << ArgName << "(nullptr)";
|
|
}
|
|
|
|
void writeCtorParameters(raw_ostream &OS) const override {
|
|
OS << getType() << " *" << getUpperName() << ", unsigned "
|
|
<< getUpperName() << "Size";
|
|
}
|
|
|
|
void writeImplicitCtorArgs(raw_ostream &OS) const override {
|
|
OS << getUpperName() << ", " << getUpperName() << "Size";
|
|
}
|
|
|
|
void writeDeclarations(raw_ostream &OS) const override {
|
|
OS << " unsigned " << ArgSizeName << ";\n";
|
|
OS << " " << getType() << " *" << ArgName << ";";
|
|
}
|
|
|
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
|
OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n";
|
|
OS << " SmallVector<" << getType() << ", 4> "
|
|
<< getLowerName() << ";\n";
|
|
OS << " " << getLowerName() << ".reserve(" << getLowerName()
|
|
<< "Size);\n";
|
|
|
|
// If we can't store the values in the current type (if it's something
|
|
// like StringRef), store them in a different type and convert the
|
|
// container afterwards.
|
|
std::string StorageType = std::string(getStorageType(getType()));
|
|
std::string StorageName = std::string(getLowerName());
|
|
if (StorageType != getType()) {
|
|
StorageName += "Storage";
|
|
OS << " SmallVector<" << StorageType << ", 4> "
|
|
<< StorageName << ";\n";
|
|
OS << " " << StorageName << ".reserve(" << getLowerName()
|
|
<< "Size);\n";
|
|
}
|
|
|
|
OS << " for (unsigned i = 0; i != " << getLowerName() << "Size; ++i)\n";
|
|
std::string read = ReadPCHRecord(Type);
|
|
OS << " " << StorageName << ".push_back(" << read << ");\n";
|
|
|
|
if (StorageType != getType()) {
|
|
OS << " for (unsigned i = 0; i != " << getLowerName() << "Size; ++i)\n";
|
|
OS << " " << getLowerName() << ".push_back("
|
|
<< StorageName << "[i]);\n";
|
|
}
|
|
}
|
|
|
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
|
OS << getLowerName() << ".data(), " << getLowerName() << "Size";
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << " Record.push_back(SA->" << getLowerName() << "_size());\n";
|
|
OS << " for (auto &Val : SA->" << RangeName << "())\n";
|
|
OS << " " << WritePCHRecord(Type, "Val");
|
|
}
|
|
|
|
void writeValue(raw_ostream &OS) const override {
|
|
OS << "\";\n";
|
|
OS << " for (const auto &Val : " << RangeName << "()) {\n"
|
|
<< " DelimitAttributeArgument(OS, IsFirstArgument);\n";
|
|
writeValueImpl(OS);
|
|
OS << " }\n";
|
|
OS << " OS << \"";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {
|
|
OS << " for (const auto &Val : SA->" << RangeName << "())\n";
|
|
writeDumpImpl(OS);
|
|
}
|
|
};
|
|
|
|
class VariadicParamIdxArgument : public VariadicArgument {
|
|
public:
|
|
VariadicParamIdxArgument(const Record &Arg, StringRef Attr)
|
|
: VariadicArgument(Arg, Attr, "ParamIdx") {}
|
|
|
|
public:
|
|
void writeValueImpl(raw_ostream &OS) const override {
|
|
OS << " OS << Val.getSourceIndex();\n";
|
|
}
|
|
|
|
void writeDumpImpl(raw_ostream &OS) const override {
|
|
OS << " OS << \" \" << Val.getSourceIndex();\n";
|
|
}
|
|
};
|
|
|
|
struct VariadicParamOrParamIdxArgument : public VariadicArgument {
|
|
VariadicParamOrParamIdxArgument(const Record &Arg, StringRef Attr)
|
|
: VariadicArgument(Arg, Attr, "int") {}
|
|
};
|
|
|
|
// Unique the enums, but maintain the original declaration ordering.
|
|
std::vector<StringRef>
|
|
uniqueEnumsInOrder(const std::vector<StringRef> &enums) {
|
|
std::vector<StringRef> uniques;
|
|
SmallDenseSet<StringRef, 8> unique_set;
|
|
for (const auto &i : enums) {
|
|
if (unique_set.insert(i).second)
|
|
uniques.push_back(i);
|
|
}
|
|
return uniques;
|
|
}
|
|
|
|
class EnumArgument : public Argument {
|
|
std::string type;
|
|
std::vector<StringRef> values, enums, uniques;
|
|
|
|
public:
|
|
EnumArgument(const Record &Arg, StringRef Attr)
|
|
: Argument(Arg, Attr), type(std::string(Arg.getValueAsString("Type"))),
|
|
values(Arg.getValueAsListOfStrings("Values")),
|
|
enums(Arg.getValueAsListOfStrings("Enums")),
|
|
uniques(uniqueEnumsInOrder(enums)) {
|
|
// FIXME: Emit a proper error
|
|
assert(!uniques.empty());
|
|
}
|
|
|
|
bool isEnumArg() const override { return true; }
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
OS << " " << type << " get" << getUpperName() << "() const {\n";
|
|
OS << " return " << getLowerName() << ";\n";
|
|
OS << " }";
|
|
}
|
|
|
|
void writeCloneArgs(raw_ostream &OS) const override {
|
|
OS << getLowerName();
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
OS << "A->get" << getUpperName() << "()";
|
|
}
|
|
void writeCtorInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "(" << getUpperName() << ")";
|
|
}
|
|
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "(" << type << "(0))";
|
|
}
|
|
void writeCtorParameters(raw_ostream &OS) const override {
|
|
OS << type << " " << getUpperName();
|
|
}
|
|
void writeDeclarations(raw_ostream &OS) const override {
|
|
auto i = uniques.cbegin(), e = uniques.cend();
|
|
// The last one needs to not have a comma.
|
|
--e;
|
|
|
|
OS << "public:\n";
|
|
OS << " enum " << type << " {\n";
|
|
for (; i != e; ++i)
|
|
OS << " " << *i << ",\n";
|
|
OS << " " << *e << "\n";
|
|
OS << " };\n";
|
|
OS << "private:\n";
|
|
OS << " " << type << " " << getLowerName() << ";";
|
|
}
|
|
|
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
|
OS << " " << getAttrName() << "Attr::" << type << " " << getLowerName()
|
|
<< "(static_cast<" << getAttrName() << "Attr::" << type
|
|
<< ">(Record.readInt()));\n";
|
|
}
|
|
|
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
|
OS << getLowerName();
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << "Record.push_back(SA->get" << getUpperName() << "());\n";
|
|
}
|
|
|
|
void writeValue(raw_ostream &OS) const override {
|
|
// FIXME: this isn't 100% correct -- some enum arguments require printing
|
|
// as a string literal, while others require printing as an identifier.
|
|
// Tablegen currently does not distinguish between the two forms.
|
|
OS << "\\\"\" << " << getAttrName() << "Attr::Convert" << type << "ToStr(get"
|
|
<< getUpperName() << "()) << \"\\\"";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {
|
|
OS << " switch(SA->get" << getUpperName() << "()) {\n";
|
|
for (const auto &I : uniques) {
|
|
OS << " case " << getAttrName() << "Attr::" << I << ":\n";
|
|
OS << " OS << \" " << I << "\";\n";
|
|
OS << " break;\n";
|
|
}
|
|
OS << " }\n";
|
|
}
|
|
|
|
void writeConversion(raw_ostream &OS, bool Header) const {
|
|
if (Header) {
|
|
OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type
|
|
<< " &Out);\n";
|
|
OS << " static const char *Convert" << type << "ToStr(" << type
|
|
<< " Val);\n";
|
|
return;
|
|
}
|
|
|
|
OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type
|
|
<< "(StringRef Val, " << type << " &Out) {\n";
|
|
OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<";
|
|
OS << type << ">>(Val)\n";
|
|
for (size_t I = 0; I < enums.size(); ++I) {
|
|
OS << " .Case(\"" << values[I] << "\", ";
|
|
OS << getAttrName() << "Attr::" << enums[I] << ")\n";
|
|
}
|
|
OS << " .Default(Optional<" << type << ">());\n";
|
|
OS << " if (R) {\n";
|
|
OS << " Out = *R;\n return true;\n }\n";
|
|
OS << " return false;\n";
|
|
OS << "}\n\n";
|
|
|
|
// Mapping from enumeration values back to enumeration strings isn't
|
|
// trivial because some enumeration values have multiple named
|
|
// enumerators, such as type_visibility(internal) and
|
|
// type_visibility(hidden) both mapping to TypeVisibilityAttr::Hidden.
|
|
OS << "const char *" << getAttrName() << "Attr::Convert" << type
|
|
<< "ToStr(" << type << " Val) {\n"
|
|
<< " switch(Val) {\n";
|
|
SmallDenseSet<StringRef, 8> Uniques;
|
|
for (size_t I = 0; I < enums.size(); ++I) {
|
|
if (Uniques.insert(enums[I]).second)
|
|
OS << " case " << getAttrName() << "Attr::" << enums[I]
|
|
<< ": return \"" << values[I] << "\";\n";
|
|
}
|
|
OS << " }\n"
|
|
<< " llvm_unreachable(\"No enumerator with that value\");\n"
|
|
<< "}\n";
|
|
}
|
|
};
|
|
|
|
class VariadicEnumArgument: public VariadicArgument {
|
|
std::string type, QualifiedTypeName;
|
|
std::vector<StringRef> values, enums, uniques;
|
|
|
|
protected:
|
|
void writeValueImpl(raw_ostream &OS) const override {
|
|
// FIXME: this isn't 100% correct -- some enum arguments require printing
|
|
// as a string literal, while others require printing as an identifier.
|
|
// Tablegen currently does not distinguish between the two forms.
|
|
OS << " OS << \"\\\"\" << " << getAttrName() << "Attr::Convert" << type
|
|
<< "ToStr(Val)" << "<< \"\\\"\";\n";
|
|
}
|
|
|
|
public:
|
|
VariadicEnumArgument(const Record &Arg, StringRef Attr)
|
|
: VariadicArgument(Arg, Attr,
|
|
std::string(Arg.getValueAsString("Type"))),
|
|
type(std::string(Arg.getValueAsString("Type"))),
|
|
values(Arg.getValueAsListOfStrings("Values")),
|
|
enums(Arg.getValueAsListOfStrings("Enums")),
|
|
uniques(uniqueEnumsInOrder(enums)) {
|
|
QualifiedTypeName = getAttrName().str() + "Attr::" + type;
|
|
|
|
// FIXME: Emit a proper error
|
|
assert(!uniques.empty());
|
|
}
|
|
|
|
bool isVariadicEnumArg() const override { return true; }
|
|
|
|
void writeDeclarations(raw_ostream &OS) const override {
|
|
auto i = uniques.cbegin(), e = uniques.cend();
|
|
// The last one needs to not have a comma.
|
|
--e;
|
|
|
|
OS << "public:\n";
|
|
OS << " enum " << type << " {\n";
|
|
for (; i != e; ++i)
|
|
OS << " " << *i << ",\n";
|
|
OS << " " << *e << "\n";
|
|
OS << " };\n";
|
|
OS << "private:\n";
|
|
|
|
VariadicArgument::writeDeclarations(OS);
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {
|
|
OS << " for (" << getAttrName() << "Attr::" << getLowerName()
|
|
<< "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->"
|
|
<< getLowerName() << "_end(); I != E; ++I) {\n";
|
|
OS << " switch(*I) {\n";
|
|
for (const auto &UI : uniques) {
|
|
OS << " case " << getAttrName() << "Attr::" << UI << ":\n";
|
|
OS << " OS << \" " << UI << "\";\n";
|
|
OS << " break;\n";
|
|
}
|
|
OS << " }\n";
|
|
OS << " }\n";
|
|
}
|
|
|
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
|
OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n";
|
|
OS << " SmallVector<" << QualifiedTypeName << ", 4> " << getLowerName()
|
|
<< ";\n";
|
|
OS << " " << getLowerName() << ".reserve(" << getLowerName()
|
|
<< "Size);\n";
|
|
OS << " for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
|
|
OS << " " << getLowerName() << ".push_back(" << "static_cast<"
|
|
<< QualifiedTypeName << ">(Record.readInt()));\n";
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << " Record.push_back(SA->" << getLowerName() << "_size());\n";
|
|
OS << " for (" << getAttrName() << "Attr::" << getLowerName()
|
|
<< "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->"
|
|
<< getLowerName() << "_end(); i != e; ++i)\n";
|
|
OS << " " << WritePCHRecord(QualifiedTypeName, "(*i)");
|
|
}
|
|
|
|
void writeConversion(raw_ostream &OS, bool Header) const {
|
|
if (Header) {
|
|
OS << " static bool ConvertStrTo" << type << "(StringRef Val, " << type
|
|
<< " &Out);\n";
|
|
OS << " static const char *Convert" << type << "ToStr(" << type
|
|
<< " Val);\n";
|
|
return;
|
|
}
|
|
|
|
OS << "bool " << getAttrName() << "Attr::ConvertStrTo" << type
|
|
<< "(StringRef Val, ";
|
|
OS << type << " &Out) {\n";
|
|
OS << " Optional<" << type << "> R = llvm::StringSwitch<Optional<";
|
|
OS << type << ">>(Val)\n";
|
|
for (size_t I = 0; I < enums.size(); ++I) {
|
|
OS << " .Case(\"" << values[I] << "\", ";
|
|
OS << getAttrName() << "Attr::" << enums[I] << ")\n";
|
|
}
|
|
OS << " .Default(Optional<" << type << ">());\n";
|
|
OS << " if (R) {\n";
|
|
OS << " Out = *R;\n return true;\n }\n";
|
|
OS << " return false;\n";
|
|
OS << "}\n\n";
|
|
|
|
OS << "const char *" << getAttrName() << "Attr::Convert" << type
|
|
<< "ToStr(" << type << " Val) {\n"
|
|
<< " switch(Val) {\n";
|
|
SmallDenseSet<StringRef, 8> Uniques;
|
|
for (size_t I = 0; I < enums.size(); ++I) {
|
|
if (Uniques.insert(enums[I]).second)
|
|
OS << " case " << getAttrName() << "Attr::" << enums[I]
|
|
<< ": return \"" << values[I] << "\";\n";
|
|
}
|
|
OS << " }\n"
|
|
<< " llvm_unreachable(\"No enumerator with that value\");\n"
|
|
<< "}\n";
|
|
}
|
|
};
|
|
|
|
class VersionArgument : public Argument {
|
|
public:
|
|
VersionArgument(const Record &Arg, StringRef Attr)
|
|
: Argument(Arg, Attr)
|
|
{}
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
OS << " VersionTuple get" << getUpperName() << "() const {\n";
|
|
OS << " return " << getLowerName() << ";\n";
|
|
OS << " }\n";
|
|
OS << " void set" << getUpperName()
|
|
<< "(ASTContext &C, VersionTuple V) {\n";
|
|
OS << " " << getLowerName() << " = V;\n";
|
|
OS << " }";
|
|
}
|
|
|
|
void writeCloneArgs(raw_ostream &OS) const override {
|
|
OS << "get" << getUpperName() << "()";
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
OS << "A->get" << getUpperName() << "()";
|
|
}
|
|
|
|
void writeCtorInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "(" << getUpperName() << ")";
|
|
}
|
|
|
|
void writeCtorDefaultInitializers(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "()";
|
|
}
|
|
|
|
void writeCtorParameters(raw_ostream &OS) const override {
|
|
OS << "VersionTuple " << getUpperName();
|
|
}
|
|
|
|
void writeDeclarations(raw_ostream &OS) const override {
|
|
OS << "VersionTuple " << getLowerName() << ";\n";
|
|
}
|
|
|
|
void writePCHReadDecls(raw_ostream &OS) const override {
|
|
OS << " VersionTuple " << getLowerName()
|
|
<< "= Record.readVersionTuple();\n";
|
|
}
|
|
|
|
void writePCHReadArgs(raw_ostream &OS) const override {
|
|
OS << getLowerName();
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << " Record.AddVersionTuple(SA->get" << getUpperName() << "());\n";
|
|
}
|
|
|
|
void writeValue(raw_ostream &OS) const override {
|
|
OS << getLowerName() << "=\" << get" << getUpperName() << "() << \"";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {
|
|
OS << " OS << \" \" << SA->get" << getUpperName() << "();\n";
|
|
}
|
|
};
|
|
|
|
class ExprArgument : public SimpleArgument {
|
|
public:
|
|
ExprArgument(const Record &Arg, StringRef Attr)
|
|
: SimpleArgument(Arg, Attr, "Expr *")
|
|
{}
|
|
|
|
void writeASTVisitorTraversal(raw_ostream &OS) const override {
|
|
OS << " if (!"
|
|
<< "getDerived().TraverseStmt(A->get" << getUpperName() << "()))\n";
|
|
OS << " return false;\n";
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
OS << "tempInst" << getUpperName();
|
|
}
|
|
|
|
void writeTemplateInstantiation(raw_ostream &OS) const override {
|
|
OS << " " << getType() << " tempInst" << getUpperName() << ";\n";
|
|
OS << " {\n";
|
|
OS << " EnterExpressionEvaluationContext "
|
|
<< "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n";
|
|
OS << " ExprResult " << "Result = S.SubstExpr("
|
|
<< "A->get" << getUpperName() << "(), TemplateArgs);\n";
|
|
OS << " if (Result.isInvalid())\n";
|
|
OS << " return nullptr;\n";
|
|
OS << " tempInst" << getUpperName() << " = Result.get();\n";
|
|
OS << " }\n";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {}
|
|
|
|
void writeDumpChildren(raw_ostream &OS) const override {
|
|
OS << " Visit(SA->get" << getUpperName() << "());\n";
|
|
}
|
|
|
|
void writeHasChildren(raw_ostream &OS) const override { OS << "true"; }
|
|
};
|
|
|
|
class VariadicExprArgument : public VariadicArgument {
|
|
public:
|
|
VariadicExprArgument(const Record &Arg, StringRef Attr)
|
|
: VariadicArgument(Arg, Attr, "Expr *")
|
|
{}
|
|
|
|
VariadicExprArgument(StringRef ArgName, StringRef Attr)
|
|
: VariadicArgument(ArgName, Attr, "Expr *") {}
|
|
|
|
void writeASTVisitorTraversal(raw_ostream &OS) const override {
|
|
OS << " {\n";
|
|
OS << " " << getType() << " *I = A->" << getLowerName()
|
|
<< "_begin();\n";
|
|
OS << " " << getType() << " *E = A->" << getLowerName()
|
|
<< "_end();\n";
|
|
OS << " for (; I != E; ++I) {\n";
|
|
OS << " if (!getDerived().TraverseStmt(*I))\n";
|
|
OS << " return false;\n";
|
|
OS << " }\n";
|
|
OS << " }\n";
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
OS << "tempInst" << getUpperName() << ", "
|
|
<< "A->" << getLowerName() << "_size()";
|
|
}
|
|
|
|
void writeTemplateInstantiation(raw_ostream &OS) const override {
|
|
OS << " auto *tempInst" << getUpperName()
|
|
<< " = new (C, 16) " << getType()
|
|
<< "[A->" << getLowerName() << "_size()];\n";
|
|
OS << " {\n";
|
|
OS << " EnterExpressionEvaluationContext "
|
|
<< "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n";
|
|
OS << " " << getType() << " *TI = tempInst" << getUpperName()
|
|
<< ";\n";
|
|
OS << " " << getType() << " *I = A->" << getLowerName()
|
|
<< "_begin();\n";
|
|
OS << " " << getType() << " *E = A->" << getLowerName()
|
|
<< "_end();\n";
|
|
OS << " for (; I != E; ++I, ++TI) {\n";
|
|
OS << " ExprResult Result = S.SubstExpr(*I, TemplateArgs);\n";
|
|
OS << " if (Result.isInvalid())\n";
|
|
OS << " return nullptr;\n";
|
|
OS << " *TI = Result.get();\n";
|
|
OS << " }\n";
|
|
OS << " }\n";
|
|
}
|
|
|
|
void writeDump(raw_ostream &OS) const override {}
|
|
|
|
void writeDumpChildren(raw_ostream &OS) const override {
|
|
OS << " for (" << getAttrName() << "Attr::" << getLowerName()
|
|
<< "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->"
|
|
<< getLowerName() << "_end(); I != E; ++I)\n";
|
|
OS << " Visit(*I);\n";
|
|
}
|
|
|
|
void writeHasChildren(raw_ostream &OS) const override {
|
|
OS << "SA->" << getLowerName() << "_begin() != "
|
|
<< "SA->" << getLowerName() << "_end()";
|
|
}
|
|
};
|
|
|
|
class VariadicIdentifierArgument : public VariadicArgument {
|
|
public:
|
|
VariadicIdentifierArgument(const Record &Arg, StringRef Attr)
|
|
: VariadicArgument(Arg, Attr, "IdentifierInfo *")
|
|
{}
|
|
};
|
|
|
|
class VariadicStringArgument : public VariadicArgument {
|
|
public:
|
|
VariadicStringArgument(const Record &Arg, StringRef Attr)
|
|
: VariadicArgument(Arg, Attr, "StringRef")
|
|
{}
|
|
|
|
void writeCtorBody(raw_ostream &OS) const override {
|
|
OS << " for (size_t I = 0, E = " << getArgSizeName() << "; I != E;\n"
|
|
" ++I) {\n"
|
|
" StringRef Ref = " << getUpperName() << "[I];\n"
|
|
" if (!Ref.empty()) {\n"
|
|
" char *Mem = new (Ctx, 1) char[Ref.size()];\n"
|
|
" std::memcpy(Mem, Ref.data(), Ref.size());\n"
|
|
" " << getArgName() << "[I] = StringRef(Mem, Ref.size());\n"
|
|
" }\n"
|
|
" }\n";
|
|
}
|
|
|
|
void writeValueImpl(raw_ostream &OS) const override {
|
|
OS << " OS << \"\\\"\" << Val << \"\\\"\";\n";
|
|
}
|
|
};
|
|
|
|
class TypeArgument : public SimpleArgument {
|
|
public:
|
|
TypeArgument(const Record &Arg, StringRef Attr)
|
|
: SimpleArgument(Arg, Attr, "TypeSourceInfo *")
|
|
{}
|
|
|
|
void writeAccessors(raw_ostream &OS) const override {
|
|
OS << " QualType get" << getUpperName() << "() const {\n";
|
|
OS << " return " << getLowerName() << "->getType();\n";
|
|
OS << " }";
|
|
OS << " " << getType() << " get" << getUpperName() << "Loc() const {\n";
|
|
OS << " return " << getLowerName() << ";\n";
|
|
OS << " }";
|
|
}
|
|
|
|
void writeASTVisitorTraversal(raw_ostream &OS) const override {
|
|
OS << " if (auto *TSI = A->get" << getUpperName() << "Loc())\n";
|
|
OS << " if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n";
|
|
OS << " return false;\n";
|
|
}
|
|
|
|
void writeTemplateInstantiation(raw_ostream &OS) const override {
|
|
OS << " " << getType() << " tempInst" << getUpperName() << " =\n";
|
|
OS << " S.SubstType(A->get" << getUpperName() << "Loc(), "
|
|
<< "TemplateArgs, A->getLoc(), A->getAttrName());\n";
|
|
OS << " if (!tempInst" << getUpperName() << ")\n";
|
|
OS << " return nullptr;\n";
|
|
}
|
|
|
|
void writeTemplateInstantiationArgs(raw_ostream &OS) const override {
|
|
OS << "tempInst" << getUpperName();
|
|
}
|
|
|
|
void writePCHWrite(raw_ostream &OS) const override {
|
|
OS << " "
|
|
<< WritePCHRecord(getType(),
|
|
"SA->get" + std::string(getUpperName()) + "Loc()");
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static std::unique_ptr<Argument>
|
|
createArgument(const Record &Arg, StringRef Attr,
|
|
const Record *Search = nullptr) {
|
|
if (!Search)
|
|
Search = &Arg;
|
|
|
|
std::unique_ptr<Argument> Ptr;
|
|
llvm::StringRef ArgName = Search->getName();
|
|
|
|
if (ArgName == "AlignedArgument")
|
|
Ptr = std::make_unique<AlignedArgument>(Arg, Attr);
|
|
else if (ArgName == "EnumArgument")
|
|
Ptr = std::make_unique<EnumArgument>(Arg, Attr);
|
|
else if (ArgName == "ExprArgument")
|
|
Ptr = std::make_unique<ExprArgument>(Arg, Attr);
|
|
else if (ArgName == "DeclArgument")
|
|
Ptr = std::make_unique<SimpleArgument>(
|
|
Arg, Attr, (Arg.getValueAsDef("Kind")->getName() + "Decl *").str());
|
|
else if (ArgName == "IdentifierArgument")
|
|
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "IdentifierInfo *");
|
|
else if (ArgName == "DefaultBoolArgument")
|
|
Ptr = std::make_unique<DefaultSimpleArgument>(
|
|
Arg, Attr, "bool", Arg.getValueAsBit("Default"));
|
|
else if (ArgName == "BoolArgument")
|
|
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "bool");
|
|
else if (ArgName == "DefaultIntArgument")
|
|
Ptr = std::make_unique<DefaultSimpleArgument>(
|
|
Arg, Attr, "int", Arg.getValueAsInt("Default"));
|
|
else if (ArgName == "IntArgument")
|
|
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "int");
|
|
else if (ArgName == "StringArgument")
|
|
Ptr = std::make_unique<StringArgument>(Arg, Attr);
|
|
else if (ArgName == "TypeArgument")
|
|
Ptr = std::make_unique<TypeArgument>(Arg, Attr);
|
|
else if (ArgName == "UnsignedArgument")
|
|
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "unsigned");
|
|
else if (ArgName == "VariadicUnsignedArgument")
|
|
Ptr = std::make_unique<VariadicArgument>(Arg, Attr, "unsigned");
|
|
else if (ArgName == "VariadicStringArgument")
|
|
Ptr = std::make_unique<VariadicStringArgument>(Arg, Attr);
|
|
else if (ArgName == "VariadicEnumArgument")
|
|
Ptr = std::make_unique<VariadicEnumArgument>(Arg, Attr);
|
|
else if (ArgName == "VariadicExprArgument")
|
|
Ptr = std::make_unique<VariadicExprArgument>(Arg, Attr);
|
|
else if (ArgName == "VariadicParamIdxArgument")
|
|
Ptr = std::make_unique<VariadicParamIdxArgument>(Arg, Attr);
|
|
else if (ArgName == "VariadicParamOrParamIdxArgument")
|
|
Ptr = std::make_unique<VariadicParamOrParamIdxArgument>(Arg, Attr);
|
|
else if (ArgName == "ParamIdxArgument")
|
|
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "ParamIdx");
|
|
else if (ArgName == "VariadicIdentifierArgument")
|
|
Ptr = std::make_unique<VariadicIdentifierArgument>(Arg, Attr);
|
|
else if (ArgName == "VersionArgument")
|
|
Ptr = std::make_unique<VersionArgument>(Arg, Attr);
|
|
else if (ArgName == "OMPTraitInfoArgument")
|
|
Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "OMPTraitInfo *");
|
|
|
|
if (!Ptr) {
|
|
// Search in reverse order so that the most-derived type is handled first.
|
|
ArrayRef<std::pair<Record*, SMRange>> Bases = Search->getSuperClasses();
|
|
for (const auto &Base : llvm::reverse(Bases)) {
|
|
if ((Ptr = createArgument(Arg, Attr, Base.first)))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Ptr && Arg.getValueAsBit("Optional"))
|
|
Ptr->setOptional(true);
|
|
|
|
if (Ptr && Arg.getValueAsBit("Fake"))
|
|
Ptr->setFake(true);
|
|
|
|
return Ptr;
|
|
}
|
|
|
|
static void writeAvailabilityValue(raw_ostream &OS) {
|
|
OS << "\" << getPlatform()->getName();\n"
|
|
<< " if (getStrict()) OS << \", strict\";\n"
|
|
<< " if (!getIntroduced().empty()) OS << \", introduced=\" << getIntroduced();\n"
|
|
<< " if (!getDeprecated().empty()) OS << \", deprecated=\" << getDeprecated();\n"
|
|
<< " if (!getObsoleted().empty()) OS << \", obsoleted=\" << getObsoleted();\n"
|
|
<< " if (getUnavailable()) OS << \", unavailable\";\n"
|
|
<< " OS << \"";
|
|
}
|
|
|
|
static void writeDeprecatedAttrValue(raw_ostream &OS, std::string &Variety) {
|
|
OS << "\\\"\" << getMessage() << \"\\\"\";\n";
|
|
// Only GNU deprecated has an optional fixit argument at the second position.
|
|
if (Variety == "GNU")
|
|
OS << " if (!getReplacement().empty()) OS << \", \\\"\""
|
|
" << getReplacement() << \"\\\"\";\n";
|
|
OS << " OS << \"";
|
|
}
|
|
|
|
static void writeGetSpellingFunction(const Record &R, raw_ostream &OS) {
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
|
|
|
|
OS << "const char *" << R.getName() << "Attr::getSpelling() const {\n";
|
|
if (Spellings.empty()) {
|
|
OS << " return \"(No spelling)\";\n}\n\n";
|
|
return;
|
|
}
|
|
|
|
OS << " switch (getAttributeSpellingListIndex()) {\n"
|
|
" default:\n"
|
|
" llvm_unreachable(\"Unknown attribute spelling!\");\n"
|
|
" return \"(No spelling)\";\n";
|
|
|
|
for (unsigned I = 0; I < Spellings.size(); ++I)
|
|
OS << " case " << I << ":\n"
|
|
" return \"" << Spellings[I].name() << "\";\n";
|
|
// End of the switch statement.
|
|
OS << " }\n";
|
|
// End of the getSpelling function.
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
static void
|
|
writePrettyPrintFunction(const Record &R,
|
|
const std::vector<std::unique_ptr<Argument>> &Args,
|
|
raw_ostream &OS) {
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
|
|
|
|
OS << "void " << R.getName() << "Attr::printPretty("
|
|
<< "raw_ostream &OS, const PrintingPolicy &Policy) const {\n";
|
|
|
|
if (Spellings.empty()) {
|
|
OS << "}\n\n";
|
|
return;
|
|
}
|
|
|
|
OS << " bool IsFirstArgument = true; (void)IsFirstArgument;\n"
|
|
<< " unsigned TrailingOmittedArgs = 0; (void)TrailingOmittedArgs;\n"
|
|
<< " switch (getAttributeSpellingListIndex()) {\n"
|
|
<< " default:\n"
|
|
<< " llvm_unreachable(\"Unknown attribute spelling!\");\n"
|
|
<< " break;\n";
|
|
|
|
for (unsigned I = 0; I < Spellings.size(); ++ I) {
|
|
llvm::SmallString<16> Prefix;
|
|
llvm::SmallString<8> Suffix;
|
|
// The actual spelling of the name and namespace (if applicable)
|
|
// of an attribute without considering prefix and suffix.
|
|
llvm::SmallString<64> Spelling;
|
|
std::string Name = Spellings[I].name();
|
|
std::string Variety = Spellings[I].variety();
|
|
|
|
if (Variety == "GNU") {
|
|
Prefix = " __attribute__((";
|
|
Suffix = "))";
|
|
} else if (Variety == "CXX11" || Variety == "C2x") {
|
|
Prefix = " [[";
|
|
Suffix = "]]";
|
|
std::string Namespace = Spellings[I].nameSpace();
|
|
if (!Namespace.empty()) {
|
|
Spelling += Namespace;
|
|
Spelling += "::";
|
|
}
|
|
} else if (Variety == "Declspec") {
|
|
Prefix = " __declspec(";
|
|
Suffix = ")";
|
|
} else if (Variety == "Microsoft") {
|
|
Prefix = "[";
|
|
Suffix = "]";
|
|
} else if (Variety == "Keyword") {
|
|
Prefix = " ";
|
|
Suffix = "";
|
|
} else if (Variety == "Pragma") {
|
|
Prefix = "#pragma ";
|
|
Suffix = "\n";
|
|
std::string Namespace = Spellings[I].nameSpace();
|
|
if (!Namespace.empty()) {
|
|
Spelling += Namespace;
|
|
Spelling += " ";
|
|
}
|
|
} else if (Variety == "HLSLSemantic") {
|
|
Prefix = ":";
|
|
Suffix = "";
|
|
} else {
|
|
llvm_unreachable("Unknown attribute syntax variety!");
|
|
}
|
|
|
|
Spelling += Name;
|
|
|
|
OS << " case " << I << " : {\n"
|
|
<< " OS << \"" << Prefix << Spelling << "\";\n";
|
|
|
|
if (Variety == "Pragma") {
|
|
OS << " printPrettyPragma(OS, Policy);\n";
|
|
OS << " OS << \"\\n\";";
|
|
OS << " break;\n";
|
|
OS << " }\n";
|
|
continue;
|
|
}
|
|
|
|
if (Spelling == "availability") {
|
|
OS << " OS << \"(";
|
|
writeAvailabilityValue(OS);
|
|
OS << ")\";\n";
|
|
} else if (Spelling == "deprecated" || Spelling == "gnu::deprecated") {
|
|
OS << " OS << \"(";
|
|
writeDeprecatedAttrValue(OS, Variety);
|
|
OS << ")\";\n";
|
|
} else {
|
|
// To avoid printing parentheses around an empty argument list or
|
|
// printing spurious commas at the end of an argument list, we need to
|
|
// determine where the last provided non-fake argument is.
|
|
bool FoundNonOptArg = false;
|
|
for (const auto &arg : llvm::reverse(Args)) {
|
|
if (arg->isFake())
|
|
continue;
|
|
if (FoundNonOptArg)
|
|
continue;
|
|
// FIXME: arg->getIsOmitted() == "false" means we haven't implemented
|
|
// any way to detect whether the argument was omitted.
|
|
if (!arg->isOptional() || arg->getIsOmitted() == "false") {
|
|
FoundNonOptArg = true;
|
|
continue;
|
|
}
|
|
OS << " if (" << arg->getIsOmitted() << ")\n"
|
|
<< " ++TrailingOmittedArgs;\n";
|
|
}
|
|
unsigned ArgIndex = 0;
|
|
for (const auto &arg : Args) {
|
|
if (arg->isFake())
|
|
continue;
|
|
std::string IsOmitted = arg->getIsOmitted();
|
|
if (arg->isOptional() && IsOmitted != "false")
|
|
OS << " if (!(" << IsOmitted << ")) {\n";
|
|
// Variadic arguments print their own leading comma.
|
|
if (!arg->isVariadic())
|
|
OS << " DelimitAttributeArgument(OS, IsFirstArgument);\n";
|
|
OS << " OS << \"";
|
|
arg->writeValue(OS);
|
|
OS << "\";\n";
|
|
if (arg->isOptional() && IsOmitted != "false")
|
|
OS << " }\n";
|
|
++ArgIndex;
|
|
}
|
|
if (ArgIndex != 0)
|
|
OS << " if (!IsFirstArgument)\n"
|
|
<< " OS << \")\";\n";
|
|
}
|
|
OS << " OS << \"" << Suffix << "\";\n"
|
|
<< " break;\n"
|
|
<< " }\n";
|
|
}
|
|
|
|
// End of the switch statement.
|
|
OS << "}\n";
|
|
// End of the print function.
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
/// Return the index of a spelling in a spelling list.
|
|
static unsigned
|
|
getSpellingListIndex(const std::vector<FlattenedSpelling> &SpellingList,
|
|
const FlattenedSpelling &Spelling) {
|
|
assert(!SpellingList.empty() && "Spelling list is empty!");
|
|
|
|
for (unsigned Index = 0; Index < SpellingList.size(); ++Index) {
|
|
const FlattenedSpelling &S = SpellingList[Index];
|
|
if (S.variety() != Spelling.variety())
|
|
continue;
|
|
if (S.nameSpace() != Spelling.nameSpace())
|
|
continue;
|
|
if (S.name() != Spelling.name())
|
|
continue;
|
|
|
|
return Index;
|
|
}
|
|
|
|
llvm_unreachable("Unknown spelling!");
|
|
}
|
|
|
|
static void writeAttrAccessorDefinition(const Record &R, raw_ostream &OS) {
|
|
std::vector<Record*> Accessors = R.getValueAsListOfDefs("Accessors");
|
|
if (Accessors.empty())
|
|
return;
|
|
|
|
const std::vector<FlattenedSpelling> SpellingList = GetFlattenedSpellings(R);
|
|
assert(!SpellingList.empty() &&
|
|
"Attribute with empty spelling list can't have accessors!");
|
|
for (const auto *Accessor : Accessors) {
|
|
const StringRef Name = Accessor->getValueAsString("Name");
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Accessor);
|
|
|
|
OS << " bool " << Name
|
|
<< "() const { return getAttributeSpellingListIndex() == ";
|
|
for (unsigned Index = 0; Index < Spellings.size(); ++Index) {
|
|
OS << getSpellingListIndex(SpellingList, Spellings[Index]);
|
|
if (Index != Spellings.size() - 1)
|
|
OS << " ||\n getAttributeSpellingListIndex() == ";
|
|
else
|
|
OS << "; }\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
SpellingNamesAreCommon(const std::vector<FlattenedSpelling>& Spellings) {
|
|
assert(!Spellings.empty() && "An empty list of spellings was provided");
|
|
std::string FirstName =
|
|
std::string(NormalizeNameForSpellingComparison(Spellings.front().name()));
|
|
for (const auto &Spelling :
|
|
llvm::make_range(std::next(Spellings.begin()), Spellings.end())) {
|
|
std::string Name =
|
|
std::string(NormalizeNameForSpellingComparison(Spelling.name()));
|
|
if (Name != FirstName)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
typedef std::map<unsigned, std::string> SemanticSpellingMap;
|
|
static std::string
|
|
CreateSemanticSpellings(const std::vector<FlattenedSpelling> &Spellings,
|
|
SemanticSpellingMap &Map) {
|
|
// The enumerants are automatically generated based on the variety,
|
|
// namespace (if present) and name for each attribute spelling. However,
|
|
// care is taken to avoid trampling on the reserved namespace due to
|
|
// underscores.
|
|
std::string Ret(" enum Spelling {\n");
|
|
std::set<std::string> Uniques;
|
|
unsigned Idx = 0;
|
|
|
|
// If we have a need to have this many spellings we likely need to add an
|
|
// extra bit to the SpellingIndex in AttributeCommonInfo, then increase the
|
|
// value of SpellingNotCalculated there and here.
|
|
assert(Spellings.size() < 15 &&
|
|
"Too many spellings, would step on SpellingNotCalculated in "
|
|
"AttributeCommonInfo");
|
|
for (auto I = Spellings.begin(), E = Spellings.end(); I != E; ++I, ++Idx) {
|
|
const FlattenedSpelling &S = *I;
|
|
const std::string &Variety = S.variety();
|
|
const std::string &Spelling = S.name();
|
|
const std::string &Namespace = S.nameSpace();
|
|
std::string EnumName;
|
|
|
|
EnumName += (Variety + "_");
|
|
if (!Namespace.empty())
|
|
EnumName += (NormalizeNameForSpellingComparison(Namespace).str() +
|
|
"_");
|
|
EnumName += NormalizeNameForSpellingComparison(Spelling);
|
|
|
|
// Even if the name is not unique, this spelling index corresponds to a
|
|
// particular enumerant name that we've calculated.
|
|
Map[Idx] = EnumName;
|
|
|
|
// Since we have been stripping underscores to avoid trampling on the
|
|
// reserved namespace, we may have inadvertently created duplicate
|
|
// enumerant names. These duplicates are not considered part of the
|
|
// semantic spelling, and can be elided.
|
|
if (Uniques.find(EnumName) != Uniques.end())
|
|
continue;
|
|
|
|
Uniques.insert(EnumName);
|
|
if (I != Spellings.begin())
|
|
Ret += ",\n";
|
|
// Duplicate spellings are not considered part of the semantic spelling
|
|
// enumeration, but the spelling index and semantic spelling values are
|
|
// meant to be equivalent, so we must specify a concrete value for each
|
|
// enumerator.
|
|
Ret += " " + EnumName + " = " + llvm::utostr(Idx);
|
|
}
|
|
Ret += ",\n SpellingNotCalculated = 15\n";
|
|
Ret += "\n };\n\n";
|
|
return Ret;
|
|
}
|
|
|
|
void WriteSemanticSpellingSwitch(const std::string &VarName,
|
|
const SemanticSpellingMap &Map,
|
|
raw_ostream &OS) {
|
|
OS << " switch (" << VarName << ") {\n default: "
|
|
<< "llvm_unreachable(\"Unknown spelling list index\");\n";
|
|
for (const auto &I : Map)
|
|
OS << " case " << I.first << ": return " << I.second << ";\n";
|
|
OS << " }\n";
|
|
}
|
|
|
|
// Emits the LateParsed property for attributes.
|
|
static void emitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) {
|
|
OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n";
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
|
|
for (const auto *Attr : Attrs) {
|
|
bool LateParsed = Attr->getValueAsBit("LateParsed");
|
|
|
|
if (LateParsed) {
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
|
|
|
|
// FIXME: Handle non-GNU attributes
|
|
for (const auto &I : Spellings) {
|
|
if (I.variety() != "GNU")
|
|
continue;
|
|
OS << ".Case(\"" << I.name() << "\", " << LateParsed << ")\n";
|
|
}
|
|
}
|
|
}
|
|
OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n";
|
|
}
|
|
|
|
static bool hasGNUorCXX11Spelling(const Record &Attribute) {
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attribute);
|
|
for (const auto &I : Spellings) {
|
|
if (I.variety() == "GNU" || I.variety() == "CXX11")
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct AttributeSubjectMatchRule {
|
|
const Record *MetaSubject;
|
|
const Record *Constraint;
|
|
|
|
AttributeSubjectMatchRule(const Record *MetaSubject, const Record *Constraint)
|
|
: MetaSubject(MetaSubject), Constraint(Constraint) {
|
|
assert(MetaSubject && "Missing subject");
|
|
}
|
|
|
|
bool isSubRule() const { return Constraint != nullptr; }
|
|
|
|
std::vector<Record *> getSubjects() const {
|
|
return (Constraint ? Constraint : MetaSubject)
|
|
->getValueAsListOfDefs("Subjects");
|
|
}
|
|
|
|
std::vector<Record *> getLangOpts() const {
|
|
if (Constraint) {
|
|
// Lookup the options in the sub-rule first, in case the sub-rule
|
|
// overrides the rules options.
|
|
std::vector<Record *> Opts = Constraint->getValueAsListOfDefs("LangOpts");
|
|
if (!Opts.empty())
|
|
return Opts;
|
|
}
|
|
return MetaSubject->getValueAsListOfDefs("LangOpts");
|
|
}
|
|
|
|
// Abstract rules are used only for sub-rules
|
|
bool isAbstractRule() const { return getSubjects().empty(); }
|
|
|
|
StringRef getName() const {
|
|
return (Constraint ? Constraint : MetaSubject)->getValueAsString("Name");
|
|
}
|
|
|
|
bool isNegatedSubRule() const {
|
|
assert(isSubRule() && "Not a sub-rule");
|
|
return Constraint->getValueAsBit("Negated");
|
|
}
|
|
|
|
std::string getSpelling() const {
|
|
std::string Result = std::string(MetaSubject->getValueAsString("Name"));
|
|
if (isSubRule()) {
|
|
Result += '(';
|
|
if (isNegatedSubRule())
|
|
Result += "unless(";
|
|
Result += getName();
|
|
if (isNegatedSubRule())
|
|
Result += ')';
|
|
Result += ')';
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
std::string getEnumValueName() const {
|
|
SmallString<128> Result;
|
|
Result += "SubjectMatchRule_";
|
|
Result += MetaSubject->getValueAsString("Name");
|
|
if (isSubRule()) {
|
|
Result += "_";
|
|
if (isNegatedSubRule())
|
|
Result += "not_";
|
|
Result += Constraint->getValueAsString("Name");
|
|
}
|
|
if (isAbstractRule())
|
|
Result += "_abstract";
|
|
return std::string(Result.str());
|
|
}
|
|
|
|
std::string getEnumValue() const { return "attr::" + getEnumValueName(); }
|
|
|
|
static const char *EnumName;
|
|
};
|
|
|
|
const char *AttributeSubjectMatchRule::EnumName = "attr::SubjectMatchRule";
|
|
|
|
struct PragmaClangAttributeSupport {
|
|
std::vector<AttributeSubjectMatchRule> Rules;
|
|
|
|
class RuleOrAggregateRuleSet {
|
|
std::vector<AttributeSubjectMatchRule> Rules;
|
|
bool IsRule;
|
|
RuleOrAggregateRuleSet(ArrayRef<AttributeSubjectMatchRule> Rules,
|
|
bool IsRule)
|
|
: Rules(Rules), IsRule(IsRule) {}
|
|
|
|
public:
|
|
bool isRule() const { return IsRule; }
|
|
|
|
const AttributeSubjectMatchRule &getRule() const {
|
|
assert(IsRule && "not a rule!");
|
|
return Rules[0];
|
|
}
|
|
|
|
ArrayRef<AttributeSubjectMatchRule> getAggregateRuleSet() const {
|
|
return Rules;
|
|
}
|
|
|
|
static RuleOrAggregateRuleSet
|
|
getRule(const AttributeSubjectMatchRule &Rule) {
|
|
return RuleOrAggregateRuleSet(Rule, /*IsRule=*/true);
|
|
}
|
|
static RuleOrAggregateRuleSet
|
|
getAggregateRuleSet(ArrayRef<AttributeSubjectMatchRule> Rules) {
|
|
return RuleOrAggregateRuleSet(Rules, /*IsRule=*/false);
|
|
}
|
|
};
|
|
llvm::DenseMap<const Record *, RuleOrAggregateRuleSet> SubjectsToRules;
|
|
|
|
PragmaClangAttributeSupport(RecordKeeper &Records);
|
|
|
|
bool isAttributedSupported(const Record &Attribute);
|
|
|
|
void emitMatchRuleList(raw_ostream &OS);
|
|
|
|
void generateStrictConformsTo(const Record &Attr, raw_ostream &OS);
|
|
|
|
void generateParsingHelpers(raw_ostream &OS);
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static bool isSupportedPragmaClangAttributeSubject(const Record &Subject) {
|
|
// FIXME: #pragma clang attribute does not currently support statement
|
|
// attributes, so test whether the subject is one that appertains to a
|
|
// declaration node. However, it may be reasonable for support for statement
|
|
// attributes to be added.
|
|
if (Subject.isSubClassOf("DeclNode") || Subject.isSubClassOf("DeclBase") ||
|
|
Subject.getName() == "DeclBase")
|
|
return true;
|
|
|
|
if (Subject.isSubClassOf("SubsetSubject"))
|
|
return isSupportedPragmaClangAttributeSubject(
|
|
*Subject.getValueAsDef("Base"));
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool doesDeclDeriveFrom(const Record *D, const Record *Base) {
|
|
const Record *CurrentBase = D->getValueAsOptionalDef(BaseFieldName);
|
|
if (!CurrentBase)
|
|
return false;
|
|
if (CurrentBase == Base)
|
|
return true;
|
|
return doesDeclDeriveFrom(CurrentBase, Base);
|
|
}
|
|
|
|
PragmaClangAttributeSupport::PragmaClangAttributeSupport(
|
|
RecordKeeper &Records) {
|
|
std::vector<Record *> MetaSubjects =
|
|
Records.getAllDerivedDefinitions("AttrSubjectMatcherRule");
|
|
auto MapFromSubjectsToRules = [this](const Record *SubjectContainer,
|
|
const Record *MetaSubject,
|
|
const Record *Constraint) {
|
|
Rules.emplace_back(MetaSubject, Constraint);
|
|
std::vector<Record *> ApplicableSubjects =
|
|
SubjectContainer->getValueAsListOfDefs("Subjects");
|
|
for (const auto *Subject : ApplicableSubjects) {
|
|
bool Inserted =
|
|
SubjectsToRules
|
|
.try_emplace(Subject, RuleOrAggregateRuleSet::getRule(
|
|
AttributeSubjectMatchRule(MetaSubject,
|
|
Constraint)))
|
|
.second;
|
|
if (!Inserted) {
|
|
PrintFatalError("Attribute subject match rules should not represent"
|
|
"same attribute subjects.");
|
|
}
|
|
}
|
|
};
|
|
for (const auto *MetaSubject : MetaSubjects) {
|
|
MapFromSubjectsToRules(MetaSubject, MetaSubject, /*Constraints=*/nullptr);
|
|
std::vector<Record *> Constraints =
|
|
MetaSubject->getValueAsListOfDefs("Constraints");
|
|
for (const auto *Constraint : Constraints)
|
|
MapFromSubjectsToRules(Constraint, MetaSubject, Constraint);
|
|
}
|
|
|
|
std::vector<Record *> Aggregates =
|
|
Records.getAllDerivedDefinitions("AttrSubjectMatcherAggregateRule");
|
|
std::vector<Record *> DeclNodes =
|
|
Records.getAllDerivedDefinitions(DeclNodeClassName);
|
|
for (const auto *Aggregate : Aggregates) {
|
|
Record *SubjectDecl = Aggregate->getValueAsDef("Subject");
|
|
|
|
// Gather sub-classes of the aggregate subject that act as attribute
|
|
// subject rules.
|
|
std::vector<AttributeSubjectMatchRule> Rules;
|
|
for (const auto *D : DeclNodes) {
|
|
if (doesDeclDeriveFrom(D, SubjectDecl)) {
|
|
auto It = SubjectsToRules.find(D);
|
|
if (It == SubjectsToRules.end())
|
|
continue;
|
|
if (!It->second.isRule() || It->second.getRule().isSubRule())
|
|
continue; // Assume that the rule will be included as well.
|
|
Rules.push_back(It->second.getRule());
|
|
}
|
|
}
|
|
|
|
bool Inserted =
|
|
SubjectsToRules
|
|
.try_emplace(SubjectDecl,
|
|
RuleOrAggregateRuleSet::getAggregateRuleSet(Rules))
|
|
.second;
|
|
if (!Inserted) {
|
|
PrintFatalError("Attribute subject match rules should not represent"
|
|
"same attribute subjects.");
|
|
}
|
|
}
|
|
}
|
|
|
|
static PragmaClangAttributeSupport &
|
|
getPragmaAttributeSupport(RecordKeeper &Records) {
|
|
static PragmaClangAttributeSupport Instance(Records);
|
|
return Instance;
|
|
}
|
|
|
|
void PragmaClangAttributeSupport::emitMatchRuleList(raw_ostream &OS) {
|
|
OS << "#ifndef ATTR_MATCH_SUB_RULE\n";
|
|
OS << "#define ATTR_MATCH_SUB_RULE(Value, Spelling, IsAbstract, Parent, "
|
|
"IsNegated) "
|
|
<< "ATTR_MATCH_RULE(Value, Spelling, IsAbstract)\n";
|
|
OS << "#endif\n";
|
|
for (const auto &Rule : Rules) {
|
|
OS << (Rule.isSubRule() ? "ATTR_MATCH_SUB_RULE" : "ATTR_MATCH_RULE") << '(';
|
|
OS << Rule.getEnumValueName() << ", \"" << Rule.getSpelling() << "\", "
|
|
<< Rule.isAbstractRule();
|
|
if (Rule.isSubRule())
|
|
OS << ", "
|
|
<< AttributeSubjectMatchRule(Rule.MetaSubject, nullptr).getEnumValue()
|
|
<< ", " << Rule.isNegatedSubRule();
|
|
OS << ")\n";
|
|
}
|
|
OS << "#undef ATTR_MATCH_SUB_RULE\n";
|
|
}
|
|
|
|
bool PragmaClangAttributeSupport::isAttributedSupported(
|
|
const Record &Attribute) {
|
|
// If the attribute explicitly specified whether to support #pragma clang
|
|
// attribute, use that setting.
|
|
bool Unset;
|
|
bool SpecifiedResult =
|
|
Attribute.getValueAsBitOrUnset("PragmaAttributeSupport", Unset);
|
|
if (!Unset)
|
|
return SpecifiedResult;
|
|
|
|
// Opt-out rules:
|
|
// An attribute requires delayed parsing (LateParsed is on)
|
|
if (Attribute.getValueAsBit("LateParsed"))
|
|
return false;
|
|
// An attribute has no GNU/CXX11 spelling
|
|
if (!hasGNUorCXX11Spelling(Attribute))
|
|
return false;
|
|
// An attribute subject list has a subject that isn't covered by one of the
|
|
// subject match rules or has no subjects at all.
|
|
if (Attribute.isValueUnset("Subjects"))
|
|
return false;
|
|
const Record *SubjectObj = Attribute.getValueAsDef("Subjects");
|
|
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
|
|
bool HasAtLeastOneValidSubject = false;
|
|
for (const auto *Subject : Subjects) {
|
|
if (!isSupportedPragmaClangAttributeSubject(*Subject))
|
|
continue;
|
|
if (SubjectsToRules.find(Subject) == SubjectsToRules.end())
|
|
return false;
|
|
HasAtLeastOneValidSubject = true;
|
|
}
|
|
return HasAtLeastOneValidSubject;
|
|
}
|
|
|
|
static std::string GenerateTestExpression(ArrayRef<Record *> LangOpts) {
|
|
std::string Test;
|
|
|
|
for (auto *E : LangOpts) {
|
|
if (!Test.empty())
|
|
Test += " || ";
|
|
|
|
const StringRef Code = E->getValueAsString("CustomCode");
|
|
if (!Code.empty()) {
|
|
Test += "(";
|
|
Test += Code;
|
|
Test += ")";
|
|
if (!E->getValueAsString("Name").empty()) {
|
|
PrintWarning(
|
|
E->getLoc(),
|
|
"non-empty 'Name' field ignored because 'CustomCode' was supplied");
|
|
}
|
|
} else {
|
|
Test += "LangOpts.";
|
|
Test += E->getValueAsString("Name");
|
|
}
|
|
}
|
|
|
|
if (Test.empty())
|
|
return "true";
|
|
|
|
return Test;
|
|
}
|
|
|
|
void
|
|
PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr,
|
|
raw_ostream &OS) {
|
|
if (!isAttributedSupported(Attr) || Attr.isValueUnset("Subjects"))
|
|
return;
|
|
// Generate a function that constructs a set of matching rules that describe
|
|
// to which declarations the attribute should apply to.
|
|
OS << "void getPragmaAttributeMatchRules("
|
|
<< "llvm::SmallVectorImpl<std::pair<"
|
|
<< AttributeSubjectMatchRule::EnumName
|
|
<< ", bool>> &MatchRules, const LangOptions &LangOpts) const override {\n";
|
|
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
|
|
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
|
|
for (const auto *Subject : Subjects) {
|
|
if (!isSupportedPragmaClangAttributeSubject(*Subject))
|
|
continue;
|
|
auto It = SubjectsToRules.find(Subject);
|
|
assert(It != SubjectsToRules.end() &&
|
|
"This attribute is unsupported by #pragma clang attribute");
|
|
for (const auto &Rule : It->getSecond().getAggregateRuleSet()) {
|
|
// The rule might be language specific, so only subtract it from the given
|
|
// rules if the specific language options are specified.
|
|
std::vector<Record *> LangOpts = Rule.getLangOpts();
|
|
OS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue()
|
|
<< ", /*IsSupported=*/" << GenerateTestExpression(LangOpts)
|
|
<< "));\n";
|
|
}
|
|
}
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) {
|
|
// Generate routines that check the names of sub-rules.
|
|
OS << "Optional<attr::SubjectMatchRule> "
|
|
"defaultIsAttributeSubjectMatchSubRuleFor(StringRef, bool) {\n";
|
|
OS << " return None;\n";
|
|
OS << "}\n\n";
|
|
|
|
llvm::MapVector<const Record *, std::vector<AttributeSubjectMatchRule>>
|
|
SubMatchRules;
|
|
for (const auto &Rule : Rules) {
|
|
if (!Rule.isSubRule())
|
|
continue;
|
|
SubMatchRules[Rule.MetaSubject].push_back(Rule);
|
|
}
|
|
|
|
for (const auto &SubMatchRule : SubMatchRules) {
|
|
OS << "Optional<attr::SubjectMatchRule> isAttributeSubjectMatchSubRuleFor_"
|
|
<< SubMatchRule.first->getValueAsString("Name")
|
|
<< "(StringRef Name, bool IsUnless) {\n";
|
|
OS << " if (IsUnless)\n";
|
|
OS << " return "
|
|
"llvm::StringSwitch<Optional<attr::SubjectMatchRule>>(Name).\n";
|
|
for (const auto &Rule : SubMatchRule.second) {
|
|
if (Rule.isNegatedSubRule())
|
|
OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue()
|
|
<< ").\n";
|
|
}
|
|
OS << " Default(None);\n";
|
|
OS << " return "
|
|
"llvm::StringSwitch<Optional<attr::SubjectMatchRule>>(Name).\n";
|
|
for (const auto &Rule : SubMatchRule.second) {
|
|
if (!Rule.isNegatedSubRule())
|
|
OS << " Case(\"" << Rule.getName() << "\", " << Rule.getEnumValue()
|
|
<< ").\n";
|
|
}
|
|
OS << " Default(None);\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
// Generate the function that checks for the top-level rules.
|
|
OS << "std::pair<Optional<attr::SubjectMatchRule>, "
|
|
"Optional<attr::SubjectMatchRule> (*)(StringRef, "
|
|
"bool)> isAttributeSubjectMatchRule(StringRef Name) {\n";
|
|
OS << " return "
|
|
"llvm::StringSwitch<std::pair<Optional<attr::SubjectMatchRule>, "
|
|
"Optional<attr::SubjectMatchRule> (*) (StringRef, "
|
|
"bool)>>(Name).\n";
|
|
for (const auto &Rule : Rules) {
|
|
if (Rule.isSubRule())
|
|
continue;
|
|
std::string SubRuleFunction;
|
|
if (SubMatchRules.count(Rule.MetaSubject))
|
|
SubRuleFunction =
|
|
("isAttributeSubjectMatchSubRuleFor_" + Rule.getName()).str();
|
|
else
|
|
SubRuleFunction = "defaultIsAttributeSubjectMatchSubRuleFor";
|
|
OS << " Case(\"" << Rule.getName() << "\", std::make_pair("
|
|
<< Rule.getEnumValue() << ", " << SubRuleFunction << ")).\n";
|
|
}
|
|
OS << " Default(std::make_pair(None, "
|
|
"defaultIsAttributeSubjectMatchSubRuleFor));\n";
|
|
OS << "}\n\n";
|
|
|
|
// Generate the function that checks for the submatch rules.
|
|
OS << "const char *validAttributeSubjectMatchSubRules("
|
|
<< AttributeSubjectMatchRule::EnumName << " Rule) {\n";
|
|
OS << " switch (Rule) {\n";
|
|
for (const auto &SubMatchRule : SubMatchRules) {
|
|
OS << " case "
|
|
<< AttributeSubjectMatchRule(SubMatchRule.first, nullptr).getEnumValue()
|
|
<< ":\n";
|
|
OS << " return \"'";
|
|
bool IsFirst = true;
|
|
for (const auto &Rule : SubMatchRule.second) {
|
|
if (!IsFirst)
|
|
OS << ", '";
|
|
IsFirst = false;
|
|
if (Rule.isNegatedSubRule())
|
|
OS << "unless(";
|
|
OS << Rule.getName();
|
|
if (Rule.isNegatedSubRule())
|
|
OS << ')';
|
|
OS << "'";
|
|
}
|
|
OS << "\";\n";
|
|
}
|
|
OS << " default: return nullptr;\n";
|
|
OS << " }\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
template <typename Fn>
|
|
static void forEachUniqueSpelling(const Record &Attr, Fn &&F) {
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
|
|
SmallDenseSet<StringRef, 8> Seen;
|
|
for (const FlattenedSpelling &S : Spellings) {
|
|
if (Seen.insert(S.name()).second)
|
|
F(S);
|
|
}
|
|
}
|
|
|
|
static bool isTypeArgument(const Record *Arg) {
|
|
return !Arg->getSuperClasses().empty() &&
|
|
Arg->getSuperClasses().back().first->getName() == "TypeArgument";
|
|
}
|
|
|
|
/// Emits the first-argument-is-type property for attributes.
|
|
static void emitClangAttrTypeArgList(RecordKeeper &Records, raw_ostream &OS) {
|
|
OS << "#if defined(CLANG_ATTR_TYPE_ARG_LIST)\n";
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
|
|
for (const auto *Attr : Attrs) {
|
|
// Determine whether the first argument is a type.
|
|
std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
|
|
if (Args.empty())
|
|
continue;
|
|
|
|
if (!isTypeArgument(Args[0]))
|
|
continue;
|
|
|
|
// All these spellings take a single type argument.
|
|
forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
|
|
OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n";
|
|
});
|
|
}
|
|
OS << "#endif // CLANG_ATTR_TYPE_ARG_LIST\n\n";
|
|
}
|
|
|
|
/// Emits the parse-arguments-in-unevaluated-context property for
|
|
/// attributes.
|
|
static void emitClangAttrArgContextList(RecordKeeper &Records, raw_ostream &OS) {
|
|
OS << "#if defined(CLANG_ATTR_ARG_CONTEXT_LIST)\n";
|
|
ParsedAttrMap Attrs = getParsedAttrList(Records);
|
|
for (const auto &I : Attrs) {
|
|
const Record &Attr = *I.second;
|
|
|
|
if (!Attr.getValueAsBit("ParseArgumentsAsUnevaluated"))
|
|
continue;
|
|
|
|
// All these spellings take are parsed unevaluated.
|
|
forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) {
|
|
OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n";
|
|
});
|
|
}
|
|
OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n";
|
|
}
|
|
|
|
static bool isIdentifierArgument(const Record *Arg) {
|
|
return !Arg->getSuperClasses().empty() &&
|
|
llvm::StringSwitch<bool>(Arg->getSuperClasses().back().first->getName())
|
|
.Case("IdentifierArgument", true)
|
|
.Case("EnumArgument", true)
|
|
.Case("VariadicEnumArgument", true)
|
|
.Default(false);
|
|
}
|
|
|
|
static bool isVariadicIdentifierArgument(const Record *Arg) {
|
|
return !Arg->getSuperClasses().empty() &&
|
|
llvm::StringSwitch<bool>(
|
|
Arg->getSuperClasses().back().first->getName())
|
|
.Case("VariadicIdentifierArgument", true)
|
|
.Case("VariadicParamOrParamIdxArgument", true)
|
|
.Default(false);
|
|
}
|
|
|
|
static bool isVariadicExprArgument(const Record *Arg) {
|
|
return !Arg->getSuperClasses().empty() &&
|
|
llvm::StringSwitch<bool>(
|
|
Arg->getSuperClasses().back().first->getName())
|
|
.Case("VariadicExprArgument", true)
|
|
.Default(false);
|
|
}
|
|
|
|
static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records,
|
|
raw_ostream &OS) {
|
|
OS << "#if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST)\n";
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
for (const auto *A : Attrs) {
|
|
// Determine whether the first argument is a variadic identifier.
|
|
std::vector<Record *> Args = A->getValueAsListOfDefs("Args");
|
|
if (Args.empty() || !isVariadicIdentifierArgument(Args[0]))
|
|
continue;
|
|
|
|
// All these spellings take an identifier argument.
|
|
forEachUniqueSpelling(*A, [&](const FlattenedSpelling &S) {
|
|
OS << ".Case(\"" << S.name() << "\", "
|
|
<< "true"
|
|
<< ")\n";
|
|
});
|
|
}
|
|
OS << "#endif // CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST\n\n";
|
|
}
|
|
|
|
// Emits the first-argument-is-identifier property for attributes.
|
|
static void emitClangAttrIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) {
|
|
OS << "#if defined(CLANG_ATTR_IDENTIFIER_ARG_LIST)\n";
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
|
|
for (const auto *Attr : Attrs) {
|
|
// Determine whether the first argument is an identifier.
|
|
std::vector<Record *> Args = Attr->getValueAsListOfDefs("Args");
|
|
if (Args.empty() || !isIdentifierArgument(Args[0]))
|
|
continue;
|
|
|
|
// All these spellings take an identifier argument.
|
|
forEachUniqueSpelling(*Attr, [&](const FlattenedSpelling &S) {
|
|
OS << ".Case(\"" << S.name() << "\", " << "true" << ")\n";
|
|
});
|
|
}
|
|
OS << "#endif // CLANG_ATTR_IDENTIFIER_ARG_LIST\n\n";
|
|
}
|
|
|
|
static bool keywordThisIsaIdentifierInArgument(const Record *Arg) {
|
|
return !Arg->getSuperClasses().empty() &&
|
|
llvm::StringSwitch<bool>(
|
|
Arg->getSuperClasses().back().first->getName())
|
|
.Case("VariadicParamOrParamIdxArgument", true)
|
|
.Default(false);
|
|
}
|
|
|
|
static void emitClangAttrThisIsaIdentifierArgList(RecordKeeper &Records,
|
|
raw_ostream &OS) {
|
|
OS << "#if defined(CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST)\n";
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
for (const auto *A : Attrs) {
|
|
// Determine whether the first argument is a variadic identifier.
|
|
std::vector<Record *> Args = A->getValueAsListOfDefs("Args");
|
|
if (Args.empty() || !keywordThisIsaIdentifierInArgument(Args[0]))
|
|
continue;
|
|
|
|
// All these spellings take an identifier argument.
|
|
forEachUniqueSpelling(*A, [&](const FlattenedSpelling &S) {
|
|
OS << ".Case(\"" << S.name() << "\", "
|
|
<< "true"
|
|
<< ")\n";
|
|
});
|
|
}
|
|
OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n";
|
|
}
|
|
|
|
static void emitClangAttrAcceptsExprPack(RecordKeeper &Records,
|
|
raw_ostream &OS) {
|
|
OS << "#if defined(CLANG_ATTR_ACCEPTS_EXPR_PACK)\n";
|
|
ParsedAttrMap Attrs = getParsedAttrList(Records);
|
|
for (const auto &I : Attrs) {
|
|
const Record &Attr = *I.second;
|
|
|
|
if (!Attr.getValueAsBit("AcceptsExprPack"))
|
|
continue;
|
|
|
|
forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) {
|
|
OS << ".Case(\"" << S.name() << "\", true)\n";
|
|
});
|
|
}
|
|
OS << "#endif // CLANG_ATTR_ACCEPTS_EXPR_PACK\n\n";
|
|
}
|
|
|
|
static void emitAttributes(RecordKeeper &Records, raw_ostream &OS,
|
|
bool Header) {
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
ParsedAttrMap AttrMap = getParsedAttrList(Records);
|
|
|
|
// Helper to print the starting character of an attribute argument. If there
|
|
// hasn't been an argument yet, it prints an opening parenthese; otherwise it
|
|
// prints a comma.
|
|
OS << "static inline void DelimitAttributeArgument("
|
|
<< "raw_ostream& OS, bool& IsFirst) {\n"
|
|
<< " if (IsFirst) {\n"
|
|
<< " IsFirst = false;\n"
|
|
<< " OS << \"(\";\n"
|
|
<< " } else\n"
|
|
<< " OS << \", \";\n"
|
|
<< "}\n";
|
|
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
|
|
// FIXME: Currently, documentation is generated as-needed due to the fact
|
|
// that there is no way to allow a generated project "reach into" the docs
|
|
// directory (for instance, it may be an out-of-tree build). However, we want
|
|
// to ensure that every attribute has a Documentation field, and produce an
|
|
// error if it has been neglected. Otherwise, the on-demand generation which
|
|
// happens server-side will fail. This code is ensuring that functionality,
|
|
// even though this Emitter doesn't technically need the documentation.
|
|
// When attribute documentation can be generated as part of the build
|
|
// itself, this code can be removed.
|
|
(void)R.getValueAsListOfDefs("Documentation");
|
|
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
ArrayRef<std::pair<Record *, SMRange>> Supers = R.getSuperClasses();
|
|
assert(!Supers.empty() && "Forgot to specify a superclass for the attr");
|
|
std::string SuperName;
|
|
bool Inheritable = false;
|
|
for (const auto &Super : llvm::reverse(Supers)) {
|
|
const Record *R = Super.first;
|
|
if (R->getName() != "TargetSpecificAttr" &&
|
|
R->getName() != "DeclOrTypeAttr" && SuperName.empty())
|
|
SuperName = std::string(R->getName());
|
|
if (R->getName() == "InheritableAttr")
|
|
Inheritable = true;
|
|
}
|
|
|
|
if (Header)
|
|
OS << "class " << R.getName() << "Attr : public " << SuperName << " {\n";
|
|
else
|
|
OS << "\n// " << R.getName() << "Attr implementation\n\n";
|
|
|
|
std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args");
|
|
std::vector<std::unique_ptr<Argument>> Args;
|
|
Args.reserve(ArgRecords.size());
|
|
|
|
bool AttrAcceptsExprPack = Attr->getValueAsBit("AcceptsExprPack");
|
|
if (AttrAcceptsExprPack) {
|
|
for (size_t I = 0; I < ArgRecords.size(); ++I) {
|
|
const Record *ArgR = ArgRecords[I];
|
|
if (isIdentifierArgument(ArgR) || isVariadicIdentifierArgument(ArgR) ||
|
|
isTypeArgument(ArgR))
|
|
PrintFatalError(Attr->getLoc(),
|
|
"Attributes accepting packs cannot also "
|
|
"have identifier or type arguments.");
|
|
// When trying to determine if value-dependent expressions can populate
|
|
// the attribute without prior instantiation, the decision is made based
|
|
// on the assumption that only the last argument is ever variadic.
|
|
if (I < (ArgRecords.size() - 1) && isVariadicExprArgument(ArgR))
|
|
PrintFatalError(Attr->getLoc(),
|
|
"Attributes accepting packs can only have the last "
|
|
"argument be variadic.");
|
|
}
|
|
}
|
|
|
|
bool HasOptArg = false;
|
|
bool HasFakeArg = false;
|
|
for (const auto *ArgRecord : ArgRecords) {
|
|
Args.emplace_back(createArgument(*ArgRecord, R.getName()));
|
|
if (Header) {
|
|
Args.back()->writeDeclarations(OS);
|
|
OS << "\n\n";
|
|
}
|
|
|
|
// For these purposes, fake takes priority over optional.
|
|
if (Args.back()->isFake()) {
|
|
HasFakeArg = true;
|
|
} else if (Args.back()->isOptional()) {
|
|
HasOptArg = true;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<VariadicExprArgument> DelayedArgs = nullptr;
|
|
if (AttrAcceptsExprPack) {
|
|
DelayedArgs =
|
|
std::make_unique<VariadicExprArgument>("DelayedArgs", R.getName());
|
|
if (Header) {
|
|
DelayedArgs->writeDeclarations(OS);
|
|
OS << "\n\n";
|
|
}
|
|
}
|
|
|
|
if (Header)
|
|
OS << "public:\n";
|
|
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
|
|
|
|
// If there are zero or one spellings, all spelling-related functionality
|
|
// can be elided. If all of the spellings share the same name, the spelling
|
|
// functionality can also be elided.
|
|
bool ElideSpelling = (Spellings.size() <= 1) ||
|
|
SpellingNamesAreCommon(Spellings);
|
|
|
|
// This maps spelling index values to semantic Spelling enumerants.
|
|
SemanticSpellingMap SemanticToSyntacticMap;
|
|
|
|
std::string SpellingEnum;
|
|
if (Spellings.size() > 1)
|
|
SpellingEnum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap);
|
|
if (Header)
|
|
OS << SpellingEnum;
|
|
|
|
const auto &ParsedAttrSpellingItr = llvm::find_if(
|
|
AttrMap, [R](const std::pair<std::string, const Record *> &P) {
|
|
return &R == P.second;
|
|
});
|
|
|
|
// Emit CreateImplicit factory methods.
|
|
auto emitCreate = [&](bool Implicit, bool DelayedArgsOnly, bool emitFake) {
|
|
if (Header)
|
|
OS << " static ";
|
|
OS << R.getName() << "Attr *";
|
|
if (!Header)
|
|
OS << R.getName() << "Attr::";
|
|
OS << "Create";
|
|
if (Implicit)
|
|
OS << "Implicit";
|
|
if (DelayedArgsOnly)
|
|
OS << "WithDelayedArgs";
|
|
OS << "(";
|
|
OS << "ASTContext &Ctx";
|
|
if (!DelayedArgsOnly) {
|
|
for (auto const &ai : Args) {
|
|
if (ai->isFake() && !emitFake)
|
|
continue;
|
|
OS << ", ";
|
|
ai->writeCtorParameters(OS);
|
|
}
|
|
} else {
|
|
OS << ", ";
|
|
DelayedArgs->writeCtorParameters(OS);
|
|
}
|
|
OS << ", const AttributeCommonInfo &CommonInfo";
|
|
if (Header && Implicit)
|
|
OS << " = {SourceRange{}}";
|
|
OS << ")";
|
|
if (Header) {
|
|
OS << ";\n";
|
|
return;
|
|
}
|
|
|
|
OS << " {\n";
|
|
OS << " auto *A = new (Ctx) " << R.getName();
|
|
OS << "Attr(Ctx, CommonInfo";
|
|
if (!DelayedArgsOnly) {
|
|
for (auto const &ai : Args) {
|
|
if (ai->isFake() && !emitFake)
|
|
continue;
|
|
OS << ", ";
|
|
ai->writeImplicitCtorArgs(OS);
|
|
}
|
|
}
|
|
OS << ");\n";
|
|
if (Implicit) {
|
|
OS << " A->setImplicit(true);\n";
|
|
}
|
|
if (Implicit || ElideSpelling) {
|
|
OS << " if (!A->isAttributeSpellingListCalculated() && "
|
|
"!A->getAttrName())\n";
|
|
OS << " A->setAttributeSpellingListIndex(0);\n";
|
|
}
|
|
if (DelayedArgsOnly) {
|
|
OS << " A->setDelayedArgs(Ctx, ";
|
|
DelayedArgs->writeImplicitCtorArgs(OS);
|
|
OS << ");\n";
|
|
}
|
|
OS << " return A;\n}\n\n";
|
|
};
|
|
|
|
auto emitCreateNoCI = [&](bool Implicit, bool DelayedArgsOnly,
|
|
bool emitFake) {
|
|
if (Header)
|
|
OS << " static ";
|
|
OS << R.getName() << "Attr *";
|
|
if (!Header)
|
|
OS << R.getName() << "Attr::";
|
|
OS << "Create";
|
|
if (Implicit)
|
|
OS << "Implicit";
|
|
if (DelayedArgsOnly)
|
|
OS << "WithDelayedArgs";
|
|
OS << "(";
|
|
OS << "ASTContext &Ctx";
|
|
if (!DelayedArgsOnly) {
|
|
for (auto const &ai : Args) {
|
|
if (ai->isFake() && !emitFake)
|
|
continue;
|
|
OS << ", ";
|
|
ai->writeCtorParameters(OS);
|
|
}
|
|
} else {
|
|
OS << ", ";
|
|
DelayedArgs->writeCtorParameters(OS);
|
|
}
|
|
OS << ", SourceRange Range, AttributeCommonInfo::Syntax Syntax";
|
|
if (!ElideSpelling) {
|
|
OS << ", " << R.getName() << "Attr::Spelling S";
|
|
if (Header)
|
|
OS << " = static_cast<Spelling>(SpellingNotCalculated)";
|
|
}
|
|
OS << ")";
|
|
if (Header) {
|
|
OS << ";\n";
|
|
return;
|
|
}
|
|
|
|
OS << " {\n";
|
|
OS << " AttributeCommonInfo I(Range, ";
|
|
|
|
if (ParsedAttrSpellingItr != std::end(AttrMap))
|
|
OS << "AT_" << ParsedAttrSpellingItr->first;
|
|
else
|
|
OS << "NoSemaHandlerAttribute";
|
|
|
|
OS << ", Syntax";
|
|
if (!ElideSpelling)
|
|
OS << ", S";
|
|
OS << ");\n";
|
|
OS << " return Create";
|
|
if (Implicit)
|
|
OS << "Implicit";
|
|
if (DelayedArgsOnly)
|
|
OS << "WithDelayedArgs";
|
|
OS << "(Ctx";
|
|
if (!DelayedArgsOnly) {
|
|
for (auto const &ai : Args) {
|
|
if (ai->isFake() && !emitFake)
|
|
continue;
|
|
OS << ", ";
|
|
ai->writeImplicitCtorArgs(OS);
|
|
}
|
|
} else {
|
|
OS << ", ";
|
|
DelayedArgs->writeImplicitCtorArgs(OS);
|
|
}
|
|
OS << ", I);\n";
|
|
OS << "}\n\n";
|
|
};
|
|
|
|
auto emitCreates = [&](bool DelayedArgsOnly, bool emitFake) {
|
|
emitCreate(true, DelayedArgsOnly, emitFake);
|
|
emitCreate(false, DelayedArgsOnly, emitFake);
|
|
emitCreateNoCI(true, DelayedArgsOnly, emitFake);
|
|
emitCreateNoCI(false, DelayedArgsOnly, emitFake);
|
|
};
|
|
|
|
if (Header)
|
|
OS << " // Factory methods\n";
|
|
|
|
// Emit a CreateImplicit that takes all the arguments.
|
|
emitCreates(false, true);
|
|
|
|
// Emit a CreateImplicit that takes all the non-fake arguments.
|
|
if (HasFakeArg)
|
|
emitCreates(false, false);
|
|
|
|
// Emit a CreateWithDelayedArgs that takes only the dependent argument
|
|
// expressions.
|
|
if (DelayedArgs)
|
|
emitCreates(true, false);
|
|
|
|
// Emit constructors.
|
|
auto emitCtor = [&](bool emitOpt, bool emitFake, bool emitNoArgs) {
|
|
auto shouldEmitArg = [=](const std::unique_ptr<Argument> &arg) {
|
|
if (emitNoArgs)
|
|
return false;
|
|
if (arg->isFake())
|
|
return emitFake;
|
|
if (arg->isOptional())
|
|
return emitOpt;
|
|
return true;
|
|
};
|
|
if (Header)
|
|
OS << " ";
|
|
else
|
|
OS << R.getName() << "Attr::";
|
|
OS << R.getName()
|
|
<< "Attr(ASTContext &Ctx, const AttributeCommonInfo &CommonInfo";
|
|
OS << '\n';
|
|
for (auto const &ai : Args) {
|
|
if (!shouldEmitArg(ai))
|
|
continue;
|
|
OS << " , ";
|
|
ai->writeCtorParameters(OS);
|
|
OS << "\n";
|
|
}
|
|
|
|
OS << " )";
|
|
if (Header) {
|
|
OS << ";\n";
|
|
return;
|
|
}
|
|
OS << "\n : " << SuperName << "(Ctx, CommonInfo, ";
|
|
OS << "attr::" << R.getName() << ", "
|
|
<< (R.getValueAsBit("LateParsed") ? "true" : "false");
|
|
if (Inheritable) {
|
|
OS << ", "
|
|
<< (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true"
|
|
: "false");
|
|
}
|
|
OS << ")\n";
|
|
|
|
for (auto const &ai : Args) {
|
|
OS << " , ";
|
|
if (!shouldEmitArg(ai)) {
|
|
ai->writeCtorDefaultInitializers(OS);
|
|
} else {
|
|
ai->writeCtorInitializers(OS);
|
|
}
|
|
OS << "\n";
|
|
}
|
|
if (DelayedArgs) {
|
|
OS << " , ";
|
|
DelayedArgs->writeCtorDefaultInitializers(OS);
|
|
OS << "\n";
|
|
}
|
|
|
|
OS << " {\n";
|
|
|
|
for (auto const &ai : Args) {
|
|
if (!shouldEmitArg(ai))
|
|
continue;
|
|
ai->writeCtorBody(OS);
|
|
}
|
|
OS << "}\n\n";
|
|
};
|
|
|
|
if (Header)
|
|
OS << "\n // Constructors\n";
|
|
|
|
// Emit a constructor that includes all the arguments.
|
|
// This is necessary for cloning.
|
|
emitCtor(true, true, false);
|
|
|
|
// Emit a constructor that takes all the non-fake arguments.
|
|
if (HasFakeArg)
|
|
emitCtor(true, false, false);
|
|
|
|
// Emit a constructor that takes all the non-fake, non-optional arguments.
|
|
if (HasOptArg)
|
|
emitCtor(false, false, false);
|
|
|
|
// Emit constructors that takes no arguments if none already exists.
|
|
// This is used for delaying arguments.
|
|
bool HasRequiredArgs = std::count_if(
|
|
Args.begin(), Args.end(), [=](const std::unique_ptr<Argument> &arg) {
|
|
return !arg->isFake() && !arg->isOptional();
|
|
});
|
|
if (DelayedArgs && HasRequiredArgs)
|
|
emitCtor(false, false, true);
|
|
|
|
if (Header) {
|
|
OS << '\n';
|
|
OS << " " << R.getName() << "Attr *clone(ASTContext &C) const;\n";
|
|
OS << " void printPretty(raw_ostream &OS,\n"
|
|
<< " const PrintingPolicy &Policy) const;\n";
|
|
OS << " const char *getSpelling() const;\n";
|
|
}
|
|
|
|
if (!ElideSpelling) {
|
|
assert(!SemanticToSyntacticMap.empty() && "Empty semantic mapping list");
|
|
if (Header)
|
|
OS << " Spelling getSemanticSpelling() const;\n";
|
|
else {
|
|
OS << R.getName() << "Attr::Spelling " << R.getName()
|
|
<< "Attr::getSemanticSpelling() const {\n";
|
|
WriteSemanticSpellingSwitch("getAttributeSpellingListIndex()",
|
|
SemanticToSyntacticMap, OS);
|
|
OS << "}\n";
|
|
}
|
|
}
|
|
|
|
if (Header)
|
|
writeAttrAccessorDefinition(R, OS);
|
|
|
|
for (auto const &ai : Args) {
|
|
if (Header) {
|
|
ai->writeAccessors(OS);
|
|
} else {
|
|
ai->writeAccessorDefinitions(OS);
|
|
}
|
|
OS << "\n\n";
|
|
|
|
// Don't write conversion routines for fake arguments.
|
|
if (ai->isFake()) continue;
|
|
|
|
if (ai->isEnumArg())
|
|
static_cast<const EnumArgument *>(ai.get())->writeConversion(OS,
|
|
Header);
|
|
else if (ai->isVariadicEnumArg())
|
|
static_cast<const VariadicEnumArgument *>(ai.get())->writeConversion(
|
|
OS, Header);
|
|
}
|
|
|
|
if (Header) {
|
|
if (DelayedArgs) {
|
|
DelayedArgs->writeAccessors(OS);
|
|
DelayedArgs->writeSetter(OS);
|
|
}
|
|
|
|
OS << R.getValueAsString("AdditionalMembers");
|
|
OS << "\n\n";
|
|
|
|
OS << " static bool classof(const Attr *A) { return A->getKind() == "
|
|
<< "attr::" << R.getName() << "; }\n";
|
|
|
|
OS << "};\n\n";
|
|
} else {
|
|
if (DelayedArgs)
|
|
DelayedArgs->writeAccessorDefinitions(OS);
|
|
|
|
OS << R.getName() << "Attr *" << R.getName()
|
|
<< "Attr::clone(ASTContext &C) const {\n";
|
|
OS << " auto *A = new (C) " << R.getName() << "Attr(C, *this";
|
|
for (auto const &ai : Args) {
|
|
OS << ", ";
|
|
ai->writeCloneArgs(OS);
|
|
}
|
|
OS << ");\n";
|
|
OS << " A->Inherited = Inherited;\n";
|
|
OS << " A->IsPackExpansion = IsPackExpansion;\n";
|
|
OS << " A->setImplicit(Implicit);\n";
|
|
if (DelayedArgs) {
|
|
OS << " A->setDelayedArgs(C, ";
|
|
DelayedArgs->writeCloneArgs(OS);
|
|
OS << ");\n";
|
|
}
|
|
OS << " return A;\n}\n\n";
|
|
|
|
writePrettyPrintFunction(R, Args, OS);
|
|
writeGetSpellingFunction(R, OS);
|
|
}
|
|
}
|
|
}
|
|
// Emits the class definitions for attributes.
|
|
void clang::EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Attribute classes' definitions", OS);
|
|
|
|
OS << "#ifndef LLVM_CLANG_ATTR_CLASSES_INC\n";
|
|
OS << "#define LLVM_CLANG_ATTR_CLASSES_INC\n\n";
|
|
|
|
emitAttributes(Records, OS, true);
|
|
|
|
OS << "#endif // LLVM_CLANG_ATTR_CLASSES_INC\n";
|
|
}
|
|
|
|
// Emits the class method definitions for attributes.
|
|
void clang::EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Attribute classes' member function definitions", OS);
|
|
|
|
emitAttributes(Records, OS, false);
|
|
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
|
|
// Instead of relying on virtual dispatch we just create a huge dispatch
|
|
// switch. This is both smaller and faster than virtual functions.
|
|
auto EmitFunc = [&](const char *Method) {
|
|
OS << " switch (getKind()) {\n";
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
OS << " case attr::" << R.getName() << ":\n";
|
|
OS << " return cast<" << R.getName() << "Attr>(this)->" << Method
|
|
<< ";\n";
|
|
}
|
|
OS << " }\n";
|
|
OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n";
|
|
OS << "}\n\n";
|
|
};
|
|
|
|
OS << "const char *Attr::getSpelling() const {\n";
|
|
EmitFunc("getSpelling()");
|
|
|
|
OS << "Attr *Attr::clone(ASTContext &C) const {\n";
|
|
EmitFunc("clone(C)");
|
|
|
|
OS << "void Attr::printPretty(raw_ostream &OS, "
|
|
"const PrintingPolicy &Policy) const {\n";
|
|
EmitFunc("printPretty(OS, Policy)");
|
|
}
|
|
|
|
static void emitAttrList(raw_ostream &OS, StringRef Class,
|
|
const std::vector<Record*> &AttrList) {
|
|
for (auto Cur : AttrList) {
|
|
OS << Class << "(" << Cur->getName() << ")\n";
|
|
}
|
|
}
|
|
|
|
// Determines if an attribute has a Pragma spelling.
|
|
static bool AttrHasPragmaSpelling(const Record *R) {
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*R);
|
|
return llvm::any_of(Spellings, [](const FlattenedSpelling &S) {
|
|
return S.variety() == "Pragma";
|
|
});
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct AttrClassDescriptor {
|
|
const char * const MacroName;
|
|
const char * const TableGenName;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static const AttrClassDescriptor AttrClassDescriptors[] = {
|
|
{ "ATTR", "Attr" },
|
|
{ "TYPE_ATTR", "TypeAttr" },
|
|
{ "STMT_ATTR", "StmtAttr" },
|
|
{ "DECL_OR_STMT_ATTR", "DeclOrStmtAttr" },
|
|
{ "INHERITABLE_ATTR", "InheritableAttr" },
|
|
{ "DECL_OR_TYPE_ATTR", "DeclOrTypeAttr" },
|
|
{ "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" },
|
|
{ "PARAMETER_ABI_ATTR", "ParameterABIAttr" }
|
|
};
|
|
|
|
static void emitDefaultDefine(raw_ostream &OS, StringRef name,
|
|
const char *superName) {
|
|
OS << "#ifndef " << name << "\n";
|
|
OS << "#define " << name << "(NAME) ";
|
|
if (superName) OS << superName << "(NAME)";
|
|
OS << "\n#endif\n\n";
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A class of attributes.
|
|
struct AttrClass {
|
|
const AttrClassDescriptor &Descriptor;
|
|
Record *TheRecord;
|
|
AttrClass *SuperClass = nullptr;
|
|
std::vector<AttrClass*> SubClasses;
|
|
std::vector<Record*> Attrs;
|
|
|
|
AttrClass(const AttrClassDescriptor &Descriptor, Record *R)
|
|
: Descriptor(Descriptor), TheRecord(R) {}
|
|
|
|
void emitDefaultDefines(raw_ostream &OS) const {
|
|
// Default the macro unless this is a root class (i.e. Attr).
|
|
if (SuperClass) {
|
|
emitDefaultDefine(OS, Descriptor.MacroName,
|
|
SuperClass->Descriptor.MacroName);
|
|
}
|
|
}
|
|
|
|
void emitUndefs(raw_ostream &OS) const {
|
|
OS << "#undef " << Descriptor.MacroName << "\n";
|
|
}
|
|
|
|
void emitAttrList(raw_ostream &OS) const {
|
|
for (auto SubClass : SubClasses) {
|
|
SubClass->emitAttrList(OS);
|
|
}
|
|
|
|
::emitAttrList(OS, Descriptor.MacroName, Attrs);
|
|
}
|
|
|
|
void classifyAttrOnRoot(Record *Attr) {
|
|
bool result = classifyAttr(Attr);
|
|
assert(result && "failed to classify on root"); (void) result;
|
|
}
|
|
|
|
void emitAttrRange(raw_ostream &OS) const {
|
|
OS << "ATTR_RANGE(" << Descriptor.TableGenName
|
|
<< ", " << getFirstAttr()->getName()
|
|
<< ", " << getLastAttr()->getName() << ")\n";
|
|
}
|
|
|
|
private:
|
|
bool classifyAttr(Record *Attr) {
|
|
// Check all the subclasses.
|
|
for (auto SubClass : SubClasses) {
|
|
if (SubClass->classifyAttr(Attr))
|
|
return true;
|
|
}
|
|
|
|
// It's not more specific than this class, but it might still belong here.
|
|
if (Attr->isSubClassOf(TheRecord)) {
|
|
Attrs.push_back(Attr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Record *getFirstAttr() const {
|
|
if (!SubClasses.empty())
|
|
return SubClasses.front()->getFirstAttr();
|
|
return Attrs.front();
|
|
}
|
|
|
|
Record *getLastAttr() const {
|
|
if (!Attrs.empty())
|
|
return Attrs.back();
|
|
return SubClasses.back()->getLastAttr();
|
|
}
|
|
};
|
|
|
|
/// The entire hierarchy of attribute classes.
|
|
class AttrClassHierarchy {
|
|
std::vector<std::unique_ptr<AttrClass>> Classes;
|
|
|
|
public:
|
|
AttrClassHierarchy(RecordKeeper &Records) {
|
|
// Find records for all the classes.
|
|
for (auto &Descriptor : AttrClassDescriptors) {
|
|
Record *ClassRecord = Records.getClass(Descriptor.TableGenName);
|
|
AttrClass *Class = new AttrClass(Descriptor, ClassRecord);
|
|
Classes.emplace_back(Class);
|
|
}
|
|
|
|
// Link up the hierarchy.
|
|
for (auto &Class : Classes) {
|
|
if (AttrClass *SuperClass = findSuperClass(Class->TheRecord)) {
|
|
Class->SuperClass = SuperClass;
|
|
SuperClass->SubClasses.push_back(Class.get());
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
for (auto i = Classes.begin(), e = Classes.end(); i != e; ++i) {
|
|
assert((i == Classes.begin()) == ((*i)->SuperClass == nullptr) &&
|
|
"only the first class should be a root class!");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void emitDefaultDefines(raw_ostream &OS) const {
|
|
for (auto &Class : Classes) {
|
|
Class->emitDefaultDefines(OS);
|
|
}
|
|
}
|
|
|
|
void emitUndefs(raw_ostream &OS) const {
|
|
for (auto &Class : Classes) {
|
|
Class->emitUndefs(OS);
|
|
}
|
|
}
|
|
|
|
void emitAttrLists(raw_ostream &OS) const {
|
|
// Just start from the root class.
|
|
Classes[0]->emitAttrList(OS);
|
|
}
|
|
|
|
void emitAttrRanges(raw_ostream &OS) const {
|
|
for (auto &Class : Classes)
|
|
Class->emitAttrRange(OS);
|
|
}
|
|
|
|
void classifyAttr(Record *Attr) {
|
|
// Add the attribute to the root class.
|
|
Classes[0]->classifyAttrOnRoot(Attr);
|
|
}
|
|
|
|
private:
|
|
AttrClass *findClassByRecord(Record *R) const {
|
|
for (auto &Class : Classes) {
|
|
if (Class->TheRecord == R)
|
|
return Class.get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
AttrClass *findSuperClass(Record *R) const {
|
|
// TableGen flattens the superclass list, so we just need to walk it
|
|
// in reverse.
|
|
auto SuperClasses = R->getSuperClasses();
|
|
for (signed i = 0, e = SuperClasses.size(); i != e; ++i) {
|
|
auto SuperClass = findClassByRecord(SuperClasses[e - i - 1].first);
|
|
if (SuperClass) return SuperClass;
|
|
}
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
namespace clang {
|
|
|
|
// Emits the enumeration list for attributes.
|
|
void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("List of all attributes that Clang recognizes", OS);
|
|
|
|
AttrClassHierarchy Hierarchy(Records);
|
|
|
|
// Add defaulting macro definitions.
|
|
Hierarchy.emitDefaultDefines(OS);
|
|
emitDefaultDefine(OS, "PRAGMA_SPELLING_ATTR", nullptr);
|
|
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
std::vector<Record *> PragmaAttrs;
|
|
for (auto *Attr : Attrs) {
|
|
if (!Attr->getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
// Add the attribute to the ad-hoc groups.
|
|
if (AttrHasPragmaSpelling(Attr))
|
|
PragmaAttrs.push_back(Attr);
|
|
|
|
// Place it in the hierarchy.
|
|
Hierarchy.classifyAttr(Attr);
|
|
}
|
|
|
|
// Emit the main attribute list.
|
|
Hierarchy.emitAttrLists(OS);
|
|
|
|
// Emit the ad hoc groups.
|
|
emitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs);
|
|
|
|
// Emit the attribute ranges.
|
|
OS << "#ifdef ATTR_RANGE\n";
|
|
Hierarchy.emitAttrRanges(OS);
|
|
OS << "#undef ATTR_RANGE\n";
|
|
OS << "#endif\n";
|
|
|
|
Hierarchy.emitUndefs(OS);
|
|
OS << "#undef PRAGMA_SPELLING_ATTR\n";
|
|
}
|
|
|
|
// Emits the enumeration list for attributes.
|
|
void EmitClangAttrSubjectMatchRuleList(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader(
|
|
"List of all attribute subject matching rules that Clang recognizes", OS);
|
|
PragmaClangAttributeSupport &PragmaAttributeSupport =
|
|
getPragmaAttributeSupport(Records);
|
|
emitDefaultDefine(OS, "ATTR_MATCH_RULE", nullptr);
|
|
PragmaAttributeSupport.emitMatchRuleList(OS);
|
|
OS << "#undef ATTR_MATCH_RULE\n";
|
|
}
|
|
|
|
// Emits the code to read an attribute from a precompiled header.
|
|
void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Attribute deserialization code", OS);
|
|
|
|
Record *InhClass = Records.getClass("InheritableAttr");
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"),
|
|
ArgRecords;
|
|
std::vector<std::unique_ptr<Argument>> Args;
|
|
std::unique_ptr<VariadicExprArgument> DelayedArgs;
|
|
|
|
OS << " switch (Kind) {\n";
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
OS << " case attr::" << R.getName() << ": {\n";
|
|
if (R.isSubClassOf(InhClass))
|
|
OS << " bool isInherited = Record.readInt();\n";
|
|
OS << " bool isImplicit = Record.readInt();\n";
|
|
OS << " bool isPackExpansion = Record.readInt();\n";
|
|
DelayedArgs = nullptr;
|
|
if (Attr->getValueAsBit("AcceptsExprPack")) {
|
|
DelayedArgs =
|
|
std::make_unique<VariadicExprArgument>("DelayedArgs", R.getName());
|
|
DelayedArgs->writePCHReadDecls(OS);
|
|
}
|
|
ArgRecords = R.getValueAsListOfDefs("Args");
|
|
Args.clear();
|
|
for (const auto *Arg : ArgRecords) {
|
|
Args.emplace_back(createArgument(*Arg, R.getName()));
|
|
Args.back()->writePCHReadDecls(OS);
|
|
}
|
|
OS << " New = new (Context) " << R.getName() << "Attr(Context, Info";
|
|
for (auto const &ri : Args) {
|
|
OS << ", ";
|
|
ri->writePCHReadArgs(OS);
|
|
}
|
|
OS << ");\n";
|
|
if (R.isSubClassOf(InhClass))
|
|
OS << " cast<InheritableAttr>(New)->setInherited(isInherited);\n";
|
|
OS << " New->setImplicit(isImplicit);\n";
|
|
OS << " New->setPackExpansion(isPackExpansion);\n";
|
|
if (DelayedArgs) {
|
|
OS << " cast<" << R.getName()
|
|
<< "Attr>(New)->setDelayedArgs(Context, ";
|
|
DelayedArgs->writePCHReadArgs(OS);
|
|
OS << ");\n";
|
|
}
|
|
OS << " break;\n";
|
|
OS << " }\n";
|
|
}
|
|
OS << " }\n";
|
|
}
|
|
|
|
// Emits the code to write an attribute to a precompiled header.
|
|
void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Attribute serialization code", OS);
|
|
|
|
Record *InhClass = Records.getClass("InheritableAttr");
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args;
|
|
|
|
OS << " switch (A->getKind()) {\n";
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
OS << " case attr::" << R.getName() << ": {\n";
|
|
Args = R.getValueAsListOfDefs("Args");
|
|
if (R.isSubClassOf(InhClass) || !Args.empty())
|
|
OS << " const auto *SA = cast<" << R.getName()
|
|
<< "Attr>(A);\n";
|
|
if (R.isSubClassOf(InhClass))
|
|
OS << " Record.push_back(SA->isInherited());\n";
|
|
OS << " Record.push_back(A->isImplicit());\n";
|
|
OS << " Record.push_back(A->isPackExpansion());\n";
|
|
if (Attr->getValueAsBit("AcceptsExprPack"))
|
|
VariadicExprArgument("DelayedArgs", R.getName()).writePCHWrite(OS);
|
|
|
|
for (const auto *Arg : Args)
|
|
createArgument(*Arg, R.getName())->writePCHWrite(OS);
|
|
OS << " break;\n";
|
|
OS << " }\n";
|
|
}
|
|
OS << " }\n";
|
|
}
|
|
|
|
// Helper function for GenerateTargetSpecificAttrChecks that alters the 'Test'
|
|
// parameter with only a single check type, if applicable.
|
|
static bool GenerateTargetSpecificAttrCheck(const Record *R, std::string &Test,
|
|
std::string *FnName,
|
|
StringRef ListName,
|
|
StringRef CheckAgainst,
|
|
StringRef Scope) {
|
|
if (!R->isValueUnset(ListName)) {
|
|
Test += " && (";
|
|
std::vector<StringRef> Items = R->getValueAsListOfStrings(ListName);
|
|
for (auto I = Items.begin(), E = Items.end(); I != E; ++I) {
|
|
StringRef Part = *I;
|
|
Test += CheckAgainst;
|
|
Test += " == ";
|
|
Test += Scope;
|
|
Test += Part;
|
|
if (I + 1 != E)
|
|
Test += " || ";
|
|
if (FnName)
|
|
*FnName += Part;
|
|
}
|
|
Test += ")";
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Generate a conditional expression to check if the current target satisfies
|
|
// the conditions for a TargetSpecificAttr record, and append the code for
|
|
// those checks to the Test string. If the FnName string pointer is non-null,
|
|
// append a unique suffix to distinguish this set of target checks from other
|
|
// TargetSpecificAttr records.
|
|
static bool GenerateTargetSpecificAttrChecks(const Record *R,
|
|
std::vector<StringRef> &Arches,
|
|
std::string &Test,
|
|
std::string *FnName) {
|
|
bool AnyTargetChecks = false;
|
|
|
|
// It is assumed that there will be an llvm::Triple object
|
|
// named "T" and a TargetInfo object named "Target" within
|
|
// scope that can be used to determine whether the attribute exists in
|
|
// a given target.
|
|
Test += "true";
|
|
// If one or more architectures is specified, check those. Arches are handled
|
|
// differently because GenerateTargetRequirements needs to combine the list
|
|
// with ParseKind.
|
|
if (!Arches.empty()) {
|
|
AnyTargetChecks = true;
|
|
Test += " && (";
|
|
for (auto I = Arches.begin(), E = Arches.end(); I != E; ++I) {
|
|
StringRef Part = *I;
|
|
Test += "T.getArch() == llvm::Triple::";
|
|
Test += Part;
|
|
if (I + 1 != E)
|
|
Test += " || ";
|
|
if (FnName)
|
|
*FnName += Part;
|
|
}
|
|
Test += ")";
|
|
}
|
|
|
|
// If the attribute is specific to particular OSes, check those.
|
|
AnyTargetChecks |= GenerateTargetSpecificAttrCheck(
|
|
R, Test, FnName, "OSes", "T.getOS()", "llvm::Triple::");
|
|
|
|
// If one or more object formats is specified, check those.
|
|
AnyTargetChecks |=
|
|
GenerateTargetSpecificAttrCheck(R, Test, FnName, "ObjectFormats",
|
|
"T.getObjectFormat()", "llvm::Triple::");
|
|
|
|
// If custom code is specified, emit it.
|
|
StringRef Code = R->getValueAsString("CustomCode");
|
|
if (!Code.empty()) {
|
|
AnyTargetChecks = true;
|
|
Test += " && (";
|
|
Test += Code;
|
|
Test += ")";
|
|
}
|
|
|
|
return AnyTargetChecks;
|
|
}
|
|
|
|
static void GenerateHasAttrSpellingStringSwitch(
|
|
const std::vector<Record *> &Attrs, raw_ostream &OS,
|
|
const std::string &Variety = "", const std::string &Scope = "") {
|
|
for (const auto *Attr : Attrs) {
|
|
// C++11-style attributes have specific version information associated with
|
|
// them. If the attribute has no scope, the version information must not
|
|
// have the default value (1), as that's incorrect. Instead, the unscoped
|
|
// attribute version information should be taken from the SD-6 standing
|
|
// document, which can be found at:
|
|
// https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
|
|
//
|
|
// C2x-style attributes have the same kind of version information
|
|
// associated with them. The unscoped attribute version information should
|
|
// be taken from the specification of the attribute in the C Standard.
|
|
int Version = 1;
|
|
|
|
if (Variety == "CXX11" || Variety == "C2x") {
|
|
std::vector<Record *> Spellings = Attr->getValueAsListOfDefs("Spellings");
|
|
for (const auto &Spelling : Spellings) {
|
|
if (Spelling->getValueAsString("Variety") == Variety) {
|
|
Version = static_cast<int>(Spelling->getValueAsInt("Version"));
|
|
if (Scope.empty() && Version == 1)
|
|
PrintError(Spelling->getLoc(), "Standard attributes must have "
|
|
"valid version information.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string Test;
|
|
if (Attr->isSubClassOf("TargetSpecificAttr")) {
|
|
const Record *R = Attr->getValueAsDef("Target");
|
|
std::vector<StringRef> Arches = R->getValueAsListOfStrings("Arches");
|
|
GenerateTargetSpecificAttrChecks(R, Arches, Test, nullptr);
|
|
|
|
// If this is the C++11 variety, also add in the LangOpts test.
|
|
if (Variety == "CXX11")
|
|
Test += " && LangOpts.CPlusPlus11";
|
|
else if (Variety == "C2x")
|
|
Test += " && LangOpts.DoubleSquareBracketAttributes";
|
|
} else if (Variety == "CXX11")
|
|
// C++11 mode should be checked against LangOpts, which is presumed to be
|
|
// present in the caller.
|
|
Test = "LangOpts.CPlusPlus11";
|
|
else if (Variety == "C2x")
|
|
Test = "LangOpts.DoubleSquareBracketAttributes";
|
|
|
|
std::string TestStr =
|
|
!Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : "1";
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
|
|
for (const auto &S : Spellings)
|
|
if (Variety.empty() || (Variety == S.variety() &&
|
|
(Scope.empty() || Scope == S.nameSpace())))
|
|
OS << " .Case(\"" << S.name() << "\", " << TestStr << ")\n";
|
|
}
|
|
OS << " .Default(0);\n";
|
|
}
|
|
|
|
// Emits the list of spellings for attributes.
|
|
void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Code to implement the __has_attribute logic", OS);
|
|
|
|
// Separate all of the attributes out into four group: generic, C++11, GNU,
|
|
// and declspecs. Then generate a big switch statement for each of them.
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
std::vector<Record *> Declspec, Microsoft, GNU, Pragma, HLSLSemantic;
|
|
std::map<std::string, std::vector<Record *>> CXX, C2x;
|
|
|
|
// Walk over the list of all attributes, and split them out based on the
|
|
// spelling variety.
|
|
for (auto *R : Attrs) {
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*R);
|
|
for (const auto &SI : Spellings) {
|
|
const std::string &Variety = SI.variety();
|
|
if (Variety == "GNU")
|
|
GNU.push_back(R);
|
|
else if (Variety == "Declspec")
|
|
Declspec.push_back(R);
|
|
else if (Variety == "Microsoft")
|
|
Microsoft.push_back(R);
|
|
else if (Variety == "CXX11")
|
|
CXX[SI.nameSpace()].push_back(R);
|
|
else if (Variety == "C2x")
|
|
C2x[SI.nameSpace()].push_back(R);
|
|
else if (Variety == "Pragma")
|
|
Pragma.push_back(R);
|
|
else if (Variety == "HLSLSemantic")
|
|
HLSLSemantic.push_back(R);
|
|
}
|
|
}
|
|
|
|
OS << "const llvm::Triple &T = Target.getTriple();\n";
|
|
OS << "switch (Syntax) {\n";
|
|
OS << "case AttributeCommonInfo::Syntax::AS_GNU:\n";
|
|
OS << " return llvm::StringSwitch<int>(Name)\n";
|
|
GenerateHasAttrSpellingStringSwitch(GNU, OS, "GNU");
|
|
OS << "case AttributeCommonInfo::Syntax::AS_Declspec:\n";
|
|
OS << " return llvm::StringSwitch<int>(Name)\n";
|
|
GenerateHasAttrSpellingStringSwitch(Declspec, OS, "Declspec");
|
|
OS << "case AttributeCommonInfo::Syntax::AS_Microsoft:\n";
|
|
OS << " return llvm::StringSwitch<int>(Name)\n";
|
|
GenerateHasAttrSpellingStringSwitch(Microsoft, OS, "Microsoft");
|
|
OS << "case AttributeCommonInfo::Syntax::AS_Pragma:\n";
|
|
OS << " return llvm::StringSwitch<int>(Name)\n";
|
|
GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma");
|
|
OS << "case AttributeCommonInfo::Syntax::AS_HLSLSemantic:\n";
|
|
OS << " return llvm::StringSwitch<int>(Name)\n";
|
|
GenerateHasAttrSpellingStringSwitch(HLSLSemantic, OS, "HLSLSemantic");
|
|
auto fn = [&OS](const char *Spelling,
|
|
const std::map<std::string, std::vector<Record *>> &List) {
|
|
OS << "case AttributeCommonInfo::Syntax::AS_" << Spelling << ": {\n";
|
|
// C++11-style attributes are further split out based on the Scope.
|
|
for (auto I = List.cbegin(), E = List.cend(); I != E; ++I) {
|
|
if (I != List.cbegin())
|
|
OS << " else ";
|
|
if (I->first.empty())
|
|
OS << "if (ScopeName == \"\") {\n";
|
|
else
|
|
OS << "if (ScopeName == \"" << I->first << "\") {\n";
|
|
OS << " return llvm::StringSwitch<int>(Name)\n";
|
|
GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first);
|
|
OS << "}";
|
|
}
|
|
OS << "\n} break;\n";
|
|
};
|
|
fn("CXX11", CXX);
|
|
fn("C2x", C2x);
|
|
OS << "case AttributeCommonInfo::Syntax::AS_Keyword:\n";
|
|
OS << "case AttributeCommonInfo::Syntax::AS_ContextSensitiveKeyword:\n";
|
|
OS << " llvm_unreachable(\"hasAttribute not supported for keyword\");\n";
|
|
OS << " return 0;\n";
|
|
|
|
OS << "}\n";
|
|
}
|
|
|
|
void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Code to translate different attribute spellings "
|
|
"into internal identifiers", OS);
|
|
|
|
OS << " switch (getParsedKind()) {\n";
|
|
OS << " case IgnoredAttribute:\n";
|
|
OS << " case UnknownAttribute:\n";
|
|
OS << " case NoSemaHandlerAttribute:\n";
|
|
OS << " llvm_unreachable(\"Ignored/unknown shouldn't get here\");\n";
|
|
|
|
ParsedAttrMap Attrs = getParsedAttrList(Records);
|
|
for (const auto &I : Attrs) {
|
|
const Record &R = *I.second;
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
|
|
OS << " case AT_" << I.first << ": {\n";
|
|
for (unsigned I = 0; I < Spellings.size(); ++ I) {
|
|
OS << " if (Name == \"" << Spellings[I].name() << "\" && "
|
|
<< "getSyntax() == AttributeCommonInfo::AS_" << Spellings[I].variety()
|
|
<< " && Scope == \"" << Spellings[I].nameSpace() << "\")\n"
|
|
<< " return " << I << ";\n";
|
|
}
|
|
|
|
OS << " break;\n";
|
|
OS << " }\n";
|
|
}
|
|
|
|
OS << " }\n";
|
|
OS << " return 0;\n";
|
|
}
|
|
|
|
// Emits code used by RecursiveASTVisitor to visit attributes
|
|
void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Used by RecursiveASTVisitor to visit attributes.", OS);
|
|
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
|
|
// Write method declarations for Traverse* methods.
|
|
// We emit this here because we only generate methods for attributes that
|
|
// are declared as ASTNodes.
|
|
OS << "#ifdef ATTR_VISITOR_DECLS_ONLY\n\n";
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
OS << " bool Traverse"
|
|
<< R.getName() << "Attr(" << R.getName() << "Attr *A);\n";
|
|
OS << " bool Visit"
|
|
<< R.getName() << "Attr(" << R.getName() << "Attr *A) {\n"
|
|
<< " return true; \n"
|
|
<< " }\n";
|
|
}
|
|
OS << "\n#else // ATTR_VISITOR_DECLS_ONLY\n\n";
|
|
|
|
// Write individual Traverse* methods for each attribute class.
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
OS << "template <typename Derived>\n"
|
|
<< "bool VISITORCLASS<Derived>::Traverse"
|
|
<< R.getName() << "Attr(" << R.getName() << "Attr *A) {\n"
|
|
<< " if (!getDerived().VisitAttr(A))\n"
|
|
<< " return false;\n"
|
|
<< " if (!getDerived().Visit" << R.getName() << "Attr(A))\n"
|
|
<< " return false;\n";
|
|
|
|
std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args");
|
|
for (const auto *Arg : ArgRecords)
|
|
createArgument(*Arg, R.getName())->writeASTVisitorTraversal(OS);
|
|
|
|
if (Attr->getValueAsBit("AcceptsExprPack"))
|
|
VariadicExprArgument("DelayedArgs", R.getName())
|
|
.writeASTVisitorTraversal(OS);
|
|
|
|
OS << " return true;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
// Write generic Traverse routine
|
|
OS << "template <typename Derived>\n"
|
|
<< "bool VISITORCLASS<Derived>::TraverseAttr(Attr *A) {\n"
|
|
<< " if (!A)\n"
|
|
<< " return true;\n"
|
|
<< "\n"
|
|
<< " switch (A->getKind()) {\n";
|
|
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
OS << " case attr::" << R.getName() << ":\n"
|
|
<< " return getDerived().Traverse" << R.getName() << "Attr("
|
|
<< "cast<" << R.getName() << "Attr>(A));\n";
|
|
}
|
|
OS << " }\n"; // end switch
|
|
OS << " llvm_unreachable(\"bad attribute kind\");\n";
|
|
OS << "}\n"; // end function
|
|
OS << "#endif // ATTR_VISITOR_DECLS_ONLY\n";
|
|
}
|
|
|
|
void EmitClangAttrTemplateInstantiateHelper(const std::vector<Record *> &Attrs,
|
|
raw_ostream &OS,
|
|
bool AppliesToDecl) {
|
|
|
|
OS << " switch (At->getKind()) {\n";
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
OS << " case attr::" << R.getName() << ": {\n";
|
|
bool ShouldClone = R.getValueAsBit("Clone") &&
|
|
(!AppliesToDecl ||
|
|
R.getValueAsBit("MeaningfulToClassTemplateDefinition"));
|
|
|
|
if (!ShouldClone) {
|
|
OS << " return nullptr;\n";
|
|
OS << " }\n";
|
|
continue;
|
|
}
|
|
|
|
OS << " const auto *A = cast<"
|
|
<< R.getName() << "Attr>(At);\n";
|
|
bool TDependent = R.getValueAsBit("TemplateDependent");
|
|
|
|
if (!TDependent) {
|
|
OS << " return A->clone(C);\n";
|
|
OS << " }\n";
|
|
continue;
|
|
}
|
|
|
|
std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args");
|
|
std::vector<std::unique_ptr<Argument>> Args;
|
|
Args.reserve(ArgRecords.size());
|
|
|
|
for (const auto *ArgRecord : ArgRecords)
|
|
Args.emplace_back(createArgument(*ArgRecord, R.getName()));
|
|
|
|
for (auto const &ai : Args)
|
|
ai->writeTemplateInstantiation(OS);
|
|
|
|
OS << " return new (C) " << R.getName() << "Attr(C, *A";
|
|
for (auto const &ai : Args) {
|
|
OS << ", ";
|
|
ai->writeTemplateInstantiationArgs(OS);
|
|
}
|
|
OS << ");\n"
|
|
<< " }\n";
|
|
}
|
|
OS << " } // end switch\n"
|
|
<< " llvm_unreachable(\"Unknown attribute!\");\n"
|
|
<< " return nullptr;\n";
|
|
}
|
|
|
|
// Emits code to instantiate dependent attributes on templates.
|
|
void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Template instantiation code for attributes", OS);
|
|
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
|
|
OS << "namespace clang {\n"
|
|
<< "namespace sema {\n\n"
|
|
<< "Attr *instantiateTemplateAttribute(const Attr *At, ASTContext &C, "
|
|
<< "Sema &S,\n"
|
|
<< " const MultiLevelTemplateArgumentList &TemplateArgs) {\n";
|
|
EmitClangAttrTemplateInstantiateHelper(Attrs, OS, /*AppliesToDecl*/false);
|
|
OS << "}\n\n"
|
|
<< "Attr *instantiateTemplateAttributeForDecl(const Attr *At,\n"
|
|
<< " ASTContext &C, Sema &S,\n"
|
|
<< " const MultiLevelTemplateArgumentList &TemplateArgs) {\n";
|
|
EmitClangAttrTemplateInstantiateHelper(Attrs, OS, /*AppliesToDecl*/true);
|
|
OS << "}\n\n"
|
|
<< "} // end namespace sema\n"
|
|
<< "} // end namespace clang\n";
|
|
}
|
|
|
|
// Emits the list of parsed attributes.
|
|
void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("List of all attributes that Clang recognizes", OS);
|
|
|
|
OS << "#ifndef PARSED_ATTR\n";
|
|
OS << "#define PARSED_ATTR(NAME) NAME\n";
|
|
OS << "#endif\n\n";
|
|
|
|
ParsedAttrMap Names = getParsedAttrList(Records);
|
|
for (const auto &I : Names) {
|
|
OS << "PARSED_ATTR(" << I.first << ")\n";
|
|
}
|
|
}
|
|
|
|
static bool isArgVariadic(const Record &R, StringRef AttrName) {
|
|
return createArgument(R, AttrName)->isVariadic();
|
|
}
|
|
|
|
static void emitArgInfo(const Record &R, raw_ostream &OS) {
|
|
// This function will count the number of arguments specified for the
|
|
// attribute and emit the number of required arguments followed by the
|
|
// number of optional arguments.
|
|
std::vector<Record *> Args = R.getValueAsListOfDefs("Args");
|
|
unsigned ArgCount = 0, OptCount = 0, ArgMemberCount = 0;
|
|
bool HasVariadic = false;
|
|
for (const auto *Arg : Args) {
|
|
// If the arg is fake, it's the user's job to supply it: general parsing
|
|
// logic shouldn't need to know anything about it.
|
|
if (Arg->getValueAsBit("Fake"))
|
|
continue;
|
|
Arg->getValueAsBit("Optional") ? ++OptCount : ++ArgCount;
|
|
++ArgMemberCount;
|
|
if (!HasVariadic && isArgVariadic(*Arg, R.getName()))
|
|
HasVariadic = true;
|
|
}
|
|
|
|
// If there is a variadic argument, we will set the optional argument count
|
|
// to its largest value. Since it's currently a 4-bit number, we set it to 15.
|
|
OS << " /*NumArgs=*/" << ArgCount << ",\n";
|
|
OS << " /*OptArgs=*/" << (HasVariadic ? 15 : OptCount) << ",\n";
|
|
OS << " /*NumArgMembers=*/" << ArgMemberCount << ",\n";
|
|
}
|
|
|
|
static std::string GetDiagnosticSpelling(const Record &R) {
|
|
std::string Ret = std::string(R.getValueAsString("DiagSpelling"));
|
|
if (!Ret.empty())
|
|
return Ret;
|
|
|
|
// If we couldn't find the DiagSpelling in this object, we can check to see
|
|
// if the object is one that has a base, and if it is, loop up to the Base
|
|
// member recursively.
|
|
if (auto Base = R.getValueAsOptionalDef(BaseFieldName))
|
|
return GetDiagnosticSpelling(*Base);
|
|
|
|
return "";
|
|
}
|
|
|
|
static std::string CalculateDiagnostic(const Record &S) {
|
|
// If the SubjectList object has a custom diagnostic associated with it,
|
|
// return that directly.
|
|
const StringRef CustomDiag = S.getValueAsString("CustomDiag");
|
|
if (!CustomDiag.empty())
|
|
return ("\"" + Twine(CustomDiag) + "\"").str();
|
|
|
|
std::vector<std::string> DiagList;
|
|
std::vector<Record *> Subjects = S.getValueAsListOfDefs("Subjects");
|
|
for (const auto *Subject : Subjects) {
|
|
const Record &R = *Subject;
|
|
// Get the diagnostic text from the Decl or Stmt node given.
|
|
std::string V = GetDiagnosticSpelling(R);
|
|
if (V.empty()) {
|
|
PrintError(R.getLoc(),
|
|
"Could not determine diagnostic spelling for the node: " +
|
|
R.getName() + "; please add one to DeclNodes.td");
|
|
} else {
|
|
// The node may contain a list of elements itself, so split the elements
|
|
// by a comma, and trim any whitespace.
|
|
SmallVector<StringRef, 2> Frags;
|
|
llvm::SplitString(V, Frags, ",");
|
|
for (auto Str : Frags) {
|
|
DiagList.push_back(std::string(Str.trim()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DiagList.empty()) {
|
|
PrintFatalError(S.getLoc(),
|
|
"Could not deduce diagnostic argument for Attr subjects");
|
|
return "";
|
|
}
|
|
|
|
// FIXME: this is not particularly good for localization purposes and ideally
|
|
// should be part of the diagnostics engine itself with some sort of list
|
|
// specifier.
|
|
|
|
// A single member of the list can be returned directly.
|
|
if (DiagList.size() == 1)
|
|
return '"' + DiagList.front() + '"';
|
|
|
|
if (DiagList.size() == 2)
|
|
return '"' + DiagList[0] + " and " + DiagList[1] + '"';
|
|
|
|
// If there are more than two in the list, we serialize the first N - 1
|
|
// elements with a comma. This leaves the string in the state: foo, bar,
|
|
// baz (but misses quux). We can then add ", and " for the last element
|
|
// manually.
|
|
std::string Diag = llvm::join(DiagList.begin(), DiagList.end() - 1, ", ");
|
|
return '"' + Diag + ", and " + *(DiagList.end() - 1) + '"';
|
|
}
|
|
|
|
static std::string GetSubjectWithSuffix(const Record *R) {
|
|
const std::string &B = std::string(R->getName());
|
|
if (B == "DeclBase")
|
|
return "Decl";
|
|
return B + "Decl";
|
|
}
|
|
|
|
static std::string functionNameForCustomAppertainsTo(const Record &Subject) {
|
|
return "is" + Subject.getName().str();
|
|
}
|
|
|
|
static void GenerateCustomAppertainsTo(const Record &Subject, raw_ostream &OS) {
|
|
std::string FnName = functionNameForCustomAppertainsTo(Subject);
|
|
|
|
// If this code has already been generated, we don't need to do anything.
|
|
static std::set<std::string> CustomSubjectSet;
|
|
auto I = CustomSubjectSet.find(FnName);
|
|
if (I != CustomSubjectSet.end())
|
|
return;
|
|
|
|
// This only works with non-root Decls.
|
|
Record *Base = Subject.getValueAsDef(BaseFieldName);
|
|
|
|
// Not currently support custom subjects within custom subjects.
|
|
if (Base->isSubClassOf("SubsetSubject")) {
|
|
PrintFatalError(Subject.getLoc(),
|
|
"SubsetSubjects within SubsetSubjects is not supported");
|
|
return;
|
|
}
|
|
|
|
OS << "static bool " << FnName << "(const Decl *D) {\n";
|
|
OS << " if (const auto *S = dyn_cast<";
|
|
OS << GetSubjectWithSuffix(Base);
|
|
OS << ">(D))\n";
|
|
OS << " return " << Subject.getValueAsString("CheckCode") << ";\n";
|
|
OS << " return false;\n";
|
|
OS << "}\n\n";
|
|
|
|
CustomSubjectSet.insert(FnName);
|
|
}
|
|
|
|
static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) {
|
|
// If the attribute does not contain a Subjects definition, then use the
|
|
// default appertainsTo logic.
|
|
if (Attr.isValueUnset("Subjects"))
|
|
return;
|
|
|
|
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
|
|
std::vector<Record *> Subjects = SubjectObj->getValueAsListOfDefs("Subjects");
|
|
|
|
// If the list of subjects is empty, it is assumed that the attribute
|
|
// appertains to everything.
|
|
if (Subjects.empty())
|
|
return;
|
|
|
|
bool Warn = SubjectObj->getValueAsDef("Diag")->getValueAsBit("Warn");
|
|
|
|
// Split the subjects into declaration subjects and statement subjects.
|
|
// FIXME: subset subjects are added to the declaration list until there are
|
|
// enough statement attributes with custom subject needs to warrant
|
|
// the implementation effort.
|
|
std::vector<Record *> DeclSubjects, StmtSubjects;
|
|
llvm::copy_if(
|
|
Subjects, std::back_inserter(DeclSubjects), [](const Record *R) {
|
|
return R->isSubClassOf("SubsetSubject") || !R->isSubClassOf("StmtNode");
|
|
});
|
|
llvm::copy_if(Subjects, std::back_inserter(StmtSubjects),
|
|
[](const Record *R) { return R->isSubClassOf("StmtNode"); });
|
|
|
|
// We should have sorted all of the subjects into two lists.
|
|
// FIXME: this assertion will be wrong if we ever add type attribute subjects.
|
|
assert(DeclSubjects.size() + StmtSubjects.size() == Subjects.size());
|
|
|
|
if (DeclSubjects.empty()) {
|
|
// If there are no decl subjects but there are stmt subjects, diagnose
|
|
// trying to apply a statement attribute to a declaration.
|
|
if (!StmtSubjects.empty()) {
|
|
OS << "bool diagAppertainsToDecl(Sema &S, const ParsedAttr &AL, ";
|
|
OS << "const Decl *D) const override {\n";
|
|
OS << " S.Diag(AL.getLoc(), diag::err_attribute_invalid_on_decl)\n";
|
|
OS << " << AL << D->getLocation();\n";
|
|
OS << " return false;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
} else {
|
|
// Otherwise, generate an appertainsTo check specific to this attribute
|
|
// which checks all of the given subjects against the Decl passed in.
|
|
OS << "bool diagAppertainsToDecl(Sema &S, ";
|
|
OS << "const ParsedAttr &Attr, const Decl *D) const override {\n";
|
|
OS << " if (";
|
|
for (auto I = DeclSubjects.begin(), E = DeclSubjects.end(); I != E; ++I) {
|
|
// If the subject has custom code associated with it, use the generated
|
|
// function for it. The function cannot be inlined into this check (yet)
|
|
// because it requires the subject to be of a specific type, and were that
|
|
// information inlined here, it would not support an attribute with
|
|
// multiple custom subjects.
|
|
if ((*I)->isSubClassOf("SubsetSubject"))
|
|
OS << "!" << functionNameForCustomAppertainsTo(**I) << "(D)";
|
|
else
|
|
OS << "!isa<" << GetSubjectWithSuffix(*I) << ">(D)";
|
|
|
|
if (I + 1 != E)
|
|
OS << " && ";
|
|
}
|
|
OS << ") {\n";
|
|
OS << " S.Diag(Attr.getLoc(), diag::";
|
|
OS << (Warn ? "warn_attribute_wrong_decl_type_str"
|
|
: "err_attribute_wrong_decl_type_str");
|
|
OS << ")\n";
|
|
OS << " << Attr << ";
|
|
OS << CalculateDiagnostic(*SubjectObj) << ";\n";
|
|
OS << " return false;\n";
|
|
OS << " }\n";
|
|
OS << " return true;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
if (StmtSubjects.empty()) {
|
|
// If there are no stmt subjects but there are decl subjects, diagnose
|
|
// trying to apply a declaration attribute to a statement.
|
|
if (!DeclSubjects.empty()) {
|
|
OS << "bool diagAppertainsToStmt(Sema &S, const ParsedAttr &AL, ";
|
|
OS << "const Stmt *St) const override {\n";
|
|
OS << " S.Diag(AL.getLoc(), diag::err_decl_attribute_invalid_on_stmt)\n";
|
|
OS << " << AL << St->getBeginLoc();\n";
|
|
OS << " return false;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
} else {
|
|
// Now, do the same for statements.
|
|
OS << "bool diagAppertainsToStmt(Sema &S, ";
|
|
OS << "const ParsedAttr &Attr, const Stmt *St) const override {\n";
|
|
OS << " if (";
|
|
for (auto I = StmtSubjects.begin(), E = StmtSubjects.end(); I != E; ++I) {
|
|
OS << "!isa<" << (*I)->getName() << ">(St)";
|
|
if (I + 1 != E)
|
|
OS << " && ";
|
|
}
|
|
OS << ") {\n";
|
|
OS << " S.Diag(Attr.getLoc(), diag::";
|
|
OS << (Warn ? "warn_attribute_wrong_decl_type_str"
|
|
: "err_attribute_wrong_decl_type_str");
|
|
OS << ")\n";
|
|
OS << " << Attr << ";
|
|
OS << CalculateDiagnostic(*SubjectObj) << ";\n";
|
|
OS << " return false;\n";
|
|
OS << " }\n";
|
|
OS << " return true;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
}
|
|
|
|
// Generates the mutual exclusion checks. The checks for parsed attributes are
|
|
// written into OS and the checks for merging declaration attributes are
|
|
// written into MergeOS.
|
|
static void GenerateMutualExclusionsChecks(const Record &Attr,
|
|
const RecordKeeper &Records,
|
|
raw_ostream &OS,
|
|
raw_ostream &MergeDeclOS,
|
|
raw_ostream &MergeStmtOS) {
|
|
// Find all of the definitions that inherit from MutualExclusions and include
|
|
// the given attribute in the list of exclusions to generate the
|
|
// diagMutualExclusion() check.
|
|
std::vector<Record *> ExclusionsList =
|
|
Records.getAllDerivedDefinitions("MutualExclusions");
|
|
|
|
// We don't do any of this magic for type attributes yet.
|
|
if (Attr.isSubClassOf("TypeAttr"))
|
|
return;
|
|
|
|
// This means the attribute is either a statement attribute, a decl
|
|
// attribute, or both; find out which.
|
|
bool CurAttrIsStmtAttr =
|
|
Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr");
|
|
bool CurAttrIsDeclAttr =
|
|
!CurAttrIsStmtAttr || Attr.isSubClassOf("DeclOrStmtAttr");
|
|
|
|
std::vector<std::string> DeclAttrs, StmtAttrs;
|
|
|
|
for (const Record *Exclusion : ExclusionsList) {
|
|
std::vector<Record *> MutuallyExclusiveAttrs =
|
|
Exclusion->getValueAsListOfDefs("Exclusions");
|
|
auto IsCurAttr = [Attr](const Record *R) {
|
|
return R->getName() == Attr.getName();
|
|
};
|
|
if (llvm::any_of(MutuallyExclusiveAttrs, IsCurAttr)) {
|
|
// This list of exclusions includes the attribute we're looking for, so
|
|
// add the exclusive attributes to the proper list for checking.
|
|
for (const Record *AttrToExclude : MutuallyExclusiveAttrs) {
|
|
if (IsCurAttr(AttrToExclude))
|
|
continue;
|
|
|
|
if (CurAttrIsStmtAttr)
|
|
StmtAttrs.push_back((AttrToExclude->getName() + "Attr").str());
|
|
if (CurAttrIsDeclAttr)
|
|
DeclAttrs.push_back((AttrToExclude->getName() + "Attr").str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there are any decl or stmt attributes, silence -Woverloaded-virtual
|
|
// warnings for them both.
|
|
if (!DeclAttrs.empty() || !StmtAttrs.empty())
|
|
OS << " using ParsedAttrInfo::diagMutualExclusion;\n\n";
|
|
|
|
// If we discovered any decl or stmt attributes to test for, generate the
|
|
// predicates for them now.
|
|
if (!DeclAttrs.empty()) {
|
|
// Generate the ParsedAttrInfo subclass logic for declarations.
|
|
OS << " bool diagMutualExclusion(Sema &S, const ParsedAttr &AL, "
|
|
<< "const Decl *D) const override {\n";
|
|
for (const std::string &A : DeclAttrs) {
|
|
OS << " if (const auto *A = D->getAttr<" << A << ">()) {\n";
|
|
OS << " S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)"
|
|
<< " << AL << A;\n";
|
|
OS << " S.Diag(A->getLocation(), diag::note_conflicting_attribute);";
|
|
OS << " \nreturn false;\n";
|
|
OS << " }\n";
|
|
}
|
|
OS << " return true;\n";
|
|
OS << " }\n\n";
|
|
|
|
// Also generate the declaration attribute merging logic if the current
|
|
// attribute is one that can be inheritted on a declaration. It is assumed
|
|
// this code will be executed in the context of a function with parameters:
|
|
// Sema &S, Decl *D, Attr *A and that returns a bool (false on diagnostic,
|
|
// true on success).
|
|
if (Attr.isSubClassOf("InheritableAttr")) {
|
|
MergeDeclOS << " if (const auto *Second = dyn_cast<"
|
|
<< (Attr.getName() + "Attr").str() << ">(A)) {\n";
|
|
for (const std::string &A : DeclAttrs) {
|
|
MergeDeclOS << " if (const auto *First = D->getAttr<" << A
|
|
<< ">()) {\n";
|
|
MergeDeclOS << " S.Diag(First->getLocation(), "
|
|
<< "diag::err_attributes_are_not_compatible) << First << "
|
|
<< "Second;\n";
|
|
MergeDeclOS << " S.Diag(Second->getLocation(), "
|
|
<< "diag::note_conflicting_attribute);\n";
|
|
MergeDeclOS << " return false;\n";
|
|
MergeDeclOS << " }\n";
|
|
}
|
|
MergeDeclOS << " return true;\n";
|
|
MergeDeclOS << " }\n";
|
|
}
|
|
}
|
|
|
|
// Statement attributes are a bit different from declarations. With
|
|
// declarations, each attribute is added to the declaration as it is
|
|
// processed, and so you can look on the Decl * itself to see if there is a
|
|
// conflicting attribute. Statement attributes are processed as a group
|
|
// because AttributedStmt needs to tail-allocate all of the attribute nodes
|
|
// at once. This means we cannot check whether the statement already contains
|
|
// an attribute to check for the conflict. Instead, we need to check whether
|
|
// the given list of semantic attributes contain any conflicts. It is assumed
|
|
// this code will be executed in the context of a function with parameters:
|
|
// Sema &S, const SmallVectorImpl<const Attr *> &C. The code will be within a
|
|
// loop which loops over the container C with a loop variable named A to
|
|
// represent the current attribute to check for conflicts.
|
|
//
|
|
// FIXME: it would be nice not to walk over the list of potential attributes
|
|
// to apply to the statement more than once, but statements typically don't
|
|
// have long lists of attributes on them, so re-walking the list should not
|
|
// be an expensive operation.
|
|
if (!StmtAttrs.empty()) {
|
|
MergeStmtOS << " if (const auto *Second = dyn_cast<"
|
|
<< (Attr.getName() + "Attr").str() << ">(A)) {\n";
|
|
MergeStmtOS << " auto Iter = llvm::find_if(C, [](const Attr *Check) "
|
|
<< "{ return isa<";
|
|
interleave(
|
|
StmtAttrs, [&](const std::string &Name) { MergeStmtOS << Name; },
|
|
[&] { MergeStmtOS << ", "; });
|
|
MergeStmtOS << ">(Check); });\n";
|
|
MergeStmtOS << " if (Iter != C.end()) {\n";
|
|
MergeStmtOS << " S.Diag((*Iter)->getLocation(), "
|
|
<< "diag::err_attributes_are_not_compatible) << *Iter << "
|
|
<< "Second;\n";
|
|
MergeStmtOS << " S.Diag(Second->getLocation(), "
|
|
<< "diag::note_conflicting_attribute);\n";
|
|
MergeStmtOS << " return false;\n";
|
|
MergeStmtOS << " }\n";
|
|
MergeStmtOS << " }\n";
|
|
}
|
|
}
|
|
|
|
static void
|
|
emitAttributeMatchRules(PragmaClangAttributeSupport &PragmaAttributeSupport,
|
|
raw_ostream &OS) {
|
|
OS << "static bool checkAttributeMatchRuleAppliesTo(const Decl *D, "
|
|
<< AttributeSubjectMatchRule::EnumName << " rule) {\n";
|
|
OS << " switch (rule) {\n";
|
|
for (const auto &Rule : PragmaAttributeSupport.Rules) {
|
|
if (Rule.isAbstractRule()) {
|
|
OS << " case " << Rule.getEnumValue() << ":\n";
|
|
OS << " assert(false && \"Abstract matcher rule isn't allowed\");\n";
|
|
OS << " return false;\n";
|
|
continue;
|
|
}
|
|
std::vector<Record *> Subjects = Rule.getSubjects();
|
|
assert(!Subjects.empty() && "Missing subjects");
|
|
OS << " case " << Rule.getEnumValue() << ":\n";
|
|
OS << " return ";
|
|
for (auto I = Subjects.begin(), E = Subjects.end(); I != E; ++I) {
|
|
// If the subject has custom code associated with it, use the function
|
|
// that was generated for GenerateAppertainsTo to check if the declaration
|
|
// is valid.
|
|
if ((*I)->isSubClassOf("SubsetSubject"))
|
|
OS << functionNameForCustomAppertainsTo(**I) << "(D)";
|
|
else
|
|
OS << "isa<" << GetSubjectWithSuffix(*I) << ">(D)";
|
|
|
|
if (I + 1 != E)
|
|
OS << " || ";
|
|
}
|
|
OS << ";\n";
|
|
}
|
|
OS << " }\n";
|
|
OS << " llvm_unreachable(\"Invalid match rule\");\nreturn false;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
static void GenerateLangOptRequirements(const Record &R,
|
|
raw_ostream &OS) {
|
|
// If the attribute has an empty or unset list of language requirements,
|
|
// use the default handler.
|
|
std::vector<Record *> LangOpts = R.getValueAsListOfDefs("LangOpts");
|
|
if (LangOpts.empty())
|
|
return;
|
|
|
|
OS << "bool acceptsLangOpts(const LangOptions &LangOpts) const override {\n";
|
|
OS << " return " << GenerateTestExpression(LangOpts) << ";\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
static void GenerateTargetRequirements(const Record &Attr,
|
|
const ParsedAttrMap &Dupes,
|
|
raw_ostream &OS) {
|
|
// If the attribute is not a target specific attribute, use the default
|
|
// target handler.
|
|
if (!Attr.isSubClassOf("TargetSpecificAttr"))
|
|
return;
|
|
|
|
// Get the list of architectures to be tested for.
|
|
const Record *R = Attr.getValueAsDef("Target");
|
|
std::vector<StringRef> Arches = R->getValueAsListOfStrings("Arches");
|
|
|
|
// If there are other attributes which share the same parsed attribute kind,
|
|
// such as target-specific attributes with a shared spelling, collapse the
|
|
// duplicate architectures. This is required because a shared target-specific
|
|
// attribute has only one ParsedAttr::Kind enumeration value, but it
|
|
// applies to multiple target architectures. In order for the attribute to be
|
|
// considered valid, all of its architectures need to be included.
|
|
if (!Attr.isValueUnset("ParseKind")) {
|
|
const StringRef APK = Attr.getValueAsString("ParseKind");
|
|
for (const auto &I : Dupes) {
|
|
if (I.first == APK) {
|
|
std::vector<StringRef> DA =
|
|
I.second->getValueAsDef("Target")->getValueAsListOfStrings(
|
|
"Arches");
|
|
Arches.insert(Arches.end(), DA.begin(), DA.end());
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string FnName = "isTarget";
|
|
std::string Test;
|
|
bool UsesT = GenerateTargetSpecificAttrChecks(R, Arches, Test, &FnName);
|
|
|
|
OS << "bool existsInTarget(const TargetInfo &Target) const override {\n";
|
|
if (UsesT)
|
|
OS << " const llvm::Triple &T = Target.getTriple(); (void)T;\n";
|
|
OS << " return " << Test << ";\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
static void GenerateSpellingIndexToSemanticSpelling(const Record &Attr,
|
|
raw_ostream &OS) {
|
|
// If the attribute does not have a semantic form, we can bail out early.
|
|
if (!Attr.getValueAsBit("ASTNode"))
|
|
return;
|
|
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
|
|
|
|
// If there are zero or one spellings, or all of the spellings share the same
|
|
// name, we can also bail out early.
|
|
if (Spellings.size() <= 1 || SpellingNamesAreCommon(Spellings))
|
|
return;
|
|
|
|
// Generate the enumeration we will use for the mapping.
|
|
SemanticSpellingMap SemanticToSyntacticMap;
|
|
std::string Enum = CreateSemanticSpellings(Spellings, SemanticToSyntacticMap);
|
|
std::string Name = Attr.getName().str() + "AttrSpellingMap";
|
|
|
|
OS << "unsigned spellingIndexToSemanticSpelling(";
|
|
OS << "const ParsedAttr &Attr) const override {\n";
|
|
OS << Enum;
|
|
OS << " unsigned Idx = Attr.getAttributeSpellingListIndex();\n";
|
|
WriteSemanticSpellingSwitch("Idx", SemanticToSyntacticMap, OS);
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
static void GenerateHandleDeclAttribute(const Record &Attr, raw_ostream &OS) {
|
|
// Only generate if Attr can be handled simply.
|
|
if (!Attr.getValueAsBit("SimpleHandler"))
|
|
return;
|
|
|
|
// Generate a function which just converts from ParsedAttr to the Attr type.
|
|
OS << "AttrHandling handleDeclAttribute(Sema &S, Decl *D,";
|
|
OS << "const ParsedAttr &Attr) const override {\n";
|
|
OS << " D->addAttr(::new (S.Context) " << Attr.getName();
|
|
OS << "Attr(S.Context, Attr));\n";
|
|
OS << " return AttributeApplied;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
static bool isParamExpr(const Record *Arg) {
|
|
return !Arg->getSuperClasses().empty() &&
|
|
llvm::StringSwitch<bool>(
|
|
Arg->getSuperClasses().back().first->getName())
|
|
.Case("ExprArgument", true)
|
|
.Case("VariadicExprArgument", true)
|
|
.Default(false);
|
|
}
|
|
|
|
void GenerateIsParamExpr(const Record &Attr, raw_ostream &OS) {
|
|
OS << "bool isParamExpr(size_t N) const override {\n";
|
|
OS << " return ";
|
|
auto Args = Attr.getValueAsListOfDefs("Args");
|
|
for (size_t I = 0; I < Args.size(); ++I)
|
|
if (isParamExpr(Args[I]))
|
|
OS << "(N == " << I << ") || ";
|
|
OS << "false;\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
void GenerateHandleAttrWithDelayedArgs(RecordKeeper &Records, raw_ostream &OS) {
|
|
OS << "static void handleAttrWithDelayedArgs(Sema &S, Decl *D, ";
|
|
OS << "const ParsedAttr &Attr) {\n";
|
|
OS << " SmallVector<Expr *, 4> ArgExprs;\n";
|
|
OS << " ArgExprs.reserve(Attr.getNumArgs());\n";
|
|
OS << " for (unsigned I = 0; I < Attr.getNumArgs(); ++I) {\n";
|
|
OS << " assert(!Attr.isArgIdent(I));\n";
|
|
OS << " ArgExprs.push_back(Attr.getArgAsExpr(I));\n";
|
|
OS << " }\n";
|
|
OS << " clang::Attr *CreatedAttr = nullptr;\n";
|
|
OS << " switch (Attr.getKind()) {\n";
|
|
OS << " default:\n";
|
|
OS << " llvm_unreachable(\"Attribute cannot hold delayed arguments.\");\n";
|
|
ParsedAttrMap Attrs = getParsedAttrList(Records);
|
|
for (const auto &I : Attrs) {
|
|
const Record &R = *I.second;
|
|
if (!R.getValueAsBit("AcceptsExprPack"))
|
|
continue;
|
|
OS << " case ParsedAttr::AT_" << I.first << ": {\n";
|
|
OS << " CreatedAttr = " << R.getName() << "Attr::CreateWithDelayedArgs";
|
|
OS << "(S.Context, ArgExprs.data(), ArgExprs.size(), Attr);\n";
|
|
OS << " break;\n";
|
|
OS << " }\n";
|
|
}
|
|
OS << " }\n";
|
|
OS << " D->addAttr(CreatedAttr);\n";
|
|
OS << "}\n\n";
|
|
}
|
|
|
|
static bool IsKnownToGCC(const Record &Attr) {
|
|
// Look at the spellings for this subject; if there are any spellings which
|
|
// claim to be known to GCC, the attribute is known to GCC.
|
|
return llvm::any_of(
|
|
GetFlattenedSpellings(Attr),
|
|
[](const FlattenedSpelling &S) { return S.knownToGCC(); });
|
|
}
|
|
|
|
/// Emits the parsed attribute helpers
|
|
void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Parsed attribute helpers", OS);
|
|
|
|
OS << "#if !defined(WANT_DECL_MERGE_LOGIC) && "
|
|
<< "!defined(WANT_STMT_MERGE_LOGIC)\n";
|
|
PragmaClangAttributeSupport &PragmaAttributeSupport =
|
|
getPragmaAttributeSupport(Records);
|
|
|
|
// Get the list of parsed attributes, and accept the optional list of
|
|
// duplicates due to the ParseKind.
|
|
ParsedAttrMap Dupes;
|
|
ParsedAttrMap Attrs = getParsedAttrList(Records, &Dupes);
|
|
|
|
// Generate all of the custom appertainsTo functions that the attributes
|
|
// will be using.
|
|
for (auto I : Attrs) {
|
|
const Record &Attr = *I.second;
|
|
if (Attr.isValueUnset("Subjects"))
|
|
continue;
|
|
const Record *SubjectObj = Attr.getValueAsDef("Subjects");
|
|
for (auto Subject : SubjectObj->getValueAsListOfDefs("Subjects"))
|
|
if (Subject->isSubClassOf("SubsetSubject"))
|
|
GenerateCustomAppertainsTo(*Subject, OS);
|
|
}
|
|
|
|
// This stream is used to collect all of the declaration attribute merging
|
|
// logic for performing mutual exclusion checks. This gets emitted at the
|
|
// end of the file in a helper function of its own.
|
|
std::string DeclMergeChecks, StmtMergeChecks;
|
|
raw_string_ostream MergeDeclOS(DeclMergeChecks), MergeStmtOS(StmtMergeChecks);
|
|
|
|
// Generate a ParsedAttrInfo struct for each of the attributes.
|
|
for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) {
|
|
// TODO: If the attribute's kind appears in the list of duplicates, that is
|
|
// because it is a target-specific attribute that appears multiple times.
|
|
// It would be beneficial to test whether the duplicates are "similar
|
|
// enough" to each other to not cause problems. For instance, check that
|
|
// the spellings are identical, and custom parsing rules match, etc.
|
|
|
|
// We need to generate struct instances based off ParsedAttrInfo from
|
|
// ParsedAttr.cpp.
|
|
const std::string &AttrName = I->first;
|
|
const Record &Attr = *I->second;
|
|
auto Spellings = GetFlattenedSpellings(Attr);
|
|
if (!Spellings.empty()) {
|
|
OS << "static constexpr ParsedAttrInfo::Spelling " << I->first
|
|
<< "Spellings[] = {\n";
|
|
for (const auto &S : Spellings) {
|
|
const std::string &RawSpelling = S.name();
|
|
std::string Spelling;
|
|
if (!S.nameSpace().empty())
|
|
Spelling += S.nameSpace() + "::";
|
|
if (S.variety() == "GNU")
|
|
Spelling += NormalizeGNUAttrSpelling(RawSpelling);
|
|
else
|
|
Spelling += RawSpelling;
|
|
OS << " {AttributeCommonInfo::AS_" << S.variety();
|
|
OS << ", \"" << Spelling << "\"},\n";
|
|
}
|
|
OS << "};\n";
|
|
}
|
|
|
|
std::vector<std::string> ArgNames;
|
|
for (const auto &Arg : Attr.getValueAsListOfDefs("Args")) {
|
|
bool UnusedUnset;
|
|
if (Arg->getValueAsBitOrUnset("Fake", UnusedUnset))
|
|
continue;
|
|
ArgNames.push_back(Arg->getValueAsString("Name").str());
|
|
for (const auto &Class : Arg->getSuperClasses()) {
|
|
if (Class.first->getName().startswith("Variadic")) {
|
|
ArgNames.back().append("...");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!ArgNames.empty()) {
|
|
OS << "static constexpr const char *" << I->first << "ArgNames[] = {\n";
|
|
for (const auto &N : ArgNames)
|
|
OS << '"' << N << "\",";
|
|
OS << "};\n";
|
|
}
|
|
|
|
OS << "struct ParsedAttrInfo" << I->first
|
|
<< " final : public ParsedAttrInfo {\n";
|
|
OS << " constexpr ParsedAttrInfo" << I->first << "() : ParsedAttrInfo(\n";
|
|
OS << " /*AttrKind=*/ParsedAttr::AT_" << AttrName << ",\n";
|
|
emitArgInfo(Attr, OS);
|
|
OS << " /*HasCustomParsing=*/";
|
|
OS << Attr.getValueAsBit("HasCustomParsing") << ",\n";
|
|
OS << " /*AcceptsExprPack=*/";
|
|
OS << Attr.getValueAsBit("AcceptsExprPack") << ",\n";
|
|
OS << " /*IsTargetSpecific=*/";
|
|
OS << Attr.isSubClassOf("TargetSpecificAttr") << ",\n";
|
|
OS << " /*IsType=*/";
|
|
OS << (Attr.isSubClassOf("TypeAttr") || Attr.isSubClassOf("DeclOrTypeAttr"))
|
|
<< ",\n";
|
|
OS << " /*IsStmt=*/";
|
|
OS << (Attr.isSubClassOf("StmtAttr") || Attr.isSubClassOf("DeclOrStmtAttr"))
|
|
<< ",\n";
|
|
OS << " /*IsKnownToGCC=*/";
|
|
OS << IsKnownToGCC(Attr) << ",\n";
|
|
OS << " /*IsSupportedByPragmaAttribute=*/";
|
|
OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ",\n";
|
|
if (!Spellings.empty())
|
|
OS << " /*Spellings=*/" << I->first << "Spellings,\n";
|
|
else
|
|
OS << " /*Spellings=*/{},\n";
|
|
if (!ArgNames.empty())
|
|
OS << " /*ArgNames=*/" << I->first << "ArgNames";
|
|
else
|
|
OS << " /*ArgNames=*/{}";
|
|
OS << ") {}\n";
|
|
GenerateAppertainsTo(Attr, OS);
|
|
GenerateMutualExclusionsChecks(Attr, Records, OS, MergeDeclOS, MergeStmtOS);
|
|
GenerateLangOptRequirements(Attr, OS);
|
|
GenerateTargetRequirements(Attr, Dupes, OS);
|
|
GenerateSpellingIndexToSemanticSpelling(Attr, OS);
|
|
PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS);
|
|
GenerateHandleDeclAttribute(Attr, OS);
|
|
GenerateIsParamExpr(Attr, OS);
|
|
OS << "static const ParsedAttrInfo" << I->first << " Instance;\n";
|
|
OS << "};\n";
|
|
OS << "const ParsedAttrInfo" << I->first << " ParsedAttrInfo" << I->first
|
|
<< "::Instance;\n";
|
|
}
|
|
|
|
OS << "static const ParsedAttrInfo *AttrInfoMap[] = {\n";
|
|
for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) {
|
|
OS << "&ParsedAttrInfo" << I->first << "::Instance,\n";
|
|
}
|
|
OS << "};\n\n";
|
|
|
|
// Generate function for handling attributes with delayed arguments
|
|
GenerateHandleAttrWithDelayedArgs(Records, OS);
|
|
|
|
// Generate the attribute match rules.
|
|
emitAttributeMatchRules(PragmaAttributeSupport, OS);
|
|
|
|
OS << "#elif defined(WANT_DECL_MERGE_LOGIC)\n\n";
|
|
|
|
// Write out the declaration merging check logic.
|
|
OS << "static bool DiagnoseMutualExclusions(Sema &S, const NamedDecl *D, "
|
|
<< "const Attr *A) {\n";
|
|
OS << MergeDeclOS.str();
|
|
OS << " return true;\n";
|
|
OS << "}\n\n";
|
|
|
|
OS << "#elif defined(WANT_STMT_MERGE_LOGIC)\n\n";
|
|
|
|
// Write out the statement merging check logic.
|
|
OS << "static bool DiagnoseMutualExclusions(Sema &S, "
|
|
<< "const SmallVectorImpl<const Attr *> &C) {\n";
|
|
OS << " for (const Attr *A : C) {\n";
|
|
OS << MergeStmtOS.str();
|
|
OS << " }\n";
|
|
OS << " return true;\n";
|
|
OS << "}\n\n";
|
|
|
|
OS << "#endif\n";
|
|
}
|
|
|
|
// Emits the kind list of parsed attributes
|
|
void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Attribute name matcher", OS);
|
|
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
std::vector<StringMatcher::StringPair> GNU, Declspec, Microsoft, CXX11,
|
|
Keywords, Pragma, C2x, HLSLSemantic;
|
|
std::set<std::string> Seen;
|
|
for (const auto *A : Attrs) {
|
|
const Record &Attr = *A;
|
|
|
|
bool SemaHandler = Attr.getValueAsBit("SemaHandler");
|
|
bool Ignored = Attr.getValueAsBit("Ignored");
|
|
if (SemaHandler || Ignored) {
|
|
// Attribute spellings can be shared between target-specific attributes,
|
|
// and can be shared between syntaxes for the same attribute. For
|
|
// instance, an attribute can be spelled GNU<"interrupt"> for an ARM-
|
|
// specific attribute, or MSP430-specific attribute. Additionally, an
|
|
// attribute can be spelled GNU<"dllexport"> and Declspec<"dllexport">
|
|
// for the same semantic attribute. Ultimately, we need to map each of
|
|
// these to a single AttributeCommonInfo::Kind value, but the
|
|
// StringMatcher class cannot handle duplicate match strings. So we
|
|
// generate a list of string to match based on the syntax, and emit
|
|
// multiple string matchers depending on the syntax used.
|
|
std::string AttrName;
|
|
if (Attr.isSubClassOf("TargetSpecificAttr") &&
|
|
!Attr.isValueUnset("ParseKind")) {
|
|
AttrName = std::string(Attr.getValueAsString("ParseKind"));
|
|
if (!Seen.insert(AttrName).second)
|
|
continue;
|
|
} else
|
|
AttrName = NormalizeAttrName(StringRef(Attr.getName())).str();
|
|
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
|
|
for (const auto &S : Spellings) {
|
|
const std::string &RawSpelling = S.name();
|
|
std::vector<StringMatcher::StringPair> *Matches = nullptr;
|
|
std::string Spelling;
|
|
const std::string &Variety = S.variety();
|
|
if (Variety == "CXX11") {
|
|
Matches = &CXX11;
|
|
if (!S.nameSpace().empty())
|
|
Spelling += S.nameSpace() + "::";
|
|
} else if (Variety == "C2x") {
|
|
Matches = &C2x;
|
|
if (!S.nameSpace().empty())
|
|
Spelling += S.nameSpace() + "::";
|
|
} else if (Variety == "GNU")
|
|
Matches = &GNU;
|
|
else if (Variety == "Declspec")
|
|
Matches = &Declspec;
|
|
else if (Variety == "Microsoft")
|
|
Matches = &Microsoft;
|
|
else if (Variety == "Keyword")
|
|
Matches = &Keywords;
|
|
else if (Variety == "Pragma")
|
|
Matches = &Pragma;
|
|
else if (Variety == "HLSLSemantic")
|
|
Matches = &HLSLSemantic;
|
|
|
|
assert(Matches && "Unsupported spelling variety found");
|
|
|
|
if (Variety == "GNU")
|
|
Spelling += NormalizeGNUAttrSpelling(RawSpelling);
|
|
else
|
|
Spelling += RawSpelling;
|
|
|
|
if (SemaHandler)
|
|
Matches->push_back(StringMatcher::StringPair(
|
|
Spelling, "return AttributeCommonInfo::AT_" + AttrName + ";"));
|
|
else
|
|
Matches->push_back(StringMatcher::StringPair(
|
|
Spelling, "return AttributeCommonInfo::IgnoredAttribute;"));
|
|
}
|
|
}
|
|
}
|
|
|
|
OS << "static AttributeCommonInfo::Kind getAttrKind(StringRef Name, ";
|
|
OS << "AttributeCommonInfo::Syntax Syntax) {\n";
|
|
OS << " if (AttributeCommonInfo::AS_GNU == Syntax) {\n";
|
|
StringMatcher("Name", GNU, OS).Emit();
|
|
OS << " } else if (AttributeCommonInfo::AS_Declspec == Syntax) {\n";
|
|
StringMatcher("Name", Declspec, OS).Emit();
|
|
OS << " } else if (AttributeCommonInfo::AS_Microsoft == Syntax) {\n";
|
|
StringMatcher("Name", Microsoft, OS).Emit();
|
|
OS << " } else if (AttributeCommonInfo::AS_CXX11 == Syntax) {\n";
|
|
StringMatcher("Name", CXX11, OS).Emit();
|
|
OS << " } else if (AttributeCommonInfo::AS_C2x == Syntax) {\n";
|
|
StringMatcher("Name", C2x, OS).Emit();
|
|
OS << " } else if (AttributeCommonInfo::AS_Keyword == Syntax || ";
|
|
OS << "AttributeCommonInfo::AS_ContextSensitiveKeyword == Syntax) {\n";
|
|
StringMatcher("Name", Keywords, OS).Emit();
|
|
OS << " } else if (AttributeCommonInfo::AS_Pragma == Syntax) {\n";
|
|
StringMatcher("Name", Pragma, OS).Emit();
|
|
OS << " } else if (AttributeCommonInfo::AS_HLSLSemantic == Syntax) {\n";
|
|
StringMatcher("Name", HLSLSemantic, OS).Emit();
|
|
OS << " }\n";
|
|
OS << " return AttributeCommonInfo::UnknownAttribute;\n"
|
|
<< "}\n";
|
|
}
|
|
|
|
// Emits the code to dump an attribute.
|
|
void EmitClangAttrTextNodeDump(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Attribute text node dumper", OS);
|
|
|
|
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"), Args;
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
// If the attribute has a semantically-meaningful name (which is determined
|
|
// by whether there is a Spelling enumeration for it), then write out the
|
|
// spelling used for the attribute.
|
|
|
|
std::string FunctionContent;
|
|
llvm::raw_string_ostream SS(FunctionContent);
|
|
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(R);
|
|
if (Spellings.size() > 1 && !SpellingNamesAreCommon(Spellings))
|
|
SS << " OS << \" \" << A->getSpelling();\n";
|
|
|
|
Args = R.getValueAsListOfDefs("Args");
|
|
for (const auto *Arg : Args)
|
|
createArgument(*Arg, R.getName())->writeDump(SS);
|
|
|
|
if (Attr->getValueAsBit("AcceptsExprPack"))
|
|
VariadicExprArgument("DelayedArgs", R.getName()).writeDump(OS);
|
|
|
|
if (SS.tell()) {
|
|
OS << " void Visit" << R.getName() << "Attr(const " << R.getName()
|
|
<< "Attr *A) {\n";
|
|
if (!Args.empty())
|
|
OS << " const auto *SA = cast<" << R.getName()
|
|
<< "Attr>(A); (void)SA;\n";
|
|
OS << SS.str();
|
|
OS << " }\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void EmitClangAttrNodeTraverse(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Attribute text node traverser", OS);
|
|
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"), Args;
|
|
for (const auto *Attr : Attrs) {
|
|
const Record &R = *Attr;
|
|
if (!R.getValueAsBit("ASTNode"))
|
|
continue;
|
|
|
|
std::string FunctionContent;
|
|
llvm::raw_string_ostream SS(FunctionContent);
|
|
|
|
Args = R.getValueAsListOfDefs("Args");
|
|
for (const auto *Arg : Args)
|
|
createArgument(*Arg, R.getName())->writeDumpChildren(SS);
|
|
if (Attr->getValueAsBit("AcceptsExprPack"))
|
|
VariadicExprArgument("DelayedArgs", R.getName()).writeDumpChildren(SS);
|
|
if (SS.tell()) {
|
|
OS << " void Visit" << R.getName() << "Attr(const " << R.getName()
|
|
<< "Attr *A) {\n";
|
|
if (!Args.empty())
|
|
OS << " const auto *SA = cast<" << R.getName()
|
|
<< "Attr>(A); (void)SA;\n";
|
|
OS << SS.str();
|
|
OS << " }\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void EmitClangAttrParserStringSwitches(RecordKeeper &Records,
|
|
raw_ostream &OS) {
|
|
emitSourceFileHeader("Parser-related llvm::StringSwitch cases", OS);
|
|
emitClangAttrArgContextList(Records, OS);
|
|
emitClangAttrIdentifierArgList(Records, OS);
|
|
emitClangAttrVariadicIdentifierArgList(Records, OS);
|
|
emitClangAttrThisIsaIdentifierArgList(Records, OS);
|
|
emitClangAttrAcceptsExprPack(Records, OS);
|
|
emitClangAttrTypeArgList(Records, OS);
|
|
emitClangAttrLateParsedList(Records, OS);
|
|
}
|
|
|
|
void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,
|
|
raw_ostream &OS) {
|
|
getPragmaAttributeSupport(Records).generateParsingHelpers(OS);
|
|
}
|
|
|
|
void EmitClangAttrDocTable(RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("Clang attribute documentation", OS);
|
|
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
for (const auto *A : Attrs) {
|
|
if (!A->getValueAsBit("ASTNode"))
|
|
continue;
|
|
std::vector<Record *> Docs = A->getValueAsListOfDefs("Documentation");
|
|
assert(!Docs.empty());
|
|
// Only look at the first documentation if there are several.
|
|
// (Currently there's only one such attr, revisit if this becomes common).
|
|
StringRef Text =
|
|
Docs.front()->getValueAsOptionalString("Content").value_or("");
|
|
OS << "\nstatic const char AttrDoc_" << A->getName() << "[] = "
|
|
<< "R\"reST(" << Text.trim() << ")reST\";\n";
|
|
}
|
|
}
|
|
|
|
enum class SpellingKind : size_t {
|
|
GNU,
|
|
CXX11,
|
|
C2x,
|
|
Declspec,
|
|
Microsoft,
|
|
Keyword,
|
|
Pragma,
|
|
HLSLSemantic,
|
|
NumSpellingKinds
|
|
};
|
|
static const size_t NumSpellingKinds = (size_t)SpellingKind::NumSpellingKinds;
|
|
|
|
class SpellingList {
|
|
std::vector<std::string> Spellings[NumSpellingKinds];
|
|
|
|
public:
|
|
ArrayRef<std::string> operator[](SpellingKind K) const {
|
|
return Spellings[(size_t)K];
|
|
}
|
|
|
|
void add(const Record &Attr, FlattenedSpelling Spelling) {
|
|
SpellingKind Kind = StringSwitch<SpellingKind>(Spelling.variety())
|
|
.Case("GNU", SpellingKind::GNU)
|
|
.Case("CXX11", SpellingKind::CXX11)
|
|
.Case("C2x", SpellingKind::C2x)
|
|
.Case("Declspec", SpellingKind::Declspec)
|
|
.Case("Microsoft", SpellingKind::Microsoft)
|
|
.Case("Keyword", SpellingKind::Keyword)
|
|
.Case("Pragma", SpellingKind::Pragma)
|
|
.Case("HLSLSemantic", SpellingKind::HLSLSemantic);
|
|
std::string Name;
|
|
if (!Spelling.nameSpace().empty()) {
|
|
switch (Kind) {
|
|
case SpellingKind::CXX11:
|
|
case SpellingKind::C2x:
|
|
Name = Spelling.nameSpace() + "::";
|
|
break;
|
|
case SpellingKind::Pragma:
|
|
Name = Spelling.nameSpace() + " ";
|
|
break;
|
|
default:
|
|
PrintFatalError(Attr.getLoc(), "Unexpected namespace in spelling");
|
|
}
|
|
}
|
|
Name += Spelling.name();
|
|
|
|
Spellings[(size_t)Kind].push_back(Name);
|
|
}
|
|
};
|
|
|
|
class DocumentationData {
|
|
public:
|
|
const Record *Documentation;
|
|
const Record *Attribute;
|
|
std::string Heading;
|
|
SpellingList SupportedSpellings;
|
|
|
|
DocumentationData(const Record &Documentation, const Record &Attribute,
|
|
std::pair<std::string, SpellingList> HeadingAndSpellings)
|
|
: Documentation(&Documentation), Attribute(&Attribute),
|
|
Heading(std::move(HeadingAndSpellings.first)),
|
|
SupportedSpellings(std::move(HeadingAndSpellings.second)) {}
|
|
};
|
|
|
|
static void WriteCategoryHeader(const Record *DocCategory,
|
|
raw_ostream &OS) {
|
|
const StringRef Name = DocCategory->getValueAsString("Name");
|
|
OS << Name << "\n" << std::string(Name.size(), '=') << "\n";
|
|
|
|
// If there is content, print that as well.
|
|
const StringRef ContentStr = DocCategory->getValueAsString("Content");
|
|
// Trim leading and trailing newlines and spaces.
|
|
OS << ContentStr.trim();
|
|
|
|
OS << "\n\n";
|
|
}
|
|
|
|
static std::pair<std::string, SpellingList>
|
|
GetAttributeHeadingAndSpellings(const Record &Documentation,
|
|
const Record &Attribute,
|
|
StringRef Cat) {
|
|
// FIXME: there is no way to have a per-spelling category for the attribute
|
|
// documentation. This may not be a limiting factor since the spellings
|
|
// should generally be consistently applied across the category.
|
|
|
|
std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attribute);
|
|
if (Spellings.empty())
|
|
PrintFatalError(Attribute.getLoc(),
|
|
"Attribute has no supported spellings; cannot be "
|
|
"documented");
|
|
|
|
// Determine the heading to be used for this attribute.
|
|
std::string Heading = std::string(Documentation.getValueAsString("Heading"));
|
|
if (Heading.empty()) {
|
|
// If there's only one spelling, we can simply use that.
|
|
if (Spellings.size() == 1)
|
|
Heading = Spellings.begin()->name();
|
|
else {
|
|
std::set<std::string> Uniques;
|
|
for (auto I = Spellings.begin(), E = Spellings.end();
|
|
I != E; ++I) {
|
|
std::string Spelling =
|
|
std::string(NormalizeNameForSpellingComparison(I->name()));
|
|
Uniques.insert(Spelling);
|
|
}
|
|
// If the semantic map has only one spelling, that is sufficient for our
|
|
// needs.
|
|
if (Uniques.size() == 1)
|
|
Heading = *Uniques.begin();
|
|
// If it's in the undocumented category, just construct a header by
|
|
// concatenating all the spellings. Might not be great, but better than
|
|
// nothing.
|
|
else if (Cat == "Undocumented")
|
|
Heading = llvm::join(Uniques.begin(), Uniques.end(), ", ");
|
|
}
|
|
}
|
|
|
|
// If the heading is still empty, it is an error.
|
|
if (Heading.empty())
|
|
PrintFatalError(Attribute.getLoc(),
|
|
"This attribute requires a heading to be specified");
|
|
|
|
SpellingList SupportedSpellings;
|
|
for (const auto &I : Spellings)
|
|
SupportedSpellings.add(Attribute, I);
|
|
|
|
return std::make_pair(std::move(Heading), std::move(SupportedSpellings));
|
|
}
|
|
|
|
static void WriteDocumentation(RecordKeeper &Records,
|
|
const DocumentationData &Doc, raw_ostream &OS) {
|
|
OS << Doc.Heading << "\n" << std::string(Doc.Heading.length(), '-') << "\n";
|
|
|
|
// List what spelling syntaxes the attribute supports.
|
|
OS << ".. csv-table:: Supported Syntaxes\n";
|
|
OS << " :header: \"GNU\", \"C++11\", \"C2x\", \"``__declspec``\",";
|
|
OS << " \"Keyword\", \"``#pragma``\", \"``#pragma clang attribute``\",";
|
|
OS << " \"HLSL Semantic\"\n\n \"";
|
|
for (size_t Kind = 0; Kind != NumSpellingKinds; ++Kind) {
|
|
SpellingKind K = (SpellingKind)Kind;
|
|
// TODO: List Microsoft (IDL-style attribute) spellings once we fully
|
|
// support them.
|
|
if (K == SpellingKind::Microsoft)
|
|
continue;
|
|
|
|
bool PrintedAny = false;
|
|
for (StringRef Spelling : Doc.SupportedSpellings[K]) {
|
|
if (PrintedAny)
|
|
OS << " |br| ";
|
|
OS << "``" << Spelling << "``";
|
|
PrintedAny = true;
|
|
}
|
|
|
|
OS << "\",\"";
|
|
}
|
|
|
|
if (getPragmaAttributeSupport(Records).isAttributedSupported(
|
|
*Doc.Attribute))
|
|
OS << "Yes";
|
|
OS << "\"\n\n";
|
|
|
|
// If the attribute is deprecated, print a message about it, and possibly
|
|
// provide a replacement attribute.
|
|
if (!Doc.Documentation->isValueUnset("Deprecated")) {
|
|
OS << "This attribute has been deprecated, and may be removed in a future "
|
|
<< "version of Clang.";
|
|
const Record &Deprecated = *Doc.Documentation->getValueAsDef("Deprecated");
|
|
const StringRef Replacement = Deprecated.getValueAsString("Replacement");
|
|
if (!Replacement.empty())
|
|
OS << " This attribute has been superseded by ``" << Replacement
|
|
<< "``.";
|
|
OS << "\n\n";
|
|
}
|
|
|
|
const StringRef ContentStr = Doc.Documentation->getValueAsString("Content");
|
|
// Trim leading and trailing newlines and spaces.
|
|
OS << ContentStr.trim();
|
|
|
|
OS << "\n\n\n";
|
|
}
|
|
|
|
void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) {
|
|
// Get the documentation introduction paragraph.
|
|
const Record *Documentation = Records.getDef("GlobalDocumentation");
|
|
if (!Documentation) {
|
|
PrintFatalError("The Documentation top-level definition is missing, "
|
|
"no documentation will be generated.");
|
|
return;
|
|
}
|
|
|
|
OS << Documentation->getValueAsString("Intro") << "\n";
|
|
|
|
// Gather the Documentation lists from each of the attributes, based on the
|
|
// category provided.
|
|
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
|
struct CategoryLess {
|
|
bool operator()(const Record *L, const Record *R) const {
|
|
return L->getValueAsString("Name") < R->getValueAsString("Name");
|
|
}
|
|
};
|
|
std::map<const Record *, std::vector<DocumentationData>, CategoryLess>
|
|
SplitDocs;
|
|
for (const auto *A : Attrs) {
|
|
const Record &Attr = *A;
|
|
std::vector<Record *> Docs = Attr.getValueAsListOfDefs("Documentation");
|
|
for (const auto *D : Docs) {
|
|
const Record &Doc = *D;
|
|
const Record *Category = Doc.getValueAsDef("Category");
|
|
// If the category is "InternalOnly", then there cannot be any other
|
|
// documentation categories (otherwise, the attribute would be
|
|
// emitted into the docs).
|
|
const StringRef Cat = Category->getValueAsString("Name");
|
|
bool InternalOnly = Cat == "InternalOnly";
|
|
if (InternalOnly && Docs.size() > 1)
|
|
PrintFatalError(Doc.getLoc(),
|
|
"Attribute is \"InternalOnly\", but has multiple "
|
|
"documentation categories");
|
|
|
|
if (!InternalOnly)
|
|
SplitDocs[Category].push_back(DocumentationData(
|
|
Doc, Attr, GetAttributeHeadingAndSpellings(Doc, Attr, Cat)));
|
|
}
|
|
}
|
|
|
|
// Having split the attributes out based on what documentation goes where,
|
|
// we can begin to generate sections of documentation.
|
|
for (auto &I : SplitDocs) {
|
|
WriteCategoryHeader(I.first, OS);
|
|
|
|
llvm::sort(I.second,
|
|
[](const DocumentationData &D1, const DocumentationData &D2) {
|
|
return D1.Heading < D2.Heading;
|
|
});
|
|
|
|
// Walk over each of the attributes in the category and write out their
|
|
// documentation.
|
|
for (const auto &Doc : I.second)
|
|
WriteDocumentation(Records, Doc, OS);
|
|
}
|
|
}
|
|
|
|
void EmitTestPragmaAttributeSupportedAttributes(RecordKeeper &Records,
|
|
raw_ostream &OS) {
|
|
PragmaClangAttributeSupport Support = getPragmaAttributeSupport(Records);
|
|
ParsedAttrMap Attrs = getParsedAttrList(Records);
|
|
OS << "#pragma clang attribute supports the following attributes:\n";
|
|
for (const auto &I : Attrs) {
|
|
if (!Support.isAttributedSupported(*I.second))
|
|
continue;
|
|
OS << I.first;
|
|
if (I.second->isValueUnset("Subjects")) {
|
|
OS << " ()\n";
|
|
continue;
|
|
}
|
|
const Record *SubjectObj = I.second->getValueAsDef("Subjects");
|
|
std::vector<Record *> Subjects =
|
|
SubjectObj->getValueAsListOfDefs("Subjects");
|
|
OS << " (";
|
|
bool PrintComma = false;
|
|
for (const auto &Subject : llvm::enumerate(Subjects)) {
|
|
if (!isSupportedPragmaClangAttributeSubject(*Subject.value()))
|
|
continue;
|
|
if (PrintComma)
|
|
OS << ", ";
|
|
PrintComma = true;
|
|
PragmaClangAttributeSupport::RuleOrAggregateRuleSet &RuleSet =
|
|
Support.SubjectsToRules.find(Subject.value())->getSecond();
|
|
if (RuleSet.isRule()) {
|
|
OS << RuleSet.getRule().getEnumValueName();
|
|
continue;
|
|
}
|
|
OS << "(";
|
|
for (const auto &Rule : llvm::enumerate(RuleSet.getAggregateRuleSet())) {
|
|
if (Rule.index())
|
|
OS << ", ";
|
|
OS << Rule.value().getEnumValueName();
|
|
}
|
|
OS << ")";
|
|
}
|
|
OS << ")\n";
|
|
}
|
|
OS << "End of supported attributes.\n";
|
|
}
|
|
|
|
} // end namespace clang
|