1e5dd7070Spatrick //===- ClangOptionDocEmitter.cpp - Documentation for command line flags ---===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick // FIXME: Once this has stabilized, consider moving it to LLVM.
8e5dd7070Spatrick //
9e5dd7070Spatrick //===----------------------------------------------------------------------===//
10e5dd7070Spatrick
11e5dd7070Spatrick #include "TableGenBackends.h"
12e5dd7070Spatrick #include "llvm/TableGen/Error.h"
13e5dd7070Spatrick #include "llvm/ADT/STLExtras.h"
14e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
15e5dd7070Spatrick #include "llvm/ADT/StringSwitch.h"
16e5dd7070Spatrick #include "llvm/ADT/Twine.h"
17e5dd7070Spatrick #include "llvm/TableGen/Record.h"
18e5dd7070Spatrick #include "llvm/TableGen/TableGenBackend.h"
19e5dd7070Spatrick #include <cctype>
20e5dd7070Spatrick #include <cstring>
21e5dd7070Spatrick #include <map>
22e5dd7070Spatrick
23e5dd7070Spatrick using namespace llvm;
24e5dd7070Spatrick
25e5dd7070Spatrick namespace {
26e5dd7070Spatrick struct DocumentedOption {
27e5dd7070Spatrick Record *Option;
28e5dd7070Spatrick std::vector<Record*> Aliases;
29e5dd7070Spatrick };
30e5dd7070Spatrick struct DocumentedGroup;
31e5dd7070Spatrick struct Documentation {
32e5dd7070Spatrick std::vector<DocumentedGroup> Groups;
33e5dd7070Spatrick std::vector<DocumentedOption> Options;
34e5dd7070Spatrick };
35e5dd7070Spatrick struct DocumentedGroup : Documentation {
36e5dd7070Spatrick Record *Group;
37e5dd7070Spatrick };
38e5dd7070Spatrick
39e5dd7070Spatrick // Reorganize the records into a suitable form for emitting documentation.
extractDocumentation(RecordKeeper & Records)40e5dd7070Spatrick Documentation extractDocumentation(RecordKeeper &Records) {
41e5dd7070Spatrick Documentation Result;
42e5dd7070Spatrick
43e5dd7070Spatrick // Build the tree of groups. The root in the tree is the fake option group
44e5dd7070Spatrick // (Record*)nullptr, which contains all top-level groups and options.
45e5dd7070Spatrick std::map<Record*, std::vector<Record*> > OptionsInGroup;
46e5dd7070Spatrick std::map<Record*, std::vector<Record*> > GroupsInGroup;
47e5dd7070Spatrick std::map<Record*, std::vector<Record*> > Aliases;
48e5dd7070Spatrick
49e5dd7070Spatrick std::map<std::string, Record*> OptionsByName;
50e5dd7070Spatrick for (Record *R : Records.getAllDerivedDefinitions("Option"))
51ec727ea7Spatrick OptionsByName[std::string(R->getValueAsString("Name"))] = R;
52e5dd7070Spatrick
53e5dd7070Spatrick auto Flatten = [](Record *R) {
54e5dd7070Spatrick return R->getValue("DocFlatten") && R->getValueAsBit("DocFlatten");
55e5dd7070Spatrick };
56e5dd7070Spatrick
57e5dd7070Spatrick auto SkipFlattened = [&](Record *R) -> Record* {
58e5dd7070Spatrick while (R && Flatten(R)) {
59e5dd7070Spatrick auto *G = dyn_cast<DefInit>(R->getValueInit("Group"));
60e5dd7070Spatrick if (!G)
61e5dd7070Spatrick return nullptr;
62e5dd7070Spatrick R = G->getDef();
63e5dd7070Spatrick }
64e5dd7070Spatrick return R;
65e5dd7070Spatrick };
66e5dd7070Spatrick
67e5dd7070Spatrick for (Record *R : Records.getAllDerivedDefinitions("OptionGroup")) {
68e5dd7070Spatrick if (Flatten(R))
69e5dd7070Spatrick continue;
70e5dd7070Spatrick
71e5dd7070Spatrick Record *Group = nullptr;
72e5dd7070Spatrick if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
73e5dd7070Spatrick Group = SkipFlattened(G->getDef());
74e5dd7070Spatrick GroupsInGroup[Group].push_back(R);
75e5dd7070Spatrick }
76e5dd7070Spatrick
77e5dd7070Spatrick for (Record *R : Records.getAllDerivedDefinitions("Option")) {
78e5dd7070Spatrick if (auto *A = dyn_cast<DefInit>(R->getValueInit("Alias"))) {
79e5dd7070Spatrick Aliases[A->getDef()].push_back(R);
80e5dd7070Spatrick continue;
81e5dd7070Spatrick }
82e5dd7070Spatrick
83e5dd7070Spatrick // Pretend no-X and Xno-Y options are aliases of X and XY.
84ec727ea7Spatrick std::string Name = std::string(R->getValueAsString("Name"));
85e5dd7070Spatrick if (Name.size() >= 4) {
86e5dd7070Spatrick if (Name.substr(0, 3) == "no-" && OptionsByName[Name.substr(3)]) {
87e5dd7070Spatrick Aliases[OptionsByName[Name.substr(3)]].push_back(R);
88e5dd7070Spatrick continue;
89e5dd7070Spatrick }
90e5dd7070Spatrick if (Name.substr(1, 3) == "no-" && OptionsByName[Name[0] + Name.substr(4)]) {
91e5dd7070Spatrick Aliases[OptionsByName[Name[0] + Name.substr(4)]].push_back(R);
92e5dd7070Spatrick continue;
93e5dd7070Spatrick }
94e5dd7070Spatrick }
95e5dd7070Spatrick
96e5dd7070Spatrick Record *Group = nullptr;
97e5dd7070Spatrick if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group")))
98e5dd7070Spatrick Group = SkipFlattened(G->getDef());
99e5dd7070Spatrick OptionsInGroup[Group].push_back(R);
100e5dd7070Spatrick }
101e5dd7070Spatrick
102e5dd7070Spatrick auto CompareByName = [](Record *A, Record *B) {
103e5dd7070Spatrick return A->getValueAsString("Name") < B->getValueAsString("Name");
104e5dd7070Spatrick };
105e5dd7070Spatrick
106e5dd7070Spatrick auto CompareByLocation = [](Record *A, Record *B) {
107e5dd7070Spatrick return A->getLoc()[0].getPointer() < B->getLoc()[0].getPointer();
108e5dd7070Spatrick };
109e5dd7070Spatrick
110e5dd7070Spatrick auto DocumentationForOption = [&](Record *R) -> DocumentedOption {
111e5dd7070Spatrick auto &A = Aliases[R];
112e5dd7070Spatrick llvm::sort(A, CompareByName);
113e5dd7070Spatrick return {R, std::move(A)};
114e5dd7070Spatrick };
115e5dd7070Spatrick
116e5dd7070Spatrick std::function<Documentation(Record *)> DocumentationForGroup =
117e5dd7070Spatrick [&](Record *R) -> Documentation {
118e5dd7070Spatrick Documentation D;
119e5dd7070Spatrick
120e5dd7070Spatrick auto &Groups = GroupsInGroup[R];
121e5dd7070Spatrick llvm::sort(Groups, CompareByLocation);
122e5dd7070Spatrick for (Record *G : Groups) {
123e5dd7070Spatrick D.Groups.emplace_back();
124e5dd7070Spatrick D.Groups.back().Group = G;
125e5dd7070Spatrick Documentation &Base = D.Groups.back();
126e5dd7070Spatrick Base = DocumentationForGroup(G);
127e5dd7070Spatrick }
128e5dd7070Spatrick
129e5dd7070Spatrick auto &Options = OptionsInGroup[R];
130e5dd7070Spatrick llvm::sort(Options, CompareByName);
131e5dd7070Spatrick for (Record *O : Options)
132e5dd7070Spatrick D.Options.push_back(DocumentationForOption(O));
133e5dd7070Spatrick
134e5dd7070Spatrick return D;
135e5dd7070Spatrick };
136e5dd7070Spatrick
137e5dd7070Spatrick return DocumentationForGroup(nullptr);
138e5dd7070Spatrick }
139e5dd7070Spatrick
140e5dd7070Spatrick // Get the first and successive separators to use for an OptionKind.
getSeparatorsForKind(const Record * OptionKind)141e5dd7070Spatrick std::pair<StringRef,StringRef> getSeparatorsForKind(const Record *OptionKind) {
142e5dd7070Spatrick return StringSwitch<std::pair<StringRef, StringRef>>(OptionKind->getName())
143e5dd7070Spatrick .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE",
144e5dd7070Spatrick "KIND_JOINED_AND_SEPARATE",
145e5dd7070Spatrick "KIND_REMAINING_ARGS_JOINED", {"", " "})
146e5dd7070Spatrick .Case("KIND_COMMAJOINED", {"", ","})
147e5dd7070Spatrick .Default({" ", " "});
148e5dd7070Spatrick }
149e5dd7070Spatrick
150e5dd7070Spatrick const unsigned UnlimitedArgs = unsigned(-1);
151e5dd7070Spatrick
152e5dd7070Spatrick // Get the number of arguments expected for an option, or -1 if any number of
153e5dd7070Spatrick // arguments are accepted.
getNumArgsForKind(Record * OptionKind,const Record * Option)154e5dd7070Spatrick unsigned getNumArgsForKind(Record *OptionKind, const Record *Option) {
155e5dd7070Spatrick return StringSwitch<unsigned>(OptionKind->getName())
156e5dd7070Spatrick .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", "KIND_SEPARATE", 1)
157e5dd7070Spatrick .Cases("KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOINED",
158e5dd7070Spatrick "KIND_COMMAJOINED", UnlimitedArgs)
159e5dd7070Spatrick .Case("KIND_JOINED_AND_SEPARATE", 2)
160e5dd7070Spatrick .Case("KIND_MULTIARG", Option->getValueAsInt("NumArgs"))
161e5dd7070Spatrick .Default(0);
162e5dd7070Spatrick }
163e5dd7070Spatrick
hasFlag(const Record * OptionOrGroup,StringRef OptionFlag)164e5dd7070Spatrick bool hasFlag(const Record *OptionOrGroup, StringRef OptionFlag) {
165e5dd7070Spatrick for (const Record *Flag : OptionOrGroup->getValueAsListOfDefs("Flags"))
166e5dd7070Spatrick if (Flag->getName() == OptionFlag)
167e5dd7070Spatrick return true;
168e5dd7070Spatrick return false;
169e5dd7070Spatrick }
170e5dd7070Spatrick
isIncluded(const Record * OptionOrGroup,const Record * DocInfo)171*12c85518Srobert bool isIncluded(const Record *OptionOrGroup, const Record *DocInfo) {
172*12c85518Srobert assert(DocInfo->getValue("IncludedFlags") && "Missing includeFlags");
173*12c85518Srobert for (StringRef Inclusion : DocInfo->getValueAsListOfStrings("IncludedFlags"))
174*12c85518Srobert if (hasFlag(OptionOrGroup, Inclusion))
175*12c85518Srobert return true;
176*12c85518Srobert return false;
177*12c85518Srobert }
178*12c85518Srobert
isGroupIncluded(const DocumentedGroup & Group,const Record * DocInfo)179*12c85518Srobert bool isGroupIncluded(const DocumentedGroup &Group, const Record *DocInfo) {
180*12c85518Srobert if (isIncluded(Group.Group, DocInfo))
181*12c85518Srobert return true;
182*12c85518Srobert for (auto &O : Group.Options)
183*12c85518Srobert if (isIncluded(O.Option, DocInfo))
184*12c85518Srobert return true;
185*12c85518Srobert for (auto &G : Group.Groups) {
186*12c85518Srobert if (isIncluded(G.Group, DocInfo))
187*12c85518Srobert return true;
188*12c85518Srobert if (isGroupIncluded(G, DocInfo))
189*12c85518Srobert return true;
190*12c85518Srobert }
191*12c85518Srobert return false;
192*12c85518Srobert }
193*12c85518Srobert
isExcluded(const Record * OptionOrGroup,const Record * DocInfo)194e5dd7070Spatrick bool isExcluded(const Record *OptionOrGroup, const Record *DocInfo) {
195e5dd7070Spatrick // FIXME: Provide a flag to specify the set of exclusions.
196e5dd7070Spatrick for (StringRef Exclusion : DocInfo->getValueAsListOfStrings("ExcludedFlags"))
197e5dd7070Spatrick if (hasFlag(OptionOrGroup, Exclusion))
198e5dd7070Spatrick return true;
199e5dd7070Spatrick return false;
200e5dd7070Spatrick }
201e5dd7070Spatrick
escapeRST(StringRef Str)202e5dd7070Spatrick std::string escapeRST(StringRef Str) {
203e5dd7070Spatrick std::string Out;
204e5dd7070Spatrick for (auto K : Str) {
205e5dd7070Spatrick if (StringRef("`*|_[]\\").count(K))
206e5dd7070Spatrick Out.push_back('\\');
207e5dd7070Spatrick Out.push_back(K);
208e5dd7070Spatrick }
209e5dd7070Spatrick return Out;
210e5dd7070Spatrick }
211e5dd7070Spatrick
getSphinxOptionID(StringRef OptionName)212e5dd7070Spatrick StringRef getSphinxOptionID(StringRef OptionName) {
213e5dd7070Spatrick for (auto I = OptionName.begin(), E = OptionName.end(); I != E; ++I)
214e5dd7070Spatrick if (!isalnum(*I) && *I != '-')
215e5dd7070Spatrick return OptionName.substr(0, I - OptionName.begin());
216e5dd7070Spatrick return OptionName;
217e5dd7070Spatrick }
218e5dd7070Spatrick
canSphinxCopeWithOption(const Record * Option)219e5dd7070Spatrick bool canSphinxCopeWithOption(const Record *Option) {
220e5dd7070Spatrick // HACK: Work arond sphinx's inability to cope with punctuation-only options
221e5dd7070Spatrick // such as /? by suppressing them from the option list.
222e5dd7070Spatrick for (char C : Option->getValueAsString("Name"))
223e5dd7070Spatrick if (isalnum(C))
224e5dd7070Spatrick return true;
225e5dd7070Spatrick return false;
226e5dd7070Spatrick }
227e5dd7070Spatrick
emitHeading(int Depth,std::string Heading,raw_ostream & OS)228e5dd7070Spatrick void emitHeading(int Depth, std::string Heading, raw_ostream &OS) {
229e5dd7070Spatrick assert(Depth < 8 && "groups nested too deeply");
230e5dd7070Spatrick OS << Heading << '\n'
231e5dd7070Spatrick << std::string(Heading.size(), "=~-_'+<>"[Depth]) << "\n";
232e5dd7070Spatrick }
233e5dd7070Spatrick
234e5dd7070Spatrick /// Get the value of field \p Primary, if possible. If \p Primary does not
235e5dd7070Spatrick /// exist, get the value of \p Fallback and escape it for rST emission.
getRSTStringWithTextFallback(const Record * R,StringRef Primary,StringRef Fallback)236e5dd7070Spatrick std::string getRSTStringWithTextFallback(const Record *R, StringRef Primary,
237e5dd7070Spatrick StringRef Fallback) {
238e5dd7070Spatrick for (auto Field : {Primary, Fallback}) {
239e5dd7070Spatrick if (auto *V = R->getValue(Field)) {
240e5dd7070Spatrick StringRef Value;
241e5dd7070Spatrick if (auto *SV = dyn_cast_or_null<StringInit>(V->getValue()))
242e5dd7070Spatrick Value = SV->getValue();
243e5dd7070Spatrick if (!Value.empty())
244e5dd7070Spatrick return Field == Primary ? Value.str() : escapeRST(Value);
245e5dd7070Spatrick }
246e5dd7070Spatrick }
247ec727ea7Spatrick return std::string(StringRef());
248e5dd7070Spatrick }
249e5dd7070Spatrick
emitOptionWithArgs(StringRef Prefix,const Record * Option,ArrayRef<StringRef> Args,raw_ostream & OS)250e5dd7070Spatrick void emitOptionWithArgs(StringRef Prefix, const Record *Option,
251e5dd7070Spatrick ArrayRef<StringRef> Args, raw_ostream &OS) {
252e5dd7070Spatrick OS << Prefix << escapeRST(Option->getValueAsString("Name"));
253e5dd7070Spatrick
254e5dd7070Spatrick std::pair<StringRef, StringRef> Separators =
255e5dd7070Spatrick getSeparatorsForKind(Option->getValueAsDef("Kind"));
256e5dd7070Spatrick
257e5dd7070Spatrick StringRef Separator = Separators.first;
258e5dd7070Spatrick for (auto Arg : Args) {
259e5dd7070Spatrick OS << Separator << escapeRST(Arg);
260e5dd7070Spatrick Separator = Separators.second;
261e5dd7070Spatrick }
262e5dd7070Spatrick }
263e5dd7070Spatrick
264*12c85518Srobert constexpr StringLiteral DefaultMetaVarName = "<arg>";
265*12c85518Srobert
emitOptionName(StringRef Prefix,const Record * Option,raw_ostream & OS)266e5dd7070Spatrick void emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) {
267e5dd7070Spatrick // Find the arguments to list after the option.
268e5dd7070Spatrick unsigned NumArgs = getNumArgsForKind(Option->getValueAsDef("Kind"), Option);
269e5dd7070Spatrick bool HasMetaVarName = !Option->isValueUnset("MetaVarName");
270e5dd7070Spatrick
271e5dd7070Spatrick std::vector<std::string> Args;
272e5dd7070Spatrick if (HasMetaVarName)
273ec727ea7Spatrick Args.push_back(std::string(Option->getValueAsString("MetaVarName")));
274e5dd7070Spatrick else if (NumArgs == 1)
275*12c85518Srobert Args.push_back(DefaultMetaVarName.str());
276e5dd7070Spatrick
277e5dd7070Spatrick // Fill up arguments if this option didn't provide a meta var name or it
278e5dd7070Spatrick // supports an unlimited number of arguments. We can't see how many arguments
279e5dd7070Spatrick // already are in a meta var name, so assume it has right number. This is
280e5dd7070Spatrick // needed for JoinedAndSeparate options so that there arent't too many
281e5dd7070Spatrick // arguments.
282e5dd7070Spatrick if (!HasMetaVarName || NumArgs == UnlimitedArgs) {
283e5dd7070Spatrick while (Args.size() < NumArgs) {
284e5dd7070Spatrick Args.push_back(("<arg" + Twine(Args.size() + 1) + ">").str());
285e5dd7070Spatrick // Use '--args <arg1> <arg2>...' if any number of args are allowed.
286e5dd7070Spatrick if (Args.size() == 2 && NumArgs == UnlimitedArgs) {
287e5dd7070Spatrick Args.back() += "...";
288e5dd7070Spatrick break;
289e5dd7070Spatrick }
290e5dd7070Spatrick }
291e5dd7070Spatrick }
292e5dd7070Spatrick
293e5dd7070Spatrick emitOptionWithArgs(Prefix, Option, std::vector<StringRef>(Args.begin(), Args.end()), OS);
294e5dd7070Spatrick
295e5dd7070Spatrick auto AliasArgs = Option->getValueAsListOfStrings("AliasArgs");
296e5dd7070Spatrick if (!AliasArgs.empty()) {
297e5dd7070Spatrick Record *Alias = Option->getValueAsDef("Alias");
298e5dd7070Spatrick OS << " (equivalent to ";
299e5dd7070Spatrick emitOptionWithArgs(
300e5dd7070Spatrick Alias->getValueAsListOfStrings("Prefixes").front(), Alias,
301e5dd7070Spatrick AliasArgs, OS);
302e5dd7070Spatrick OS << ")";
303e5dd7070Spatrick }
304e5dd7070Spatrick }
305e5dd7070Spatrick
emitOptionNames(const Record * Option,raw_ostream & OS,bool EmittedAny)306e5dd7070Spatrick bool emitOptionNames(const Record *Option, raw_ostream &OS, bool EmittedAny) {
307e5dd7070Spatrick for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) {
308e5dd7070Spatrick if (EmittedAny)
309e5dd7070Spatrick OS << ", ";
310e5dd7070Spatrick emitOptionName(Prefix, Option, OS);
311e5dd7070Spatrick EmittedAny = true;
312e5dd7070Spatrick }
313e5dd7070Spatrick return EmittedAny;
314e5dd7070Spatrick }
315e5dd7070Spatrick
316e5dd7070Spatrick template <typename Fn>
forEachOptionName(const DocumentedOption & Option,const Record * DocInfo,Fn F)317e5dd7070Spatrick void forEachOptionName(const DocumentedOption &Option, const Record *DocInfo,
318e5dd7070Spatrick Fn F) {
319e5dd7070Spatrick F(Option.Option);
320e5dd7070Spatrick
321e5dd7070Spatrick for (auto *Alias : Option.Aliases)
322e5dd7070Spatrick if (!isExcluded(Alias, DocInfo) && canSphinxCopeWithOption(Option.Option))
323e5dd7070Spatrick F(Alias);
324e5dd7070Spatrick }
325e5dd7070Spatrick
emitOption(const DocumentedOption & Option,const Record * DocInfo,raw_ostream & OS)326e5dd7070Spatrick void emitOption(const DocumentedOption &Option, const Record *DocInfo,
327e5dd7070Spatrick raw_ostream &OS) {
328e5dd7070Spatrick if (isExcluded(Option.Option, DocInfo))
329e5dd7070Spatrick return;
330*12c85518Srobert if (DocInfo->getValue("IncludedFlags") && !isIncluded(Option.Option, DocInfo))
331*12c85518Srobert return;
332e5dd7070Spatrick if (Option.Option->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" ||
333e5dd7070Spatrick Option.Option->getValueAsDef("Kind")->getName() == "KIND_INPUT")
334e5dd7070Spatrick return;
335e5dd7070Spatrick if (!canSphinxCopeWithOption(Option.Option))
336e5dd7070Spatrick return;
337e5dd7070Spatrick
338e5dd7070Spatrick // HACK: Emit a different program name with each option to work around
339e5dd7070Spatrick // sphinx's inability to cope with options that differ only by punctuation
340e5dd7070Spatrick // (eg -ObjC vs -ObjC++, -G vs -G=).
341e5dd7070Spatrick std::vector<std::string> SphinxOptionIDs;
342e5dd7070Spatrick forEachOptionName(Option, DocInfo, [&](const Record *Option) {
343e5dd7070Spatrick for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes"))
344ec727ea7Spatrick SphinxOptionIDs.push_back(std::string(getSphinxOptionID(
345ec727ea7Spatrick (Prefix + Option->getValueAsString("Name")).str())));
346e5dd7070Spatrick });
347e5dd7070Spatrick assert(!SphinxOptionIDs.empty() && "no flags for option");
348e5dd7070Spatrick static std::map<std::string, int> NextSuffix;
349e5dd7070Spatrick int SphinxWorkaroundSuffix = NextSuffix[*std::max_element(
350e5dd7070Spatrick SphinxOptionIDs.begin(), SphinxOptionIDs.end(),
351e5dd7070Spatrick [&](const std::string &A, const std::string &B) {
352e5dd7070Spatrick return NextSuffix[A] < NextSuffix[B];
353e5dd7070Spatrick })];
354e5dd7070Spatrick for (auto &S : SphinxOptionIDs)
355e5dd7070Spatrick NextSuffix[S] = SphinxWorkaroundSuffix + 1;
356e5dd7070Spatrick if (SphinxWorkaroundSuffix)
357e5dd7070Spatrick OS << ".. program:: " << DocInfo->getValueAsString("Program")
358e5dd7070Spatrick << SphinxWorkaroundSuffix << "\n";
359e5dd7070Spatrick
360e5dd7070Spatrick // Emit the names of the option.
361e5dd7070Spatrick OS << ".. option:: ";
362e5dd7070Spatrick bool EmittedAny = false;
363e5dd7070Spatrick forEachOptionName(Option, DocInfo, [&](const Record *Option) {
364e5dd7070Spatrick EmittedAny = emitOptionNames(Option, OS, EmittedAny);
365e5dd7070Spatrick });
366e5dd7070Spatrick if (SphinxWorkaroundSuffix)
367e5dd7070Spatrick OS << "\n.. program:: " << DocInfo->getValueAsString("Program");
368e5dd7070Spatrick OS << "\n\n";
369e5dd7070Spatrick
370e5dd7070Spatrick // Emit the description, if we have one.
371*12c85518Srobert const Record *R = Option.Option;
372e5dd7070Spatrick std::string Description =
373*12c85518Srobert getRSTStringWithTextFallback(R, "DocBrief", "HelpText");
374*12c85518Srobert
375*12c85518Srobert if (!isa<UnsetInit>(R->getValueInit("Values"))) {
376*12c85518Srobert if (!Description.empty() && Description.back() != '.')
377*12c85518Srobert Description.push_back('.');
378*12c85518Srobert
379*12c85518Srobert StringRef MetaVarName;
380*12c85518Srobert if (!isa<UnsetInit>(R->getValueInit("MetaVarName")))
381*12c85518Srobert MetaVarName = R->getValueAsString("MetaVarName");
382*12c85518Srobert else
383*12c85518Srobert MetaVarName = DefaultMetaVarName;
384*12c85518Srobert
385*12c85518Srobert SmallVector<StringRef> Values;
386*12c85518Srobert SplitString(R->getValueAsString("Values"), Values, ",");
387*12c85518Srobert Description += (" " + MetaVarName + " must be '").str();
388*12c85518Srobert if (Values.size() > 1) {
389*12c85518Srobert Description += join(Values.begin(), Values.end() - 1, "', '");
390*12c85518Srobert Description += "' or '";
391*12c85518Srobert }
392*12c85518Srobert Description += (Values.back() + "'.").str();
393*12c85518Srobert }
394*12c85518Srobert
395e5dd7070Spatrick if (!Description.empty())
396e5dd7070Spatrick OS << Description << "\n\n";
397e5dd7070Spatrick }
398e5dd7070Spatrick
399e5dd7070Spatrick void emitDocumentation(int Depth, const Documentation &Doc,
400e5dd7070Spatrick const Record *DocInfo, raw_ostream &OS);
401e5dd7070Spatrick
emitGroup(int Depth,const DocumentedGroup & Group,const Record * DocInfo,raw_ostream & OS)402e5dd7070Spatrick void emitGroup(int Depth, const DocumentedGroup &Group, const Record *DocInfo,
403e5dd7070Spatrick raw_ostream &OS) {
404e5dd7070Spatrick if (isExcluded(Group.Group, DocInfo))
405e5dd7070Spatrick return;
406e5dd7070Spatrick
407*12c85518Srobert if (DocInfo->getValue("IncludedFlags") && !isGroupIncluded(Group, DocInfo))
408*12c85518Srobert return;
409*12c85518Srobert
410e5dd7070Spatrick emitHeading(Depth,
411e5dd7070Spatrick getRSTStringWithTextFallback(Group.Group, "DocName", "Name"), OS);
412e5dd7070Spatrick
413e5dd7070Spatrick // Emit the description, if we have one.
414e5dd7070Spatrick std::string Description =
415e5dd7070Spatrick getRSTStringWithTextFallback(Group.Group, "DocBrief", "HelpText");
416e5dd7070Spatrick if (!Description.empty())
417e5dd7070Spatrick OS << Description << "\n\n";
418e5dd7070Spatrick
419e5dd7070Spatrick // Emit contained options and groups.
420e5dd7070Spatrick emitDocumentation(Depth + 1, Group, DocInfo, OS);
421e5dd7070Spatrick }
422e5dd7070Spatrick
emitDocumentation(int Depth,const Documentation & Doc,const Record * DocInfo,raw_ostream & OS)423e5dd7070Spatrick void emitDocumentation(int Depth, const Documentation &Doc,
424e5dd7070Spatrick const Record *DocInfo, raw_ostream &OS) {
425e5dd7070Spatrick for (auto &O : Doc.Options)
426e5dd7070Spatrick emitOption(O, DocInfo, OS);
427e5dd7070Spatrick for (auto &G : Doc.Groups)
428e5dd7070Spatrick emitGroup(Depth, G, DocInfo, OS);
429e5dd7070Spatrick }
430e5dd7070Spatrick
431e5dd7070Spatrick } // namespace
432e5dd7070Spatrick
EmitClangOptDocs(RecordKeeper & Records,raw_ostream & OS)433e5dd7070Spatrick void clang::EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) {
434e5dd7070Spatrick const Record *DocInfo = Records.getDef("GlobalDocumentation");
435e5dd7070Spatrick if (!DocInfo) {
436e5dd7070Spatrick PrintFatalError("The GlobalDocumentation top-level definition is missing, "
437e5dd7070Spatrick "no documentation will be generated.");
438e5dd7070Spatrick return;
439e5dd7070Spatrick }
440e5dd7070Spatrick OS << DocInfo->getValueAsString("Intro") << "\n";
441e5dd7070Spatrick OS << ".. program:: " << DocInfo->getValueAsString("Program") << "\n";
442e5dd7070Spatrick
443e5dd7070Spatrick emitDocumentation(0, extractDocumentation(Records), DocInfo, OS);
444e5dd7070Spatrick }
445