xref: /openbsd-src/gnu/llvm/clang/utils/TableGen/ClangOptionDocEmitter.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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