1 //===-- ClangOptionDocEmitter.cpp - Documentation for command line flags --===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 // FIXME: Once this has stabilized, consider moving it to LLVM.
9 //===----------------------------------------------------------------------===//
11 #include "TableGenBackends.h"
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/ADT/StringSwitch.h"
14 #include "llvm/ADT/Twine.h"
15 #include "llvm/TableGen/Error.h"
16 #include "llvm/TableGen/Record.h"
17 #include "llvm/TableGen/TableGenBackend.h"
25 struct DocumentedOption
{
27 std::vector
<const Record
*> Aliases
;
29 struct DocumentedGroup
;
30 struct Documentation
{
31 std::vector
<DocumentedGroup
> Groups
;
32 std::vector
<DocumentedOption
> Options
;
35 return Groups
.empty() && Options
.empty();
38 struct DocumentedGroup
: Documentation
{
42 static bool hasFlag(const Record
*Option
, StringRef OptionFlag
,
43 StringRef FlagsField
) {
44 for (const Record
*Flag
: Option
->getValueAsListOfDefs(FlagsField
))
45 if (Flag
->getName() == OptionFlag
)
47 if (const DefInit
*DI
= dyn_cast
<DefInit
>(Option
->getValueInit("Group")))
48 for (const Record
*Flag
: DI
->getDef()->getValueAsListOfDefs(FlagsField
))
49 if (Flag
->getName() == OptionFlag
)
54 static bool isOptionVisible(const Record
*Option
, const Record
*DocInfo
) {
55 for (StringRef IgnoredFlag
: DocInfo
->getValueAsListOfStrings("IgnoreFlags"))
56 if (hasFlag(Option
, IgnoredFlag
, "Flags"))
58 for (StringRef Mask
: DocInfo
->getValueAsListOfStrings("VisibilityMask"))
59 if (hasFlag(Option
, Mask
, "Visibility"))
64 // Reorganize the records into a suitable form for emitting documentation.
65 Documentation
extractDocumentation(const RecordKeeper
&Records
,
66 const Record
*DocInfo
) {
69 // Build the tree of groups. The root in the tree is the fake option group
70 // (Record*)nullptr, which contains all top-level groups and options.
71 std::map
<const Record
*, std::vector
<const Record
*>> OptionsInGroup
;
72 std::map
<const Record
*, std::vector
<const Record
*>> GroupsInGroup
;
73 std::map
<const Record
*, std::vector
<const Record
*>> Aliases
;
75 std::map
<std::string
, const Record
*> OptionsByName
;
76 for (const Record
*R
: Records
.getAllDerivedDefinitions("Option"))
77 OptionsByName
[std::string(R
->getValueAsString("Name"))] = R
;
79 auto Flatten
= [](const Record
*R
) {
80 return R
->getValue("DocFlatten") && R
->getValueAsBit("DocFlatten");
83 auto SkipFlattened
= [&](const Record
*R
) -> const Record
* {
84 while (R
&& Flatten(R
)) {
85 auto *G
= dyn_cast
<DefInit
>(R
->getValueInit("Group"));
93 for (const Record
*R
: Records
.getAllDerivedDefinitions("OptionGroup")) {
97 const Record
*Group
= nullptr;
98 if (auto *G
= dyn_cast
<DefInit
>(R
->getValueInit("Group")))
99 Group
= SkipFlattened(G
->getDef());
100 GroupsInGroup
[Group
].push_back(R
);
103 for (const Record
*R
: Records
.getAllDerivedDefinitions("Option")) {
104 if (auto *A
= dyn_cast
<DefInit
>(R
->getValueInit("Alias"))) {
105 Aliases
[A
->getDef()].push_back(R
);
109 // Pretend no-X and Xno-Y options are aliases of X and XY.
110 std::string Name
= std::string(R
->getValueAsString("Name"));
111 if (Name
.size() >= 4) {
112 if (Name
.substr(0, 3) == "no-" && OptionsByName
[Name
.substr(3)]) {
113 Aliases
[OptionsByName
[Name
.substr(3)]].push_back(R
);
116 if (Name
.substr(1, 3) == "no-" && OptionsByName
[Name
[0] + Name
.substr(4)]) {
117 Aliases
[OptionsByName
[Name
[0] + Name
.substr(4)]].push_back(R
);
122 const Record
*Group
= nullptr;
123 if (auto *G
= dyn_cast
<DefInit
>(R
->getValueInit("Group")))
124 Group
= SkipFlattened(G
->getDef());
125 OptionsInGroup
[Group
].push_back(R
);
128 auto CompareByName
= [](const Record
*A
, const Record
*B
) {
129 return A
->getValueAsString("Name") < B
->getValueAsString("Name");
132 auto CompareByLocation
= [](const Record
*A
, const Record
*B
) {
133 return A
->getLoc()[0].getPointer() < B
->getLoc()[0].getPointer();
136 auto DocumentationForOption
= [&](const Record
*R
) -> DocumentedOption
{
137 auto &A
= Aliases
[R
];
138 sort(A
, CompareByName
);
139 return {R
, std::move(A
)};
142 std::function
<Documentation(const Record
*)> DocumentationForGroup
=
143 [&](const Record
*R
) -> Documentation
{
146 auto &Groups
= GroupsInGroup
[R
];
147 sort(Groups
, CompareByLocation
);
148 for (const Record
*G
: Groups
) {
149 D
.Groups
.emplace_back();
150 D
.Groups
.back().Group
= G
;
151 Documentation
&Base
= D
.Groups
.back();
152 Base
= DocumentationForGroup(G
);
157 auto &Options
= OptionsInGroup
[R
];
158 sort(Options
, CompareByName
);
159 for (const Record
*O
: Options
)
160 if (isOptionVisible(O
, DocInfo
))
161 D
.Options
.push_back(DocumentationForOption(O
));
166 return DocumentationForGroup(nullptr);
169 // Get the first and successive separators to use for an OptionKind.
170 std::pair
<StringRef
,StringRef
> getSeparatorsForKind(const Record
*OptionKind
) {
171 return StringSwitch
<std::pair
<StringRef
, StringRef
>>(OptionKind
->getName())
172 .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE",
173 "KIND_JOINED_AND_SEPARATE",
174 "KIND_REMAINING_ARGS_JOINED", {"", " "})
175 .Case("KIND_COMMAJOINED", {"", ","})
176 .Default({" ", " "});
179 const unsigned UnlimitedArgs
= unsigned(-1);
181 // Get the number of arguments expected for an option, or -1 if any number of
182 // arguments are accepted.
183 unsigned getNumArgsForKind(const Record
*OptionKind
, const Record
*Option
) {
184 return StringSwitch
<unsigned>(OptionKind
->getName())
185 .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", "KIND_SEPARATE", 1)
186 .Cases("KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOINED",
187 "KIND_COMMAJOINED", UnlimitedArgs
)
188 .Case("KIND_JOINED_AND_SEPARATE", 2)
189 .Case("KIND_MULTIARG", Option
->getValueAsInt("NumArgs"))
193 std::string
escapeRST(StringRef Str
) {
196 if (StringRef("`*|[]\\").count(K
))
203 StringRef
getSphinxOptionID(StringRef OptionName
) {
204 for (auto I
= OptionName
.begin(), E
= OptionName
.end(); I
!= E
; ++I
)
205 if (!isalnum(*I
) && *I
!= '-')
206 return OptionName
.substr(0, I
- OptionName
.begin());
210 bool canSphinxCopeWithOption(const Record
*Option
) {
211 // HACK: Work arond sphinx's inability to cope with punctuation-only options
212 // such as /? by suppressing them from the option list.
213 for (char C
: Option
->getValueAsString("Name"))
219 void emitHeading(int Depth
, std::string Heading
, raw_ostream
&OS
) {
220 assert(Depth
< 8 && "groups nested too deeply");
221 OS
<< Heading
<< '\n'
222 << std::string(Heading
.size(), "=~-_'+<>"[Depth
]) << "\n";
225 /// Get the value of field \p Primary, if possible. If \p Primary does not
226 /// exist, get the value of \p Fallback and escape it for rST emission.
227 std::string
getRSTStringWithTextFallback(const Record
*R
, StringRef Primary
,
228 StringRef Fallback
) {
229 for (auto Field
: {Primary
, Fallback
}) {
230 if (auto *V
= R
->getValue(Field
)) {
232 if (auto *SV
= dyn_cast_or_null
<StringInit
>(V
->getValue()))
233 Value
= SV
->getValue();
235 return Field
== Primary
? Value
.str() : escapeRST(Value
);
238 return std::string(StringRef());
241 void emitOptionWithArgs(StringRef Prefix
, const Record
*Option
,
242 ArrayRef
<StringRef
> Args
, raw_ostream
&OS
) {
243 OS
<< Prefix
<< escapeRST(Option
->getValueAsString("Name"));
245 std::pair
<StringRef
, StringRef
> Separators
=
246 getSeparatorsForKind(Option
->getValueAsDef("Kind"));
248 StringRef Separator
= Separators
.first
;
249 for (auto Arg
: Args
) {
250 OS
<< Separator
<< escapeRST(Arg
);
251 Separator
= Separators
.second
;
255 constexpr StringLiteral DefaultMetaVarName
= "<arg>";
257 void emitOptionName(StringRef Prefix
, const Record
*Option
, raw_ostream
&OS
) {
258 // Find the arguments to list after the option.
259 unsigned NumArgs
= getNumArgsForKind(Option
->getValueAsDef("Kind"), Option
);
260 bool HasMetaVarName
= !Option
->isValueUnset("MetaVarName");
262 std::vector
<std::string
> Args
;
264 Args
.push_back(std::string(Option
->getValueAsString("MetaVarName")));
265 else if (NumArgs
== 1)
266 Args
.push_back(DefaultMetaVarName
.str());
268 // Fill up arguments if this option didn't provide a meta var name or it
269 // supports an unlimited number of arguments. We can't see how many arguments
270 // already are in a meta var name, so assume it has right number. This is
271 // needed for JoinedAndSeparate options so that there arent't too many
273 if (!HasMetaVarName
|| NumArgs
== UnlimitedArgs
) {
274 while (Args
.size() < NumArgs
) {
275 Args
.push_back(("<arg" + Twine(Args
.size() + 1) + ">").str());
276 // Use '--args <arg1> <arg2>...' if any number of args are allowed.
277 if (Args
.size() == 2 && NumArgs
== UnlimitedArgs
) {
278 Args
.back() += "...";
284 emitOptionWithArgs(Prefix
, Option
,
285 std::vector
<StringRef
>(Args
.begin(), Args
.end()), OS
);
287 auto AliasArgs
= Option
->getValueAsListOfStrings("AliasArgs");
288 if (!AliasArgs
.empty()) {
289 const Record
*Alias
= Option
->getValueAsDef("Alias");
290 OS
<< " (equivalent to ";
292 Alias
->getValueAsListOfStrings("Prefixes").front(), Alias
,
298 bool emitOptionNames(const Record
*Option
, raw_ostream
&OS
, bool EmittedAny
) {
299 for (auto &Prefix
: Option
->getValueAsListOfStrings("Prefixes")) {
302 emitOptionName(Prefix
, Option
, OS
);
308 template <typename Fn
>
309 void forEachOptionName(const DocumentedOption
&Option
, const Record
*DocInfo
,
313 for (auto *Alias
: Option
.Aliases
)
314 if (isOptionVisible(Alias
, DocInfo
) &&
315 canSphinxCopeWithOption(Option
.Option
))
319 void emitOption(const DocumentedOption
&Option
, const Record
*DocInfo
,
321 if (Option
.Option
->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" ||
322 Option
.Option
->getValueAsDef("Kind")->getName() == "KIND_INPUT")
324 if (!canSphinxCopeWithOption(Option
.Option
))
327 // HACK: Emit a different program name with each option to work around
328 // sphinx's inability to cope with options that differ only by punctuation
329 // (eg -ObjC vs -ObjC++, -G vs -G=).
330 std::vector
<std::string
> SphinxOptionIDs
;
331 forEachOptionName(Option
, DocInfo
, [&](const Record
*Option
) {
332 for (auto &Prefix
: Option
->getValueAsListOfStrings("Prefixes"))
333 SphinxOptionIDs
.push_back(std::string(getSphinxOptionID(
334 (Prefix
+ Option
->getValueAsString("Name")).str())));
336 assert(!SphinxOptionIDs
.empty() && "no flags for option");
337 static std::map
<std::string
, int> NextSuffix
;
338 int SphinxWorkaroundSuffix
= NextSuffix
[*std::max_element(
339 SphinxOptionIDs
.begin(), SphinxOptionIDs
.end(),
340 [&](const std::string
&A
, const std::string
&B
) {
341 return NextSuffix
[A
] < NextSuffix
[B
];
343 for (auto &S
: SphinxOptionIDs
)
344 NextSuffix
[S
] = SphinxWorkaroundSuffix
+ 1;
346 std::string Program
= DocInfo
->getValueAsString("Program").lower();
347 if (SphinxWorkaroundSuffix
)
348 OS
<< ".. program:: " << Program
<< SphinxWorkaroundSuffix
<< "\n";
350 // Emit the names of the option.
351 OS
<< ".. option:: ";
352 bool EmittedAny
= false;
353 forEachOptionName(Option
, DocInfo
, [&](const Record
*Option
) {
354 EmittedAny
= emitOptionNames(Option
, OS
, EmittedAny
);
356 if (SphinxWorkaroundSuffix
)
357 OS
<< "\n.. program:: " << Program
;
360 // Emit the description, if we have one.
361 const Record
*R
= Option
.Option
;
362 std::string Description
;
364 // Prefer a program specific help string.
365 // This is a list of (visibilities, string) pairs.
366 for (const Record
*VisibilityHelp
:
367 R
->getValueAsListOfDefs("HelpTextsForVariants")) {
368 // This is a list of visibilities.
369 ArrayRef
<const Init
*> Visibilities
=
370 VisibilityHelp
->getValueAsListInit("Visibilities")->getValues();
372 // See if any of the program's visibilities are in the list.
373 for (StringRef DocInfoMask
:
374 DocInfo
->getValueAsListOfStrings("VisibilityMask")) {
375 for (const Init
*Visibility
: Visibilities
) {
376 if (Visibility
->getAsUnquotedString() == DocInfoMask
) {
377 // Use the first one we find.
378 Description
= escapeRST(VisibilityHelp
->getValueAsString("Text"));
382 if (!Description
.empty())
386 if (!Description
.empty())
390 // If there's not a program specific string, use the default one.
391 if (Description
.empty())
392 Description
= getRSTStringWithTextFallback(R
, "DocBrief", "HelpText");
394 if (!isa
<UnsetInit
>(R
->getValueInit("Values"))) {
395 if (!Description
.empty() && Description
.back() != '.')
396 Description
.push_back('.');
398 StringRef MetaVarName
;
399 if (!isa
<UnsetInit
>(R
->getValueInit("MetaVarName")))
400 MetaVarName
= R
->getValueAsString("MetaVarName");
402 MetaVarName
= DefaultMetaVarName
;
404 SmallVector
<StringRef
> Values
;
405 SplitString(R
->getValueAsString("Values"), Values
, ",");
406 Description
+= (" " + MetaVarName
+ " must be '").str();
407 if (Values
.size() > 1) {
408 Description
+= join(Values
.begin(), Values
.end() - 1, "', '");
409 Description
+= "' or '";
411 Description
+= (Values
.back() + "'.").str();
414 if (!Description
.empty())
415 OS
<< Description
<< "\n\n";
418 void emitDocumentation(int Depth
, const Documentation
&Doc
,
419 const Record
*DocInfo
, raw_ostream
&OS
);
421 void emitGroup(int Depth
, const DocumentedGroup
&Group
, const Record
*DocInfo
,
424 getRSTStringWithTextFallback(Group
.Group
, "DocName", "Name"), OS
);
426 // Emit the description, if we have one.
427 std::string Description
=
428 getRSTStringWithTextFallback(Group
.Group
, "DocBrief", "HelpText");
429 if (!Description
.empty())
430 OS
<< Description
<< "\n\n";
432 // Emit contained options and groups.
433 emitDocumentation(Depth
+ 1, Group
, DocInfo
, OS
);
436 void emitDocumentation(int Depth
, const Documentation
&Doc
,
437 const Record
*DocInfo
, raw_ostream
&OS
) {
438 for (auto &O
: Doc
.Options
)
439 emitOption(O
, DocInfo
, OS
);
440 for (auto &G
: Doc
.Groups
)
441 emitGroup(Depth
, G
, DocInfo
, OS
);
446 void clang::EmitClangOptDocs(const RecordKeeper
&Records
, raw_ostream
&OS
) {
447 const Record
*DocInfo
= Records
.getDef("GlobalDocumentation");
449 PrintFatalError("The GlobalDocumentation top-level definition is missing, "
450 "no documentation will be generated.");
453 OS
<< DocInfo
->getValueAsString("Intro") << "\n";
454 OS
<< ".. program:: " << DocInfo
->getValueAsString("Program").lower() << "\n";
456 emitDocumentation(0, extractDocumentation(Records
, DocInfo
), DocInfo
, OS
);