138465Smsmith//===- ClangOptionDocEmitter.cpp - Documentation for command line flags ---===// 238465Smsmith// 338465Smsmith// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 438465Smsmith// See https://llvm.org/LICENSE.txt for license information. 538465Smsmith// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 638465Smsmith// 738465Smsmith// FIXME: Once this has stabilized, consider moving it to LLVM. 838465Smsmith// 938465Smsmith//===----------------------------------------------------------------------===// 1038465Smsmith 1138465Smsmith#include "TableGenBackends.h" 1238465Smsmith#include "llvm/TableGen/Error.h" 1338465Smsmith#include "llvm/ADT/STLExtras.h" 1438465Smsmith#include "llvm/ADT/SmallString.h" 1538465Smsmith#include "llvm/ADT/StringSwitch.h" 1638465Smsmith#include "llvm/ADT/Twine.h" 1738465Smsmith#include "llvm/TableGen/Record.h" 1838465Smsmith#include "llvm/TableGen/TableGenBackend.h" 1938465Smsmith#include <cctype> 2038465Smsmith#include <cstring> 2138465Smsmith#include <map> 2238465Smsmith 2338465Smsmithusing namespace llvm; 2438465Smsmith 2538465Smsmithnamespace { 2650477Speterstruct DocumentedOption { 2738465Smsmith Record *Option; 2838465Smsmith std::vector<Record*> Aliases; 29234789Smarius}; 30234789Smariusstruct DocumentedGroup; 31234789Smariusstruct Documentation { 3238465Smsmith std::vector<DocumentedGroup> Groups; 3340553Smsmith std::vector<DocumentedOption> Options; 3478195Speter}; 3538465Smsmithstruct DocumentedGroup : Documentation { 3638465Smsmith Record *Group; 3738465Smsmith}; 3838465Smsmith 3938465Smsmith// Reorganize the records into a suitable form for emitting documentation. 4038465SmsmithDocumentation extractDocumentation(RecordKeeper &Records) { 4138465Smsmith Documentation Result; 4238465Smsmith 4338465Smsmith // Build the tree of groups. The root in the tree is the fake option group 4438465Smsmith // (Record*)nullptr, which contains all top-level groups and options. 4538465Smsmith std::map<Record*, std::vector<Record*> > OptionsInGroup; 4638465Smsmith std::map<Record*, std::vector<Record*> > GroupsInGroup; 4738465Smsmith std::map<Record*, std::vector<Record*> > Aliases; 4886090Sjhb 49185029Spjd std::map<std::string, Record*> OptionsByName; 50163897Smarcel for (Record *R : Records.getAllDerivedDefinitions("Option")) 51201941Smarcel OptionsByName[std::string(R->getValueAsString("Name"))] = R; 5238465Smsmith 5338465Smsmith auto Flatten = [](Record *R) { 5438465Smsmith return R->getValue("DocFlatten") && R->getValueAsBit("DocFlatten"); 5538465Smsmith }; 5638465Smsmith 5738465Smsmith auto SkipFlattened = [&](Record *R) -> Record* { 5838465Smsmith while (R && Flatten(R)) { 5938465Smsmith auto *G = dyn_cast<DefInit>(R->getValueInit("Group")); 6038465Smsmith if (!G) 6138465Smsmith return nullptr; 6264187Sjhb R = G->getDef(); 6364187Sjhb } 6438465Smsmith return R; 6564187Sjhb }; 6664187Sjhb 6764187Sjhb for (Record *R : Records.getAllDerivedDefinitions("OptionGroup")) { 6839178Smsmith if (Flatten(R)) 6964187Sjhb continue; 7039178Smsmith 7143614Sdcs Record *Group = nullptr; 7264187Sjhb if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group"))) 7364187Sjhb Group = SkipFlattened(G->getDef()); 7443614Sdcs GroupsInGroup[Group].push_back(R); 7538465Smsmith } 7664187Sjhb 7764187Sjhb for (Record *R : Records.getAllDerivedDefinitions("Option")) { 7864187Sjhb if (auto *A = dyn_cast<DefInit>(R->getValueInit("Alias"))) { 7938465Smsmith Aliases[A->getDef()].push_back(R); 8038465Smsmith continue; 8164187Sjhb } 8264187Sjhb 8364187Sjhb // Pretend no-X and Xno-Y options are aliases of X and XY. 8464187Sjhb std::string Name = std::string(R->getValueAsString("Name")); 85134441Siedowse if (Name.size() >= 4) { 86134441Siedowse if (Name.substr(0, 3) == "no-" && OptionsByName[Name.substr(3)]) { 87134441Siedowse Aliases[OptionsByName[Name.substr(3)]].push_back(R); 8838465Smsmith continue; 8943614Sdcs } 9064187Sjhb if (Name.substr(1, 3) == "no-" && OptionsByName[Name[0] + Name.substr(4)]) { 9164187Sjhb Aliases[OptionsByName[Name[0] + Name.substr(4)]].push_back(R); 9264187Sjhb continue; 9364187Sjhb } 9443614Sdcs } 9538465Smsmith 9640834Smsmith Record *Group = nullptr; 9740834Smsmith if (auto *G = dyn_cast<DefInit>(R->getValueInit("Group"))) 9840834Smsmith Group = SkipFlattened(G->getDef()); 9940834Smsmith OptionsInGroup[Group].push_back(R); 10064187Sjhb } 10140834Smsmith 10240834Smsmith auto CompareByName = [](Record *A, Record *B) { 10340834Smsmith return A->getValueAsString("Name") < B->getValueAsString("Name"); 10440834Smsmith }; 10538465Smsmith 10638465Smsmith auto CompareByLocation = [](Record *A, Record *B) { 10738465Smsmith return A->getLoc()[0].getPointer() < B->getLoc()[0].getPointer(); 10838465Smsmith }; 10964187Sjhb 11064187Sjhb auto DocumentationForOption = [&](Record *R) -> DocumentedOption { 11138465Smsmith auto &A = Aliases[R]; 112241299Savg llvm::sort(A, CompareByName); 113241299Savg return {R, std::move(A)}; 114241299Savg }; 115241299Savg 11638465Smsmith std::function<Documentation(Record *)> DocumentationForGroup = 11738465Smsmith [&](Record *R) -> Documentation { 11838465Smsmith Documentation D; 11938465Smsmith 12038465Smsmith auto &Groups = GroupsInGroup[R]; 12138465Smsmith llvm::sort(Groups, CompareByLocation); 12238465Smsmith for (Record *G : Groups) { 12364187Sjhb D.Groups.emplace_back(); 12438465Smsmith D.Groups.back().Group = G; 12538465Smsmith Documentation &Base = D.Groups.back(); 12638789Smsmith Base = DocumentationForGroup(G); 12738789Smsmith } 12840553Smsmith 12939178Smsmith auto &Options = OptionsInGroup[R]; 13064187Sjhb llvm::sort(Options, CompareByName); 13140553Smsmith for (Record *O : Options) 13239178Smsmith D.Options.push_back(DocumentationForOption(O)); 13339178Smsmith 13440553Smsmith return D; 13538789Smsmith }; 13640553Smsmith 13760938Sjake return DocumentationForGroup(nullptr); 13839178Smsmith} 13938789Smsmith 14040553Smsmith// Get the first and successive separators to use for an OptionKind. 14138789Smsmithstd::pair<StringRef,StringRef> getSeparatorsForKind(const Record *OptionKind) { 14240553Smsmith return StringSwitch<std::pair<StringRef, StringRef>>(OptionKind->getName()) 14340553Smsmith .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", 14440553Smsmith "KIND_JOINED_AND_SEPARATE", 14540553Smsmith "KIND_REMAINING_ARGS_JOINED", {"", " "}) 14640553Smsmith .Case("KIND_COMMAJOINED", {"", ","}) 14740553Smsmith .Default({" ", " "}); 14860938Sjake} 14960938Sjake 15038789Smsmithconst unsigned UnlimitedArgs = unsigned(-1); 15138789Smsmith 15265614Sdcs// Get the number of arguments expected for an option, or -1 if any number of 15365614Sdcs// arguments are accepted. 15465614Sdcsunsigned getNumArgsForKind(Record *OptionKind, const Record *Option) { 15565614Sdcs return StringSwitch<unsigned>(OptionKind->getName()) 15639178Smsmith .Cases("KIND_JOINED", "KIND_JOINED_OR_SEPARATE", "KIND_SEPARATE", 1) 15738789Smsmith .Cases("KIND_REMAINING_ARGS", "KIND_REMAINING_ARGS_JOINED", 15864187Sjhb "KIND_COMMAJOINED", UnlimitedArgs) 15964187Sjhb .Case("KIND_JOINED_AND_SEPARATE", 2) 16064187Sjhb .Case("KIND_MULTIARG", Option->getValueAsInt("NumArgs")) 16164187Sjhb .Default(0); 16264187Sjhb} 16339178Smsmith 16438789Smsmithbool hasFlag(const Record *OptionOrGroup, StringRef OptionFlag) { 16540597Smsmith for (const Record *Flag : OptionOrGroup->getValueAsListOfDefs("Flags")) 16640597Smsmith if (Flag->getName() == OptionFlag) 16740597Smsmith return true; 16840597Smsmith return false; 16940597Smsmith} 17040597Smsmith 17140597Smsmithbool isExcluded(const Record *OptionOrGroup, const Record *DocInfo) { 17259854Sbp // FIXME: Provide a flag to specify the set of exclusions. 17338465Smsmith for (StringRef Exclusion : DocInfo->getValueAsListOfStrings("ExcludedFlags")) 17438465Smsmith if (hasFlag(OptionOrGroup, Exclusion)) 17538465Smsmith return true; 17638465Smsmith return false; 17759854Sbp} 17838465Smsmith 17938465Smsmithstd::string escapeRST(StringRef Str) { 18038465Smsmith std::string Out; 18159854Sbp for (auto K : Str) { 18264187Sjhb if (StringRef("`*|_[]\\").count(K)) 18338465Smsmith Out.push_back('\\'); 18438465Smsmith Out.push_back(K); 18559854Sbp } 18683321Speter return Out; 18759854Sbp} 18859854Sbp 18959854SbpStringRef getSphinxOptionID(StringRef OptionName) { 19059854Sbp for (auto I = OptionName.begin(), E = OptionName.end(); I != E; ++I) 19183321Speter if (!isalnum(*I) && *I != '-') 19259854Sbp return OptionName.substr(0, I - OptionName.begin()); 19359854Sbp return OptionName; 19459854Sbp} 19559854Sbp 19659854Sbpbool canSphinxCopeWithOption(const Record *Option) { 19738465Smsmith // HACK: Work arond sphinx's inability to cope with punctuation-only options 19859854Sbp // such as /? by suppressing them from the option list. 19959854Sbp for (char C : Option->getValueAsString("Name")) 20038465Smsmith if (isalnum(C)) 20159854Sbp return true; 20238465Smsmith return false; 20338712Smsmith} 20438712Smsmith 20538465Smsmithvoid emitHeading(int Depth, std::string Heading, raw_ostream &OS) { 20659854Sbp assert(Depth < 8 && "groups nested too deeply"); 20738465Smsmith OS << Heading << '\n' 20859854Sbp << std::string(Heading.size(), "=~-_'+<>"[Depth]) << "\n"; 20959854Sbp} 21059854Sbp 21159854Sbp/// Get the value of field \p Primary, if possible. If \p Primary does not 21259854Sbp/// exist, get the value of \p Fallback and escape it for rST emission. 21359854Sbpstd::string getRSTStringWithTextFallback(const Record *R, StringRef Primary, 21459854Sbp StringRef Fallback) { 21559854Sbp for (auto Field : {Primary, Fallback}) { 21659854Sbp if (auto *V = R->getValue(Field)) { 21738465Smsmith StringRef Value; 21838465Smsmith if (auto *SV = dyn_cast_or_null<StringInit>(V->getValue())) 21959854Sbp Value = SV->getValue(); 22038465Smsmith if (!Value.empty()) 22138465Smsmith return Field == Primary ? Value.str() : escapeRST(Value); 222114379Speter } 22338712Smsmith } 22459854Sbp return std::string(StringRef()); 22538465Smsmith} 22659854Sbp 22759854Sbpvoid emitOptionWithArgs(StringRef Prefix, const Record *Option, 22859854Sbp ArrayRef<StringRef> Args, raw_ostream &OS) { 22959854Sbp OS << Prefix << escapeRST(Option->getValueAsString("Name")); 23083321Speter 23183321Speter std::pair<StringRef, StringRef> Separators = 23238465Smsmith getSeparatorsForKind(Option->getValueAsDef("Kind")); 23359854Sbp 23459854Sbp StringRef Separator = Separators.first; 23559854Sbp for (auto Arg : Args) { 23659854Sbp OS << Separator << escapeRST(Arg); 23759854Sbp Separator = Separators.second; 23883321Speter } 23959854Sbp} 24039178Smsmith 24139178Smsmithvoid emitOptionName(StringRef Prefix, const Record *Option, raw_ostream &OS) { 242114379Speter // Find the arguments to list after the option. 243134458Siedowse unsigned NumArgs = getNumArgsForKind(Option->getValueAsDef("Kind"), Option); 244134458Siedowse bool HasMetaVarName = !Option->isValueUnset("MetaVarName"); 245134458Siedowse 246134458Siedowse std::vector<std::string> Args; 247176484Smarcel if (HasMetaVarName) 248176484Smarcel Args.push_back(std::string(Option->getValueAsString("MetaVarName"))); 249176484Smarcel else if (NumArgs == 1) 250134458Siedowse Args.push_back("<arg>"); 251153504Smarcel 252134458Siedowse // Fill up arguments if this option didn't provide a meta var name or it 253114379Speter // supports an unlimited number of arguments. We can't see how many arguments 254134459Siedowse // already are in a meta var name, so assume it has right number. This is 255134459Siedowse // needed for JoinedAndSeparate options so that there arent't too many 256134458Siedowse // arguments. 257134458Siedowse if (!HasMetaVarName || NumArgs == UnlimitedArgs) { 258134458Siedowse while (Args.size() < NumArgs) { 259114379Speter Args.push_back(("<arg" + Twine(Args.size() + 1) + ">").str()); 26039178Smsmith // Use '--args <arg1> <arg2>...' if any number of args are allowed. 26138465Smsmith if (Args.size() == 2 && NumArgs == UnlimitedArgs) { 26238465Smsmith Args.back() += "..."; 26338465Smsmith break; 26438465Smsmith } 26538465Smsmith } 26638465Smsmith } 26738465Smsmith 26838465Smsmith emitOptionWithArgs(Prefix, Option, std::vector<StringRef>(Args.begin(), Args.end()), OS); 26938465Smsmith 27038465Smsmith auto AliasArgs = Option->getValueAsListOfStrings("AliasArgs"); 27138465Smsmith if (!AliasArgs.empty()) { 27238465Smsmith Record *Alias = Option->getValueAsDef("Alias"); 27338465Smsmith OS << " (equivalent to "; 27464187Sjhb emitOptionWithArgs( 27538465Smsmith Alias->getValueAsListOfStrings("Prefixes").front(), Alias, 27678195Speter AliasArgs, OS); 27738465Smsmith OS << ")"; 27838465Smsmith } 27938712Smsmith} 28038712Smsmith 28138712Smsmithbool emitOptionNames(const Record *Option, raw_ostream &OS, bool EmittedAny) { 28238712Smsmith for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) { 28338465Smsmith if (EmittedAny) 28438465Smsmith OS << ", "; 28538465Smsmith emitOptionName(Prefix, Option, OS); 28638465Smsmith EmittedAny = true; 28764187Sjhb } 28838465Smsmith return EmittedAny; 28964187Sjhb} 29038712Smsmith 29164187Sjhbtemplate <typename Fn> 29264187Sjhbvoid forEachOptionName(const DocumentedOption &Option, const Record *DocInfo, 29338764Smsmith Fn F) { 29464187Sjhb F(Option.Option); 29564187Sjhb 29638712Smsmith for (auto *Alias : Option.Aliases) 29764187Sjhb if (!isExcluded(Alias, DocInfo) && canSphinxCopeWithOption(Option.Option)) 29864187Sjhb F(Alias); 29939178Smsmith} 30064187Sjhb 30164187Sjhbvoid emitOption(const DocumentedOption &Option, const Record *DocInfo, 302220311Smarcel raw_ostream &OS) { 303220311Smarcel if (isExcluded(Option.Option, DocInfo)) 304220311Smarcel return; 305220311Smarcel if (Option.Option->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" || 306220311Smarcel Option.Option->getValueAsDef("Kind")->getName() == "KIND_INPUT") 307220311Smarcel return; 308220311Smarcel if (!canSphinxCopeWithOption(Option.Option)) 309220311Smarcel return; 310220311Smarcel 311220311Smarcel // HACK: Emit a different program name with each option to work around 312220311Smarcel // sphinx's inability to cope with options that differ only by punctuation 313220311Smarcel // (eg -ObjC vs -ObjC++, -G vs -G=). 314220311Smarcel std::vector<std::string> SphinxOptionIDs; 315220311Smarcel forEachOptionName(Option, DocInfo, [&](const Record *Option) { 316220311Smarcel for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) 317220311Smarcel SphinxOptionIDs.push_back(std::string(getSphinxOptionID( 318220311Smarcel (Prefix + Option->getValueAsString("Name")).str()))); 319220311Smarcel }); 320235329Savg assert(!SphinxOptionIDs.empty() && "no flags for option"); 321235329Savg static std::map<std::string, int> NextSuffix; 322235329Savg int SphinxWorkaroundSuffix = NextSuffix[*std::max_element( 32338465Smsmith SphinxOptionIDs.begin(), SphinxOptionIDs.end(), 32438465Smsmith [&](const std::string &A, const std::string &B) { 32538465Smsmith return NextSuffix[A] < NextSuffix[B]; 32639178Smsmith })]; 32764187Sjhb for (auto &S : SphinxOptionIDs) 32861659Sps NextSuffix[S] = SphinxWorkaroundSuffix + 1; 32964187Sjhb if (SphinxWorkaroundSuffix) 33091219Sbde OS << ".. program:: " << DocInfo->getValueAsString("Program") 33191219Sbde << SphinxWorkaroundSuffix << "\n"; 332234789Smarius 333235153Savg // Emit the names of the option. 334235153Savg OS << ".. option:: "; 335235153Savg bool EmittedAny = false; 336235153Savg forEachOptionName(Option, DocInfo, [&](const Record *Option) { 337235153Savg EmittedAny = emitOptionNames(Option, OS, EmittedAny); 338235153Savg }); 339234789Smarius if (SphinxWorkaroundSuffix) 340 OS << "\n.. program:: " << DocInfo->getValueAsString("Program"); 341 OS << "\n\n"; 342 343 // Emit the description, if we have one. 344 std::string Description = 345 getRSTStringWithTextFallback(Option.Option, "DocBrief", "HelpText"); 346 if (!Description.empty()) 347 OS << Description << "\n\n"; 348} 349 350void emitDocumentation(int Depth, const Documentation &Doc, 351 const Record *DocInfo, raw_ostream &OS); 352 353void emitGroup(int Depth, const DocumentedGroup &Group, const Record *DocInfo, 354 raw_ostream &OS) { 355 if (isExcluded(Group.Group, DocInfo)) 356 return; 357 358 emitHeading(Depth, 359 getRSTStringWithTextFallback(Group.Group, "DocName", "Name"), OS); 360 361 // Emit the description, if we have one. 362 std::string Description = 363 getRSTStringWithTextFallback(Group.Group, "DocBrief", "HelpText"); 364 if (!Description.empty()) 365 OS << Description << "\n\n"; 366 367 // Emit contained options and groups. 368 emitDocumentation(Depth + 1, Group, DocInfo, OS); 369} 370 371void emitDocumentation(int Depth, const Documentation &Doc, 372 const Record *DocInfo, raw_ostream &OS) { 373 for (auto &O : Doc.Options) 374 emitOption(O, DocInfo, OS); 375 for (auto &G : Doc.Groups) 376 emitGroup(Depth, G, DocInfo, OS); 377} 378 379} // namespace 380 381void clang::EmitClangOptDocs(RecordKeeper &Records, raw_ostream &OS) { 382 const Record *DocInfo = Records.getDef("GlobalDocumentation"); 383 if (!DocInfo) { 384 PrintFatalError("The GlobalDocumentation top-level definition is missing, " 385 "no documentation will be generated."); 386 return; 387 } 388 OS << DocInfo->getValueAsString("Intro") << "\n"; 389 OS << ".. program:: " << DocInfo->getValueAsString("Program") << "\n"; 390 391 emitDocumentation(0, extractDocumentation(Records), DocInfo, OS); 392} 393