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/TableGen/Error.h"
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/StringSwitch.h"
16 #include "llvm/ADT/Twine.h"
17 #include "llvm/TableGen/Record.h"
18 #include "llvm/TableGen/TableGenBackend.h"
26 struct DocumentedOption
{
28 std::vector
<Record
*> Aliases
;
30 struct DocumentedGroup
;
31 struct Documentation
{
32 std::vector
<DocumentedGroup
> Groups
;
33 std::vector
<DocumentedOption
> Options
;
36 return Groups
.empty() && Options
.empty();
39 struct DocumentedGroup
: Documentation
{
43 static bool hasFlag(const Record
*Option
, StringRef OptionFlag
,
44 StringRef FlagsField
) {
45 for (const Record
*Flag
: Option
->getValueAsListOfDefs(FlagsField
))
46 if (Flag
->getName() == OptionFlag
)
48 if (const DefInit
*DI
= dyn_cast
<DefInit
>(Option
->getValueInit("Group")))
49 for (const Record
*Flag
: DI
->getDef()->getValueAsListOfDefs(FlagsField
))
50 if (Flag
->getName() == OptionFlag
)
55 static bool isOptionVisible(const Record
*Option
, const Record
*DocInfo
) {
56 for (StringRef IgnoredFlag
: DocInfo
->getValueAsListOfStrings("IgnoreFlags"))
57 if (hasFlag(Option
, IgnoredFlag
, "Flags"))
59 for (StringRef Mask
: DocInfo
->getValueAsListOfStrings("VisibilityMask"))
60 if (hasFlag(Option
, Mask
, "Visibility"))
65 // Reorganize the records into a suitable form for emitting documentation.
66 Documentation
extractDocumentation(RecordKeeper
&Records
,
67 const Record
*DocInfo
) {
70 // Build the tree of groups. The root in the tree is the fake option group
71 // (Record*)nullptr, which contains all top-level groups and options.
72 std::map
<Record
*, std::vector
<Record
*> > OptionsInGroup
;
73 std::map
<Record
*, std::vector
<Record
*> > GroupsInGroup
;
74 std::map
<Record
*, std::vector
<Record
*> > Aliases
;
76 std::map
<std::string
, Record
*> OptionsByName
;
77 for (Record
*R
: Records
.getAllDerivedDefinitions("Option"))
78 OptionsByName
[std::string(R
->getValueAsString("Name"))] = R
;
80 auto Flatten
= [](Record
*R
) {
81 return R
->getValue("DocFlatten") && R
->getValueAsBit("DocFlatten");
84 auto SkipFlattened
= [&](Record
*R
) -> Record
* {
85 while (R
&& Flatten(R
)) {
86 auto *G
= dyn_cast
<DefInit
>(R
->getValueInit("Group"));
94 for (Record
*R
: Records
.getAllDerivedDefinitions("OptionGroup")) {
98 Record
*Group
= nullptr;
99 if (auto *G
= dyn_cast
<DefInit
>(R
->getValueInit("Group")))
100 Group
= SkipFlattened(G
->getDef());
101 GroupsInGroup
[Group
].push_back(R
);
104 for (Record
*R
: Records
.getAllDerivedDefinitions("Option")) {
105 if (auto *A
= dyn_cast
<DefInit
>(R
->getValueInit("Alias"))) {
106 Aliases
[A
->getDef()].push_back(R
);
110 // Pretend no-X and Xno-Y options are aliases of X and XY.
111 std::string Name
= std::string(R
->getValueAsString("Name"));
112 if (Name
.size() >= 4) {
113 if (Name
.substr(0, 3) == "no-" && OptionsByName
[Name
.substr(3)]) {
114 Aliases
[OptionsByName
[Name
.substr(3)]].push_back(R
);
117 if (Name
.substr(1, 3) == "no-" && OptionsByName
[Name
[0] + Name
.substr(4)]) {
118 Aliases
[OptionsByName
[Name
[0] + Name
.substr(4)]].push_back(R
);
123 Record
*Group
= nullptr;
124 if (auto *G
= dyn_cast
<DefInit
>(R
->getValueInit("Group")))
125 Group
= SkipFlattened(G
->getDef());
126 OptionsInGroup
[Group
].push_back(R
);
129 auto CompareByName
= [](Record
*A
, Record
*B
) {
130 return A
->getValueAsString("Name") < B
->getValueAsString("Name");
133 auto CompareByLocation
= [](Record
*A
, Record
*B
) {
134 return A
->getLoc()[0].getPointer() < B
->getLoc()[0].getPointer();
137 auto DocumentationForOption
= [&](Record
*R
) -> DocumentedOption
{
138 auto &A
= Aliases
[R
];
139 llvm::sort(A
, CompareByName
);
140 return {R
, std::move(A
)};
143 std::function
<Documentation(Record
*)> DocumentationForGroup
=
144 [&](Record
*R
) -> Documentation
{
147 auto &Groups
= GroupsInGroup
[R
];
148 llvm::sort(Groups
, CompareByLocation
);
149 for (Record
*G
: Groups
) {
150 D
.Groups
.emplace_back();
151 D
.Groups
.back().Group
= G
;
152 Documentation
&Base
= D
.Groups
.back();
153 Base
= DocumentationForGroup(G
);
158 auto &Options
= OptionsInGroup
[R
];
159 llvm::sort(Options
, CompareByName
);
160 for (Record
*O
: Options
)
161 if (isOptionVisible(O
, DocInfo
))
162 D
.Options
.push_back(DocumentationForOption(O
));
167 return DocumentationForGroup(nullptr);
170 // Get the first and successive separators to use for an OptionKind.
171 std::pair
<StringRef
,StringRef
> getSeparatorsForKind(const Record
*OptionKind
) {
172 return StringSwitch
<std::pair
<StringRef
, StringRef
>>(OptionKind
->getName())
173 .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE",
174 "KIND_JOINED_AND_SEPARATE",
175 "KIND_REMAINING_ARGS_JOINED", {"", " "})
176 .Case("KIND_COMMAJOINED", {"", ","})
177 .Default({" ", " "});
180 const unsigned UnlimitedArgs
= unsigned(-1);
182 // Get the number of arguments expected for an option, or -1 if any number of
183 // arguments are accepted.
184 unsigned getNumArgsForKind(Record
*OptionKind
, const Record
*Option
) {
185 return StringSwitch
<unsigned>(OptionKind
->getName())
186 .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", "KIND_SEPARATE", 1)
187 .Cases("KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOINED",
188 "KIND_COMMAJOINED", UnlimitedArgs
)
189 .Case("KIND_JOINED_AND_SEPARATE", 2)
190 .Case("KIND_MULTIARG", Option
->getValueAsInt("NumArgs"))
194 std::string
escapeRST(StringRef Str
) {
197 if (StringRef("`*|[]\\").count(K
))
204 StringRef
getSphinxOptionID(StringRef OptionName
) {
205 for (auto I
= OptionName
.begin(), E
= OptionName
.end(); I
!= E
; ++I
)
206 if (!isalnum(*I
) && *I
!= '-')
207 return OptionName
.substr(0, I
- OptionName
.begin());
211 bool canSphinxCopeWithOption(const Record
*Option
) {
212 // HACK: Work arond sphinx's inability to cope with punctuation-only options
213 // such as /? by suppressing them from the option list.
214 for (char C
: Option
->getValueAsString("Name"))
220 void emitHeading(int Depth
, std::string Heading
, raw_ostream
&OS
) {
221 assert(Depth
< 8 && "groups nested too deeply");
222 OS
<< Heading
<< '\n'
223 << std::string(Heading
.size(), "=~-_'+<>"[Depth
]) << "\n";
226 /// Get the value of field \p Primary, if possible. If \p Primary does not
227 /// exist, get the value of \p Fallback and escape it for rST emission.
228 std::string
getRSTStringWithTextFallback(const Record
*R
, StringRef Primary
,
229 StringRef Fallback
) {
230 for (auto Field
: {Primary
, Fallback
}) {
231 if (auto *V
= R
->getValue(Field
)) {
233 if (auto *SV
= dyn_cast_or_null
<StringInit
>(V
->getValue()))
234 Value
= SV
->getValue();
236 return Field
== Primary
? Value
.str() : escapeRST(Value
);
239 return std::string(StringRef());
242 void emitOptionWithArgs(StringRef Prefix
, const Record
*Option
,
243 ArrayRef
<StringRef
> Args
, raw_ostream
&OS
) {
244 OS
<< Prefix
<< escapeRST(Option
->getValueAsString("Name"));
246 std::pair
<StringRef
, StringRef
> Separators
=
247 getSeparatorsForKind(Option
->getValueAsDef("Kind"));
249 StringRef Separator
= Separators
.first
;
250 for (auto Arg
: Args
) {
251 OS
<< Separator
<< escapeRST(Arg
);
252 Separator
= Separators
.second
;
256 constexpr StringLiteral DefaultMetaVarName
= "<arg>";
258 void emitOptionName(StringRef Prefix
, const Record
*Option
, raw_ostream
&OS
) {
259 // Find the arguments to list after the option.
260 unsigned NumArgs
= getNumArgsForKind(Option
->getValueAsDef("Kind"), Option
);
261 bool HasMetaVarName
= !Option
->isValueUnset("MetaVarName");
263 std::vector
<std::string
> Args
;
265 Args
.push_back(std::string(Option
->getValueAsString("MetaVarName")));
266 else if (NumArgs
== 1)
267 Args
.push_back(DefaultMetaVarName
.str());
269 // Fill up arguments if this option didn't provide a meta var name or it
270 // supports an unlimited number of arguments. We can't see how many arguments
271 // already are in a meta var name, so assume it has right number. This is
272 // needed for JoinedAndSeparate options so that there arent't too many
274 if (!HasMetaVarName
|| NumArgs
== UnlimitedArgs
) {
275 while (Args
.size() < NumArgs
) {
276 Args
.push_back(("<arg" + Twine(Args
.size() + 1) + ">").str());
277 // Use '--args <arg1> <arg2>...' if any number of args are allowed.
278 if (Args
.size() == 2 && NumArgs
== UnlimitedArgs
) {
279 Args
.back() += "...";
285 emitOptionWithArgs(Prefix
, Option
, std::vector
<StringRef
>(Args
.begin(), Args
.end()), OS
);
287 auto AliasArgs
= Option
->getValueAsListOfStrings("AliasArgs");
288 if (!AliasArgs
.empty()) {
289 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;
345 if (SphinxWorkaroundSuffix
)
346 OS
<< ".. program:: " << DocInfo
->getValueAsString("Program")
347 << SphinxWorkaroundSuffix
<< "\n";
349 // Emit the names of the option.
350 OS
<< ".. option:: ";
351 bool EmittedAny
= false;
352 forEachOptionName(Option
, DocInfo
, [&](const Record
*Option
) {
353 EmittedAny
= emitOptionNames(Option
, OS
, EmittedAny
);
355 if (SphinxWorkaroundSuffix
)
356 OS
<< "\n.. program:: " << DocInfo
->getValueAsString("Program");
359 // Emit the description, if we have one.
360 const Record
*R
= Option
.Option
;
361 std::string Description
=
362 getRSTStringWithTextFallback(R
, "DocBrief", "HelpText");
364 if (!isa
<UnsetInit
>(R
->getValueInit("Values"))) {
365 if (!Description
.empty() && Description
.back() != '.')
366 Description
.push_back('.');
368 StringRef MetaVarName
;
369 if (!isa
<UnsetInit
>(R
->getValueInit("MetaVarName")))
370 MetaVarName
= R
->getValueAsString("MetaVarName");
372 MetaVarName
= DefaultMetaVarName
;
374 SmallVector
<StringRef
> Values
;
375 SplitString(R
->getValueAsString("Values"), Values
, ",");
376 Description
+= (" " + MetaVarName
+ " must be '").str();
377 if (Values
.size() > 1) {
378 Description
+= join(Values
.begin(), Values
.end() - 1, "', '");
379 Description
+= "' or '";
381 Description
+= (Values
.back() + "'.").str();
384 if (!Description
.empty())
385 OS
<< Description
<< "\n\n";
388 void emitDocumentation(int Depth
, const Documentation
&Doc
,
389 const Record
*DocInfo
, raw_ostream
&OS
);
391 void emitGroup(int Depth
, const DocumentedGroup
&Group
, const Record
*DocInfo
,
394 getRSTStringWithTextFallback(Group
.Group
, "DocName", "Name"), OS
);
396 // Emit the description, if we have one.
397 std::string Description
=
398 getRSTStringWithTextFallback(Group
.Group
, "DocBrief", "HelpText");
399 if (!Description
.empty())
400 OS
<< Description
<< "\n\n";
402 // Emit contained options and groups.
403 emitDocumentation(Depth
+ 1, Group
, DocInfo
, OS
);
406 void emitDocumentation(int Depth
, const Documentation
&Doc
,
407 const Record
*DocInfo
, raw_ostream
&OS
) {
408 for (auto &O
: Doc
.Options
)
409 emitOption(O
, DocInfo
, OS
);
410 for (auto &G
: Doc
.Groups
)
411 emitGroup(Depth
, G
, DocInfo
, OS
);
416 void clang::EmitClangOptDocs(RecordKeeper
&Records
, raw_ostream
&OS
) {
417 const Record
*DocInfo
= Records
.getDef("GlobalDocumentation");
419 PrintFatalError("The GlobalDocumentation top-level definition is missing, "
420 "no documentation will be generated.");
423 OS
<< DocInfo
->getValueAsString("Intro") << "\n";
424 OS
<< ".. program:: " << DocInfo
->getValueAsString("Program") << "\n";
426 emitDocumentation(0, extractDocumentation(Records
, DocInfo
), DocInfo
, OS
);