1 //===-- ClangOptionDocEmitter.cpp - Documentation for command line flags --===// 2 // 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 6 // 7 // FIXME: Once this has stabilized, consider moving it to LLVM. 8 // 9 //===----------------------------------------------------------------------===// 10 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" 18 #include <cctype> 19 #include <cstring> 20 #include <map> 21 22 using namespace llvm; 23 24 namespace { 25 struct DocumentedOption { 26 const Record *Option; 27 std::vector<const Record *> Aliases; 28 }; 29 struct DocumentedGroup; 30 struct Documentation { 31 std::vector<DocumentedGroup> Groups; 32 std::vector<DocumentedOption> Options; 33 34 bool empty() { 35 return Groups.empty() && Options.empty(); 36 } 37 }; 38 struct DocumentedGroup : Documentation { 39 const Record *Group; 40 }; 41 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) 46 return true; 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) 50 return true; 51 return false; 52 } 53 54 static bool isOptionVisible(const Record *Option, const Record *DocInfo) { 55 for (StringRef IgnoredFlag : DocInfo->getValueAsListOfStrings("IgnoreFlags")) 56 if (hasFlag(Option, IgnoredFlag, "Flags")) 57 return false; 58 for (StringRef Mask : DocInfo->getValueAsListOfStrings("VisibilityMask")) 59 if (hasFlag(Option, Mask, "Visibility")) 60 return true; 61 return false; 62 } 63 64 // Reorganize the records into a suitable form for emitting documentation. 65 Documentation extractDocumentation(const RecordKeeper &Records, 66 const Record *DocInfo) { 67 Documentation Result; 68 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; 74 75 std::map<std::string, const Record *> OptionsByName; 76 for (const Record *R : Records.getAllDerivedDefinitions("Option")) 77 OptionsByName[std::string(R->getValueAsString("Name"))] = R; 78 79 auto Flatten = [](const Record *R) { 80 return R->getValue("DocFlatten") && R->getValueAsBit("DocFlatten"); 81 }; 82 83 auto SkipFlattened = [&](const Record *R) -> const Record * { 84 while (R && Flatten(R)) { 85 auto *G = dyn_cast<DefInit>(R->getValueInit("Group")); 86 if (!G) 87 return nullptr; 88 R = G->getDef(); 89 } 90 return R; 91 }; 92 93 for (const Record *R : Records.getAllDerivedDefinitions("OptionGroup")) { 94 if (Flatten(R)) 95 continue; 96 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); 101 } 102 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); 106 continue; 107 } 108 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); 114 continue; 115 } 116 if (Name.substr(1, 3) == "no-" && OptionsByName[Name[0] + Name.substr(4)]) { 117 Aliases[OptionsByName[Name[0] + Name.substr(4)]].push_back(R); 118 continue; 119 } 120 } 121 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); 126 } 127 128 auto CompareByName = [](const Record *A, const Record *B) { 129 return A->getValueAsString("Name") < B->getValueAsString("Name"); 130 }; 131 132 auto CompareByLocation = [](const Record *A, const Record *B) { 133 return A->getLoc()[0].getPointer() < B->getLoc()[0].getPointer(); 134 }; 135 136 auto DocumentationForOption = [&](const Record *R) -> DocumentedOption { 137 auto &A = Aliases[R]; 138 sort(A, CompareByName); 139 return {R, std::move(A)}; 140 }; 141 142 std::function<Documentation(const Record *)> DocumentationForGroup = 143 [&](const Record *R) -> Documentation { 144 Documentation D; 145 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); 153 if (Base.empty()) 154 D.Groups.pop_back(); 155 } 156 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)); 162 163 return D; 164 }; 165 166 return DocumentationForGroup(nullptr); 167 } 168 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({" ", " "}); 177 } 178 179 const unsigned UnlimitedArgs = unsigned(-1); 180 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")) 190 .Default(0); 191 } 192 193 std::string escapeRST(StringRef Str) { 194 std::string Out; 195 for (auto K : Str) { 196 if (StringRef("`*|[]\\").count(K)) 197 Out.push_back('\\'); 198 Out.push_back(K); 199 } 200 return Out; 201 } 202 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()); 207 return OptionName; 208 } 209 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")) 214 if (isalnum(C)) 215 return true; 216 return false; 217 } 218 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"; 223 } 224 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)) { 231 StringRef Value; 232 if (auto *SV = dyn_cast_or_null<StringInit>(V->getValue())) 233 Value = SV->getValue(); 234 if (!Value.empty()) 235 return Field == Primary ? Value.str() : escapeRST(Value); 236 } 237 } 238 return std::string(StringRef()); 239 } 240 241 void emitOptionWithArgs(StringRef Prefix, const Record *Option, 242 ArrayRef<StringRef> Args, raw_ostream &OS) { 243 OS << Prefix << escapeRST(Option->getValueAsString("Name")); 244 245 std::pair<StringRef, StringRef> Separators = 246 getSeparatorsForKind(Option->getValueAsDef("Kind")); 247 248 StringRef Separator = Separators.first; 249 for (auto Arg : Args) { 250 OS << Separator << escapeRST(Arg); 251 Separator = Separators.second; 252 } 253 } 254 255 constexpr StringLiteral DefaultMetaVarName = "<arg>"; 256 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"); 261 262 std::vector<std::string> Args; 263 if (HasMetaVarName) 264 Args.push_back(std::string(Option->getValueAsString("MetaVarName"))); 265 else if (NumArgs == 1) 266 Args.push_back(DefaultMetaVarName.str()); 267 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 272 // arguments. 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() += "..."; 279 break; 280 } 281 } 282 } 283 284 emitOptionWithArgs(Prefix, Option, 285 std::vector<StringRef>(Args.begin(), Args.end()), OS); 286 287 auto AliasArgs = Option->getValueAsListOfStrings("AliasArgs"); 288 if (!AliasArgs.empty()) { 289 const Record *Alias = Option->getValueAsDef("Alias"); 290 OS << " (equivalent to "; 291 emitOptionWithArgs( 292 Alias->getValueAsListOfStrings("Prefixes").front(), Alias, 293 AliasArgs, OS); 294 OS << ")"; 295 } 296 } 297 298 bool emitOptionNames(const Record *Option, raw_ostream &OS, bool EmittedAny) { 299 for (auto &Prefix : Option->getValueAsListOfStrings("Prefixes")) { 300 if (EmittedAny) 301 OS << ", "; 302 emitOptionName(Prefix, Option, OS); 303 EmittedAny = true; 304 } 305 return EmittedAny; 306 } 307 308 template <typename Fn> 309 void forEachOptionName(const DocumentedOption &Option, const Record *DocInfo, 310 Fn F) { 311 F(Option.Option); 312 313 for (auto *Alias : Option.Aliases) 314 if (isOptionVisible(Alias, DocInfo) && 315 canSphinxCopeWithOption(Option.Option)) 316 F(Alias); 317 } 318 319 void emitOption(const DocumentedOption &Option, const Record *DocInfo, 320 raw_ostream &OS) { 321 if (Option.Option->getValueAsDef("Kind")->getName() == "KIND_UNKNOWN" || 322 Option.Option->getValueAsDef("Kind")->getName() == "KIND_INPUT") 323 return; 324 if (!canSphinxCopeWithOption(Option.Option)) 325 return; 326 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()))); 335 }); 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]; 342 })]; 343 for (auto &S : SphinxOptionIDs) 344 NextSuffix[S] = SphinxWorkaroundSuffix + 1; 345 346 std::string Program = DocInfo->getValueAsString("Program").lower(); 347 if (SphinxWorkaroundSuffix) 348 OS << ".. program:: " << Program << SphinxWorkaroundSuffix << "\n"; 349 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); 355 }); 356 if (SphinxWorkaroundSuffix) 357 OS << "\n.. program:: " << Program; 358 OS << "\n\n"; 359 360 // Emit the description, if we have one. 361 const Record *R = Option.Option; 362 std::string Description; 363 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(); 371 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")); 379 break; 380 } 381 } 382 if (!Description.empty()) 383 break; 384 } 385 386 if (!Description.empty()) 387 break; 388 } 389 390 // If there's not a program specific string, use the default one. 391 if (Description.empty()) 392 Description = getRSTStringWithTextFallback(R, "DocBrief", "HelpText"); 393 394 if (!isa<UnsetInit>(R->getValueInit("Values"))) { 395 if (!Description.empty() && Description.back() != '.') 396 Description.push_back('.'); 397 398 StringRef MetaVarName; 399 if (!isa<UnsetInit>(R->getValueInit("MetaVarName"))) 400 MetaVarName = R->getValueAsString("MetaVarName"); 401 else 402 MetaVarName = DefaultMetaVarName; 403 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 '"; 410 } 411 Description += (Values.back() + "'.").str(); 412 } 413 414 if (!Description.empty()) 415 OS << Description << "\n\n"; 416 } 417 418 void emitDocumentation(int Depth, const Documentation &Doc, 419 const Record *DocInfo, raw_ostream &OS); 420 421 void emitGroup(int Depth, const DocumentedGroup &Group, const Record *DocInfo, 422 raw_ostream &OS) { 423 emitHeading(Depth, 424 getRSTStringWithTextFallback(Group.Group, "DocName", "Name"), OS); 425 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"; 431 432 // Emit contained options and groups. 433 emitDocumentation(Depth + 1, Group, DocInfo, OS); 434 } 435 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); 442 } 443 444 } // namespace 445 446 void clang::EmitClangOptDocs(const RecordKeeper &Records, raw_ostream &OS) { 447 const Record *DocInfo = Records.getDef("GlobalDocumentation"); 448 if (!DocInfo) { 449 PrintFatalError("The GlobalDocumentation top-level definition is missing, " 450 "no documentation will be generated."); 451 return; 452 } 453 OS << DocInfo->getValueAsString("Intro") << "\n"; 454 OS << ".. program:: " << DocInfo->getValueAsString("Program").lower() << "\n"; 455 456 emitDocumentation(0, extractDocumentation(Records, DocInfo), DocInfo, OS); 457 } 458