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