summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/TableGen/CMakeLists.txt1
-rw-r--r--utils/TableGen/ClangAttrEmitter.cpp86
-rw-r--r--utils/TableGen/ClangOptionDocEmitter.cpp391
-rw-r--r--utils/TableGen/TableGen.cpp10
-rw-r--r--utils/TableGen/TableGenBackends.h1
5 files changed, 450 insertions, 39 deletions
diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt
index 29a1eedb7af8b..c8e9537cb56bb 100644
--- a/utils/TableGen/CMakeLists.txt
+++ b/utils/TableGen/CMakeLists.txt
@@ -7,6 +7,7 @@ add_tablegen(clang-tblgen CLANG
ClangCommentHTMLNamedCharacterReferenceEmitter.cpp
ClangCommentHTMLTagsEmitter.cpp
ClangDiagnosticsEmitter.cpp
+ ClangOptionDocEmitter.cpp
ClangSACheckersEmitter.cpp
NeonEmitter.cpp
TableGen.cpp
diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp
index 27ab34c1309d0..8aaa28beaac2a 100644
--- a/utils/TableGen/ClangAttrEmitter.cpp
+++ b/utils/TableGen/ClangAttrEmitter.cpp
@@ -90,13 +90,13 @@ GetFlattenedSpellings(const Record &Attr) {
static std::string ReadPCHRecord(StringRef type) {
return StringSwitch<std::string>(type)
- .EndsWith("Decl *", "GetLocalDeclAs<"
- + std::string(type, 0, type.size()-1) + ">(F, Record[Idx++])")
- .Case("TypeSourceInfo *", "GetTypeSourceInfo(F, Record, Idx)")
- .Case("Expr *", "ReadExpr(F)")
- .Case("IdentifierInfo *", "GetIdentifierInfo(F, Record, Idx)")
- .Case("StringRef", "ReadString(Record, Idx)")
- .Default("Record[Idx++]");
+ .EndsWith("Decl *", "Record.GetLocalDeclAs<"
+ + std::string(type, 0, type.size()-1) + ">(Record.readInt())")
+ .Case("TypeSourceInfo *", "Record.getTypeSourceInfo()")
+ .Case("Expr *", "Record.readExpr()")
+ .Case("IdentifierInfo *", "Record.getIdentifierInfo()")
+ .Case("StringRef", "Record.readString()")
+ .Default("Record.readInt()");
}
// Get a type that is suitable for storing an object of the specified type.
@@ -413,7 +413,7 @@ namespace {
void writePCHReadDecls(raw_ostream &OS) const override {
OS << " std::string " << getLowerName()
- << "= ReadString(Record, Idx);\n";
+ << "= Record.readString();\n";
}
void writePCHReadArgs(raw_ostream &OS) const override {
@@ -539,13 +539,13 @@ namespace {
}
void writePCHReadDecls(raw_ostream &OS) const override {
- OS << " bool is" << getLowerName() << "Expr = Record[Idx++];\n";
+ OS << " bool is" << getLowerName() << "Expr = Record.readInt();\n";
OS << " void *" << getLowerName() << "Ptr;\n";
OS << " if (is" << getLowerName() << "Expr)\n";
- OS << " " << getLowerName() << "Ptr = ReadExpr(F);\n";
+ OS << " " << getLowerName() << "Ptr = Record.readExpr();\n";
OS << " else\n";
OS << " " << getLowerName()
- << "Ptr = GetTypeSourceInfo(F, Record, Idx);\n";
+ << "Ptr = Record.getTypeSourceInfo();\n";
}
void writePCHWrite(raw_ostream &OS) const override {
@@ -658,7 +658,7 @@ namespace {
}
void writePCHReadDecls(raw_ostream &OS) const override {
- OS << " unsigned " << getLowerName() << "Size = Record[Idx++];\n";
+ OS << " unsigned " << getLowerName() << "Size = Record.readInt();\n";
OS << " SmallVector<" << getType() << ", 4> "
<< getLowerName() << ";\n";
OS << " " << getLowerName() << ".reserve(" << getLowerName()
@@ -783,7 +783,7 @@ namespace {
void writePCHReadDecls(raw_ostream &OS) const override {
OS << " " << getAttrName() << "Attr::" << type << " " << getLowerName()
<< "(static_cast<" << getAttrName() << "Attr::" << type
- << ">(Record[Idx++]));\n";
+ << ">(Record.readInt()));\n";
}
void writePCHReadArgs(raw_ostream &OS) const override {
@@ -906,14 +906,14 @@ namespace {
}
void writePCHReadDecls(raw_ostream &OS) const override {
- OS << " unsigned " << getLowerName() << "Size = Record[Idx++];\n";
+ 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[Idx++]));\n";
+ << QualifiedTypeName << ">(Record.readInt()));\n";
}
void writePCHWrite(raw_ostream &OS) const override {
@@ -996,7 +996,7 @@ namespace {
void writePCHReadDecls(raw_ostream &OS) const override {
OS << " VersionTuple " << getLowerName()
- << "= ReadVersionTuple(Record, Idx);\n";
+ << "= Record.readVersionTuple();\n";
}
void writePCHReadArgs(raw_ostream &OS) const override {
@@ -1036,7 +1036,7 @@ namespace {
OS << " " << getType() << " tempInst" << getUpperName() << ";\n";
OS << " {\n";
OS << " EnterExpressionEvaluationContext "
- << "Unevaluated(S, Sema::Unevaluated);\n";
+ << "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n";
OS << " ExprResult " << "Result = S.SubstExpr("
<< "A->get" << getUpperName() << "(), TemplateArgs);\n";
OS << " tempInst" << getUpperName() << " = "
@@ -1083,7 +1083,7 @@ namespace {
<< "[A->" << getLowerName() << "_size()];\n";
OS << " {\n";
OS << " EnterExpressionEvaluationContext "
- << "Unevaluated(S, Sema::Unevaluated);\n";
+ << "Unevaluated(S, Sema::ExpressionEvaluationContext::Unevaluated);\n";
OS << " " << getType() << " *TI = tempInst" << getUpperName()
<< ";\n";
OS << " " << getType() << " *I = A->" << getLowerName()
@@ -2126,9 +2126,9 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) {
OS << " case attr::" << R.getName() << ": {\n";
if (R.isSubClassOf(InhClass))
- OS << " bool isInherited = Record[Idx++];\n";
- OS << " bool isImplicit = Record[Idx++];\n";
- OS << " unsigned Spelling = Record[Idx++];\n";
+ OS << " bool isInherited = Record.readInt();\n";
+ OS << " bool isImplicit = Record.readInt();\n";
+ OS << " unsigned Spelling = Record.readInt();\n";
ArgRecords = R.getValueAsListOfDefs("Args");
Args.clear();
for (const auto *Arg : ArgRecords) {
@@ -2451,26 +2451,19 @@ void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) {
OS << "#endif // ATTR_VISITOR_DECLS_ONLY\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"
- << " switch (At->getKind()) {\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");
+ bool ShouldClone = R.getValueAsBit("Clone") &&
+ (!AppliesToDecl ||
+ R.getValueAsBit("MeaningfulToClassTemplateDefinition"));
if (!ShouldClone) {
OS << " return nullptr;\n";
@@ -2507,8 +2500,27 @@ void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) {
}
OS << " } // end switch\n"
<< " llvm_unreachable(\"Unknown attribute!\");\n"
- << " return nullptr;\n"
- << "}\n\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";
}
diff --git a/utils/TableGen/ClangOptionDocEmitter.cpp b/utils/TableGen/ClangOptionDocEmitter.cpp
new file mode 100644
index 0000000000000..aa7502e2c8506
--- /dev/null
+++ b/utils/TableGen/ClangOptionDocEmitter.cpp
@@ -0,0 +1,391 @@
+//===- ClangOptionDocEmitter.cpp - Documentation for command line flags ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+// FIXME: Once this has stabilized, consider moving it to LLVM.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/TableGen/Error.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <cctype>
+#include <cstring>
+#include <map>
+
+using namespace llvm;
+
+namespace clang {
+namespace docs {
+namespace {
+struct DocumentedOption {
+ Record *Option;
+ std::vector<Record*> Aliases;
+};
+struct DocumentedGroup;
+struct Documentation {
+ std::vector<DocumentedGroup> Groups;
+ std::vector<DocumentedOption> Options;
+};
+struct DocumentedGroup : Documentation {
+ Record *Group;
+};
+
+// Reorganize the records into a suitable form for emitting documentation.
+Documentation extractDocumentation(RecordKeeper &Records) {
+ Documentation Result;
+
+ // Build the tree of groups. The root in the tree is the fake option group
+ // (Record*)nullptr, which contains all top-level groups and options.
+ std::map<Record*, std::vector<Record*> > OptionsInGroup;
+ std::map<Record*, std::vector<Record*> > GroupsInGroup;
+ std::map<Record*, std::vector<Record*> > Aliases;
+
+ std::map<std::string, Record*> OptionsByName;
+ for (Record *R : Records.getAllDerivedDefinitions("Option"))
+ OptionsByName[R->getValueAsString("Name")] = R;
+
+ auto Flatten = [](Record *R) {
+ return R->getValue("DocFlatten") && R->getValueAsBit("DocFlatten");
+ };
+
+ auto SkipFlattened = [&](Record *R) -> Record* {
+ while (R && Flatten(R)) {
+ auto *G = dyn_cast<DefInit>(R->getValueInit("Group"));
+ if (!G)
+ return nullptr;
+ R = G->getDef();
+ }
+ return R;
+ };
+
+ for (Record *R : Records.getAllDerivedDefinitions("OptionGroup")) {
+ if (Flatten(R))
+ continue;
+
+ Record *Group = nullptr;
+ if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
+ Group = SkipFlattened(G->getDef());
+ GroupsInGroup[Group].push_back(R);
+ }
+
+ for (Record *R : Records.getAllDerivedDefinitions("Option")) {
+ if (auto *A = dyn_cast<DefInit>(R->getValueInit("Alias"))) {
+ Aliases[A->getDef()].push_back(R);
+ continue;
+ }
+
+ // Pretend no-X and Xno-Y options are aliases of X and XY.
+ auto Name = R->getValueAsString("Name");
+ if (Name.size() >= 4) {
+ if (Name.substr(0, 3) == "no-" && OptionsByName[Name.substr(3)]) {
+ Aliases[OptionsByName[Name.substr(3)]].push_back(R);
+ continue;
+ }
+ if (Name.substr(1, 3) == "no-" && OptionsByName[Name[0] + Name.substr(4)]) {
+ Aliases[OptionsByName[Name[0] + Name.substr(4)]].push_back(R);
+ continue;
+ }
+ }
+
+ Record *Group = nullptr;
+ if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
+ Group = SkipFlattened(G->getDef());
+ OptionsInGroup[Group].push_back(R);
+ }
+
+ auto CompareByName = [](Record *A, Record *B) {
+ return A->getValueAsString("Name") < B->getValueAsString("Name");
+ };
+
+ auto CompareByLocation = [](Record *A, Record *B) {
+ return A->getLoc()[0].getPointer() < B->getLoc()[0].getPointer();
+ };
+
+ auto DocumentationForOption = [&](Record *R) -> DocumentedOption {
+ auto &A = Aliases[R];
+ std::sort(A.begin(), A.end(), CompareByName);
+ return {R, std::move(A)};
+ };
+
+ std::function<Documentation(Record *)> DocumentationForGroup =
+ [&](Record *R) -> Documentation {
+ Documentation D;
+
+ auto &Groups = GroupsInGroup[R];
+ std::sort(Groups.begin(), Groups.end(), CompareByLocation);
+ for (Record *G : Groups) {
+ D.Groups.emplace_back();
+ D.Groups.back().Group = G;
+ Documentation &Base = D.Groups.back();
+ Base = DocumentationForGroup(G);
+ }
+
+ auto &Options = OptionsInGroup[R];
+ std::sort(Options.begin(), Options.end(), CompareByName);
+ for (Record *O : Options)
+ D.Options.push_back(DocumentationForOption(O));
+
+ return D;
+ };
+
+ return DocumentationForGroup(nullptr);
+}
+
+// Get the first and successive separators to use for an OptionKind.
+std::pair<StringRef,StringRef> getSeparatorsForKind(const Record *OptionKind) {
+ return StringSwitch<std::pair<StringRef, StringRef>>(OptionKind->getName())
+ .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE",
+ "KIND_JOINED_AND_SEPARATE",
+ "KIND_REMAINING_ARGS_JOINED", {"", " "})
+ .Case("KIND_COMMAJOINED", {"", ","})
+ .Default({" ", " "});
+}
+
+const unsigned UnlimitedArgs = unsigned(-1);
+
+// Get the number of arguments expected for an option, or -1 if any number of
+// arguments are accepted.
+unsigned getNumArgsForKind(Record *OptionKind, const Record *Option) {
+ return StringSwitch<unsigned>(OptionKind->getName())
+ .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", "KIND_SEPARATE", 1)
+ .Cases("KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOINED",
+ "KIND_COMMAJOINED", UnlimitedArgs)
+ .Case("KIND_JOINED_AND_SEPARATE", 2)
+ .Case("KIND_MULTIARG", Option->getValueAsInt("NumArgs"))
+ .Default(0);
+}
+
+bool hasFlag(const Record *OptionOrGroup, StringRef OptionFlag) {
+ for (const Record *Flag : OptionOrGroup->getValueAsListOfDefs("Flags"))
+ if (Flag->getName() == OptionFlag)
+ return true;
+ return false;
+}
+
+bool isExcluded(const Record *OptionOrGroup, const Record *DocInfo) {
+ // FIXME: Provide a flag to specify the set of exclusions.
+ for (StringRef Exclusion : DocInfo->getValueAsListOfStrings("ExcludedFlags"))
+ if (hasFlag(OptionOrGroup, Exclusion))
+ return true;
+ return false;
+}
+
+std::string escapeRST(StringRef Str) {
+ std::string Out;
+ for (auto K : Str) {
+ if (StringRef("`*|_[]\\").count(K))
+ Out.push_back('\\');
+ Out.push_back(K);
+ }
+ return Out;
+}
+
+StringRef getSphinxOptionID(StringRef OptionName) {
+ for (auto I = OptionName.begin(), E = OptionName.end(); I != E; ++I)
+ if (!isalnum(*I) && *I != '-')
+ return OptionName.substr(0, I - OptionName.begin());
+ return OptionName;
+}
+
+bool canSphinxCopeWithOption(const Record *Option) {
+ // HACK: Work arond sphinx's inability to cope with punctuation-only options
+ // such as /? by suppressing them from the option list.
+ for (char C : Option->getValueAsString("Name"))
+ if (isalnum(C))
+ return true;
+ return false;
+}
+
+void emitHeading(int Depth, std::string Heading, raw_ostream &OS) {
+ assert(Depth < 8 && "groups nested too deeply");
+ OS << Heading << '\n'
+ << std::string(Heading.size(), "=~-_'+<>"[Depth]) << "\n";
+}
+
+/// Get the value of field \p Primary, if possible. If \p Primary does not
+/// exist, get the value of \p Fallback and escape it for rST emission.
+std::string getRSTStringWithTextFallback(const Record *R, StringRef Primary,
+ StringRef Fallback) {
+ for (auto Field : {Primary, Fallback}) {
+ if (auto *V = R->getValue(Field)) {
+ StringRef Value;
+ if (auto *SV = dyn_cast_or_null<StringInit>(V->getValue()))
+ Value = SV->getValue();
+ else if (auto *CV = dyn_cast_or_null<CodeInit>(V->getValue()))
+ Value = CV->getValue();
+ if (!Value.empty())
+ return Field == Primary ? Value.str() : escapeRST(Value);
+ }
+ }
+ return StringRef();
+}
+
+void emitOptionWithArgs(StringRef Prefix, const Record *Option,
+ ArrayRef<std::string> Args, raw_ostream &OS) {
+ OS << Prefix << escapeRST(Option->getValueAsString("Name"));
+
+ std::pair<StringRef, StringRef> Separators =
+ getSeparatorsForKind(Option->getValueAsDef("Kind"));
+
+ StringRef Separator = Separators.first;
+ for (auto Arg : Args) {
+ OS << Separator << escapeRST(Arg);
+ Separator = Separators.second;
+ }
+}
+
+void emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) {
+ // Find the arguments to list after the option.
+ unsigned NumArgs = getNumArgsForKind(Option->getValueAsDef("Kind"), Option);
+
+ std::vector<std::string> Args;
+ if (!Option->isValueUnset("MetaVarName"))
+ Args.push_back(Option->getValueAsString("MetaVarName"));
+ else if (NumArgs == 1)
+ Args.push_back("<arg>");
+
+ while (Args.size() < NumArgs) {
+ Args.push_back(("<arg" + Twine(Args.size() + 1) + ">").str());
+ // Use '--args <arg1> <arg2>...' if any number of args are allowed.
+ if (Args.size() == 2 && NumArgs == UnlimitedArgs) {
+ Args.back() += "...";
+ break;
+ }
+ }
+
+ emitOptionWithArgs(Prefix, Option, Args, OS);
+
+ auto AliasArgs = Option->getValueAsListOfStrings("AliasArgs");
+ if (!AliasArgs.empty()) {
+ Record *Alias = Option->getValueAsDef("Alias");
+ OS << " (equivalent to ";
+ emitOptionWithArgs(Alias->getValueAsListOfStrings("Prefixes").front(),
+ Alias, Option->getValueAsListOfStrings("AliasArgs"), OS);
+ OS << ")";
+ }
+}
+
+bool emitOptionNames(const Record *Option, raw_ostream &OS, bool EmittedAny) {
+ for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) {
+ if (EmittedAny)
+ OS << ", ";
+ emitOptionName(Prefix, Option, OS);
+ EmittedAny = true;
+ }
+ return EmittedAny;
+}
+
+template <typename Fn>
+void forEachOptionName(const DocumentedOption &Option, const Record *DocInfo,
+ Fn F) {
+ F(Option.Option);
+
+ for (auto *Alias : Option.Aliases)
+ if (!isExcluded(Alias, DocInfo) && canSphinxCopeWithOption(Option.Option))
+ F(Alias);
+}
+
+void emitOption(const DocumentedOption &Option, const Record *DocInfo,
+ raw_ostream &OS) {
+ if (isExcluded(Option.Option, DocInfo))
+ return;
+ if (Option.Option->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" ||
+ Option.Option->getValueAsDef("Kind")->getName() == "KIND_INPUT")
+ return;
+ if (!canSphinxCopeWithOption(Option.Option))
+ return;
+
+ // HACK: Emit a different program name with each option to work around
+ // sphinx's inability to cope with options that differ only by punctuation
+ // (eg -ObjC vs -ObjC++, -G vs -G=).
+ std::vector<std::string> SphinxOptionIDs;
+ forEachOptionName(Option, DocInfo, [&](const Record *Option) {
+ for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes"))
+ SphinxOptionIDs.push_back(
+ getSphinxOptionID(Prefix + Option->getValueAsString("Name")));
+ });
+ assert(!SphinxOptionIDs.empty() && "no flags for option");
+ static std::map<std::string, int> NextSuffix;
+ int SphinxWorkaroundSuffix = NextSuffix[*std::max_element(
+ SphinxOptionIDs.begin(), SphinxOptionIDs.end(),
+ [&](const std::string &A, const std::string &B) {
+ return NextSuffix[A] < NextSuffix[B];
+ })];
+ for (auto &S : SphinxOptionIDs)
+ NextSuffix[S] = SphinxWorkaroundSuffix + 1;
+ if (SphinxWorkaroundSuffix)
+ OS << ".. program:: " << DocInfo->getValueAsString("Program")
+ << SphinxWorkaroundSuffix << "\n";
+
+ // Emit the names of the option.
+ OS << ".. option:: ";
+ bool EmittedAny = false;
+ forEachOptionName(Option, DocInfo, [&](const Record *Option) {
+ EmittedAny = emitOptionNames(Option, OS, EmittedAny);
+ });
+ if (SphinxWorkaroundSuffix)
+ OS << "\n.. program:: " << DocInfo->getValueAsString("Program");
+ OS << "\n\n";
+
+ // Emit the description, if we have one.
+ std::string Description =
+ getRSTStringWithTextFallback(Option.Option, "DocBrief", "HelpText");
+ if (!Description.empty())
+ OS << Description << "\n\n";
+}
+
+void emitDocumentation(int Depth, const Documentation &Doc,
+ const Record *DocInfo, raw_ostream &OS);
+
+void emitGroup(int Depth, const DocumentedGroup &Group, const Record *DocInfo,
+ raw_ostream &OS) {
+ if (isExcluded(Group.Group, DocInfo))
+ return;
+
+ emitHeading(Depth,
+ getRSTStringWithTextFallback(Group.Group, "DocName", "Name"), OS);
+
+ // Emit the description, if we have one.
+ std::string Description =
+ getRSTStringWithTextFallback(Group.Group, "DocBrief", "HelpText");
+ if (!Description.empty())
+ OS << Description << "\n\n";
+
+ // Emit contained options and groups.
+ emitDocumentation(Depth + 1, Group, DocInfo, OS);
+}
+
+void emitDocumentation(int Depth, const Documentation &Doc,
+ const Record *DocInfo, raw_ostream &OS) {
+ for (auto &O : Doc.Options)
+ emitOption(O, DocInfo, OS);
+ for (auto &G : Doc.Groups)
+ emitGroup(Depth, G, DocInfo, OS);
+}
+
+} // namespace
+} // namespace docs
+
+void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) {
+ using namespace docs;
+
+ const Record *DocInfo = Records.getDef("GlobalDocumentation");
+ if (!DocInfo) {
+ PrintFatalError("The GlobalDocumentation top-level definition is missing, "
+ "no documentation will be generated.");
+ return;
+ }
+ OS << DocInfo->getValueAsString("Intro") << "\n";
+ OS << ".. program:: " << DocInfo->getValueAsString("Program") << "\n";
+
+ emitDocumentation(0, extractDocumentation(Records), DocInfo, OS);
+}
+} // end namespace clang
diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp
index 6fb5b00c4bac5..fd7999be38774 100644
--- a/utils/TableGen/TableGen.cpp
+++ b/utils/TableGen/TableGen.cpp
@@ -53,7 +53,8 @@ enum ActionType {
GenArmNeonSema,
GenArmNeonTest,
GenAttrDocs,
- GenDiagDocs
+ GenDiagDocs,
+ GenOptDocs
};
namespace {
@@ -135,7 +136,9 @@ cl::opt<ActionType> Action(
clEnumValN(GenAttrDocs, "gen-attr-docs",
"Generate attribute documentation"),
clEnumValN(GenDiagDocs, "gen-diag-docs",
- "Generate attribute documentation")));
+ "Generate diagnostic documentation"),
+ clEnumValN(GenOptDocs, "gen-opt-docs",
+ "Generate option documentation")));
cl::opt<std::string>
ClangComponent("clang-component",
@@ -238,6 +241,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenDiagDocs:
EmitClangDiagDocs(Records, OS);
break;
+ case GenOptDocs:
+ EmitClangOptDocs(Records, OS);
+ break;
}
return false;
diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h
index 0305ed1c8c8f5..033cb78f36f36 100644
--- a/utils/TableGen/TableGenBackends.h
+++ b/utils/TableGen/TableGenBackends.h
@@ -70,6 +70,7 @@ void EmitNeonTest2(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS);
void EmitClangDiagDocs(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS);
} // end namespace clang