1 //===- OptTable.cpp - Option Table Implementation -------------------------===// 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 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Option/OptTable.h" 10 #include "llvm/ADT/STLExtras.h" 11 #include "llvm/ADT/StringRef.h" 12 #include "llvm/Option/Arg.h" 13 #include "llvm/Option/ArgList.h" 14 #include "llvm/Option/OptSpecifier.h" 15 #include "llvm/Option/Option.h" 16 #include "llvm/Support/CommandLine.h" // for expandResponseFiles 17 #include "llvm/Support/Compiler.h" 18 #include "llvm/Support/ErrorHandling.h" 19 #include "llvm/Support/raw_ostream.h" 20 #include <algorithm> 21 #include <cassert> 22 #include <cctype> 23 #include <cstring> 24 #include <map> 25 #include <set> 26 #include <string> 27 #include <utility> 28 #include <vector> 29 30 using namespace llvm; 31 using namespace llvm::opt; 32 33 namespace llvm { 34 namespace opt { 35 36 // Ordering on Info. The ordering is *almost* case-insensitive lexicographic, 37 // with an exception. '\0' comes at the end of the alphabet instead of the 38 // beginning (thus options precede any other options which prefix them). 39 static int StrCmpOptionNameIgnoreCase(StringRef A, StringRef B) { 40 size_t MinSize = std::min(A.size(), B.size()); 41 if (int Res = A.substr(0, MinSize).compare_insensitive(B.substr(0, MinSize))) 42 return Res; 43 44 if (A.size() == B.size()) 45 return 0; 46 47 return (A.size() == MinSize) ? 1 /* A is a prefix of B. */ 48 : -1 /* B is a prefix of A */; 49 } 50 51 #ifndef NDEBUG 52 static int StrCmpOptionName(StringRef A, StringRef B) { 53 if (int N = StrCmpOptionNameIgnoreCase(A, B)) 54 return N; 55 return A.compare(B); 56 } 57 58 static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) { 59 if (&A == &B) 60 return false; 61 62 if (int N = StrCmpOptionName(A.getName(), B.getName())) 63 return N < 0; 64 65 for (size_t I = 0, K = std::min(A.Prefixes.size(), B.Prefixes.size()); I != K; 66 ++I) 67 if (int N = StrCmpOptionName(A.Prefixes[I], B.Prefixes[I])) 68 return N < 0; 69 70 // Names are the same, check that classes are in order; exactly one 71 // should be joined, and it should succeed the other. 72 assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) && 73 "Unexpected classes for options with same name."); 74 return B.Kind == Option::JoinedClass; 75 } 76 #endif 77 78 // Support lower_bound between info and an option name. 79 static inline bool operator<(const OptTable::Info &I, StringRef Name) { 80 return StrCmpOptionNameIgnoreCase(I.getName(), Name) < 0; 81 } 82 83 } // end namespace opt 84 } // end namespace llvm 85 86 OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {} 87 88 OptTable::OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase) 89 : OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) { 90 // Explicitly zero initialize the error to work around a bug in array 91 // value-initialization on MinGW with gcc 4.3.5. 92 93 // Find start of normal options. 94 for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { 95 unsigned Kind = getInfo(i + 1).Kind; 96 if (Kind == Option::InputClass) { 97 assert(!InputOptionID && "Cannot have multiple input options!"); 98 InputOptionID = getInfo(i + 1).ID; 99 } else if (Kind == Option::UnknownClass) { 100 assert(!UnknownOptionID && "Cannot have multiple unknown options!"); 101 UnknownOptionID = getInfo(i + 1).ID; 102 } else if (Kind != Option::GroupClass) { 103 FirstSearchableIndex = i; 104 break; 105 } 106 } 107 assert(FirstSearchableIndex != 0 && "No searchable options?"); 108 109 #ifndef NDEBUG 110 // Check that everything after the first searchable option is a 111 // regular option class. 112 for (unsigned i = FirstSearchableIndex, e = getNumOptions(); i != e; ++i) { 113 Option::OptionClass Kind = (Option::OptionClass) getInfo(i + 1).Kind; 114 assert((Kind != Option::InputClass && Kind != Option::UnknownClass && 115 Kind != Option::GroupClass) && 116 "Special options should be defined first!"); 117 } 118 119 // Check that options are in order. 120 for (unsigned i = FirstSearchableIndex + 1, e = getNumOptions(); i != e; ++i){ 121 if (!(getInfo(i) < getInfo(i + 1))) { 122 getOption(i).dump(); 123 getOption(i + 1).dump(); 124 llvm_unreachable("Options are not in order!"); 125 } 126 } 127 #endif 128 } 129 130 void OptTable::buildPrefixChars() { 131 assert(PrefixChars.empty() && "rebuilding a non-empty prefix char"); 132 133 // Build prefix chars. 134 for (const StringLiteral &Prefix : getPrefixesUnion()) { 135 for (char C : Prefix) 136 if (!is_contained(PrefixChars, C)) 137 PrefixChars.push_back(C); 138 } 139 } 140 141 OptTable::~OptTable() = default; 142 143 const Option OptTable::getOption(OptSpecifier Opt) const { 144 unsigned id = Opt.getID(); 145 if (id == 0) 146 return Option(nullptr, nullptr); 147 assert((unsigned) (id - 1) < getNumOptions() && "Invalid ID."); 148 return Option(&getInfo(id), this); 149 } 150 151 static bool isInput(const ArrayRef<StringLiteral> &Prefixes, StringRef Arg) { 152 if (Arg == "-") 153 return true; 154 for (const StringRef &Prefix : Prefixes) 155 if (Arg.starts_with(Prefix)) 156 return false; 157 return true; 158 } 159 160 /// \returns Matched size. 0 means no match. 161 static unsigned matchOption(const OptTable::Info *I, StringRef Str, 162 bool IgnoreCase) { 163 for (auto Prefix : I->Prefixes) { 164 if (Str.starts_with(Prefix)) { 165 StringRef Rest = Str.substr(Prefix.size()); 166 bool Matched = IgnoreCase ? Rest.starts_with_insensitive(I->getName()) 167 : Rest.starts_with(I->getName()); 168 if (Matched) 169 return Prefix.size() + StringRef(I->getName()).size(); 170 } 171 } 172 return 0; 173 } 174 175 // Returns true if one of the Prefixes + In.Names matches Option 176 static bool optionMatches(const OptTable::Info &In, StringRef Option) { 177 for (auto Prefix : In.Prefixes) 178 if (Option.ends_with(In.getName())) 179 if (Option.slice(0, Option.size() - In.getName().size()) == Prefix) 180 return true; 181 return false; 182 } 183 184 // This function is for flag value completion. 185 // Eg. When "-stdlib=" and "l" was passed to this function, it will return 186 // appropiriate values for stdlib, which starts with l. 187 std::vector<std::string> 188 OptTable::suggestValueCompletions(StringRef Option, StringRef Arg) const { 189 // Search all options and return possible values. 190 for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) { 191 const Info &In = OptionInfos[I]; 192 if (!In.Values || !optionMatches(In, Option)) 193 continue; 194 195 SmallVector<StringRef, 8> Candidates; 196 StringRef(In.Values).split(Candidates, ",", -1, false); 197 198 std::vector<std::string> Result; 199 for (StringRef Val : Candidates) 200 if (Val.starts_with(Arg) && Arg.compare(Val)) 201 Result.push_back(std::string(Val)); 202 return Result; 203 } 204 return {}; 205 } 206 207 std::vector<std::string> 208 OptTable::findByPrefix(StringRef Cur, Visibility VisibilityMask, 209 unsigned int DisableFlags) const { 210 std::vector<std::string> Ret; 211 for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) { 212 const Info &In = OptionInfos[I]; 213 if (In.Prefixes.empty() || (!In.HelpText && !In.GroupID)) 214 continue; 215 if (!(In.Visibility & VisibilityMask)) 216 continue; 217 if (In.Flags & DisableFlags) 218 continue; 219 220 for (auto Prefix : In.Prefixes) { 221 std::string S = (Prefix + In.getName() + "\t").str(); 222 if (In.HelpText) 223 S += In.HelpText; 224 if (StringRef(S).starts_with(Cur) && S != std::string(Cur) + "\t") 225 Ret.push_back(S); 226 } 227 } 228 return Ret; 229 } 230 231 unsigned OptTable::findNearest(StringRef Option, std::string &NearestString, 232 Visibility VisibilityMask, 233 unsigned MinimumLength, 234 unsigned MaximumDistance) const { 235 return internalFindNearest( 236 Option, NearestString, MinimumLength, MaximumDistance, 237 [VisibilityMask](const Info &CandidateInfo) { 238 return (CandidateInfo.Visibility & VisibilityMask) == 0; 239 }); 240 } 241 242 unsigned OptTable::findNearest(StringRef Option, std::string &NearestString, 243 unsigned FlagsToInclude, unsigned FlagsToExclude, 244 unsigned MinimumLength, 245 unsigned MaximumDistance) const { 246 return internalFindNearest( 247 Option, NearestString, MinimumLength, MaximumDistance, 248 [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) { 249 if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude)) 250 return true; 251 if (CandidateInfo.Flags & FlagsToExclude) 252 return true; 253 return false; 254 }); 255 } 256 257 unsigned OptTable::internalFindNearest( 258 StringRef Option, std::string &NearestString, unsigned MinimumLength, 259 unsigned MaximumDistance, 260 std::function<bool(const Info &)> ExcludeOption) const { 261 assert(!Option.empty()); 262 263 // Consider each [option prefix + option name] pair as a candidate, finding 264 // the closest match. 265 unsigned BestDistance = 266 MaximumDistance == UINT_MAX ? UINT_MAX : MaximumDistance + 1; 267 SmallString<16> Candidate; 268 SmallString<16> NormalizedName; 269 270 for (const Info &CandidateInfo : 271 ArrayRef<Info>(OptionInfos).drop_front(FirstSearchableIndex)) { 272 StringRef CandidateName = CandidateInfo.getName(); 273 274 // We can eliminate some option prefix/name pairs as candidates right away: 275 // * Ignore option candidates with empty names, such as "--", or names 276 // that do not meet the minimum length. 277 if (CandidateName.size() < MinimumLength) 278 continue; 279 280 // Ignore options that are excluded via masks 281 if (ExcludeOption(CandidateInfo)) 282 continue; 283 284 // * Ignore positional argument option candidates (which do not 285 // have prefixes). 286 if (CandidateInfo.Prefixes.empty()) 287 continue; 288 289 // Now check if the candidate ends with a character commonly used when 290 // delimiting an option from its value, such as '=' or ':'. If it does, 291 // attempt to split the given option based on that delimiter. 292 char Last = CandidateName.back(); 293 bool CandidateHasDelimiter = Last == '=' || Last == ':'; 294 StringRef RHS; 295 if (CandidateHasDelimiter) { 296 std::tie(NormalizedName, RHS) = Option.split(Last); 297 if (Option.find(Last) == NormalizedName.size()) 298 NormalizedName += Last; 299 } else 300 NormalizedName = Option; 301 302 // Consider each possible prefix for each candidate to find the most 303 // appropriate one. For example, if a user asks for "--helm", suggest 304 // "--help" over "-help". 305 for (auto CandidatePrefix : CandidateInfo.Prefixes) { 306 // If Candidate and NormalizedName have more than 'BestDistance' 307 // characters of difference, no need to compute the edit distance, it's 308 // going to be greater than BestDistance. Don't bother computing Candidate 309 // at all. 310 size_t CandidateSize = CandidatePrefix.size() + CandidateName.size(), 311 NormalizedSize = NormalizedName.size(); 312 size_t AbsDiff = CandidateSize > NormalizedSize 313 ? CandidateSize - NormalizedSize 314 : NormalizedSize - CandidateSize; 315 if (AbsDiff > BestDistance) { 316 continue; 317 } 318 Candidate = CandidatePrefix; 319 Candidate += CandidateName; 320 unsigned Distance = StringRef(Candidate).edit_distance( 321 NormalizedName, /*AllowReplacements=*/true, 322 /*MaxEditDistance=*/BestDistance); 323 if (RHS.empty() && CandidateHasDelimiter) { 324 // The Candidate ends with a = or : delimiter, but the option passed in 325 // didn't contain the delimiter (or doesn't have anything after it). 326 // In that case, penalize the correction: `-nodefaultlibs` is more 327 // likely to be a spello for `-nodefaultlib` than `-nodefaultlib:` even 328 // though both have an unmodified editing distance of 1, since the 329 // latter would need an argument. 330 ++Distance; 331 } 332 if (Distance < BestDistance) { 333 BestDistance = Distance; 334 NearestString = (Candidate + RHS).str(); 335 } 336 } 337 } 338 return BestDistance; 339 } 340 341 // Parse a single argument, return the new argument, and update Index. If 342 // GroupedShortOptions is true, -a matches "-abc" and the argument in Args will 343 // be updated to "-bc". This overload does not support VisibilityMask or case 344 // insensitive options. 345 std::unique_ptr<Arg> OptTable::parseOneArgGrouped(InputArgList &Args, 346 unsigned &Index) const { 347 // Anything that doesn't start with PrefixesUnion is an input, as is '-' 348 // itself. 349 const char *CStr = Args.getArgString(Index); 350 StringRef Str(CStr); 351 if (isInput(getPrefixesUnion(), Str)) 352 return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, CStr); 353 354 const Info *End = OptionInfos.data() + OptionInfos.size(); 355 StringRef Name = Str.ltrim(PrefixChars); 356 const Info *Start = 357 std::lower_bound(OptionInfos.data() + FirstSearchableIndex, End, Name); 358 const Info *Fallback = nullptr; 359 unsigned Prev = Index; 360 361 // Search for the option which matches Str. 362 for (; Start != End; ++Start) { 363 unsigned ArgSize = matchOption(Start, Str, IgnoreCase); 364 if (!ArgSize) 365 continue; 366 367 Option Opt(Start, this); 368 if (std::unique_ptr<Arg> A = 369 Opt.accept(Args, StringRef(Args.getArgString(Index), ArgSize), 370 /*GroupedShortOption=*/false, Index)) 371 return A; 372 373 // If Opt is a Flag of length 2 (e.g. "-a"), we know it is a prefix of 374 // the current argument (e.g. "-abc"). Match it as a fallback if no longer 375 // option (e.g. "-ab") exists. 376 if (ArgSize == 2 && Opt.getKind() == Option::FlagClass) 377 Fallback = Start; 378 379 // Otherwise, see if the argument is missing. 380 if (Prev != Index) 381 return nullptr; 382 } 383 if (Fallback) { 384 Option Opt(Fallback, this); 385 // Check that the last option isn't a flag wrongly given an argument. 386 if (Str[2] == '=') 387 return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++, 388 CStr); 389 390 if (std::unique_ptr<Arg> A = Opt.accept( 391 Args, Str.substr(0, 2), /*GroupedShortOption=*/true, Index)) { 392 Args.replaceArgString(Index, Twine('-') + Str.substr(2)); 393 return A; 394 } 395 } 396 397 // In the case of an incorrect short option extract the character and move to 398 // the next one. 399 if (Str[1] != '-') { 400 CStr = Args.MakeArgString(Str.substr(0, 2)); 401 Args.replaceArgString(Index, Twine('-') + Str.substr(2)); 402 return std::make_unique<Arg>(getOption(UnknownOptionID), CStr, Index, CStr); 403 } 404 405 return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++, CStr); 406 } 407 408 std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index, 409 Visibility VisibilityMask) const { 410 return internalParseOneArg(Args, Index, [VisibilityMask](const Option &Opt) { 411 return !Opt.hasVisibilityFlag(VisibilityMask); 412 }); 413 } 414 415 std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index, 416 unsigned FlagsToInclude, 417 unsigned FlagsToExclude) const { 418 return internalParseOneArg( 419 Args, Index, [FlagsToInclude, FlagsToExclude](const Option &Opt) { 420 if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude)) 421 return true; 422 if (Opt.hasFlag(FlagsToExclude)) 423 return true; 424 return false; 425 }); 426 } 427 428 std::unique_ptr<Arg> OptTable::internalParseOneArg( 429 const ArgList &Args, unsigned &Index, 430 std::function<bool(const Option &)> ExcludeOption) const { 431 unsigned Prev = Index; 432 StringRef Str = Args.getArgString(Index); 433 434 // Anything that doesn't start with PrefixesUnion is an input, as is '-' 435 // itself. 436 if (isInput(getPrefixesUnion(), Str)) 437 return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, 438 Str.data()); 439 440 const Info *Start = OptionInfos.data() + FirstSearchableIndex; 441 const Info *End = OptionInfos.data() + OptionInfos.size(); 442 StringRef Name = Str.ltrim(PrefixChars); 443 444 // Search for the first next option which could be a prefix. 445 Start = std::lower_bound(Start, End, Name); 446 447 // Options are stored in sorted order, with '\0' at the end of the 448 // alphabet. Since the only options which can accept a string must 449 // prefix it, we iteratively search for the next option which could 450 // be a prefix. 451 // 452 // FIXME: This is searching much more than necessary, but I am 453 // blanking on the simplest way to make it fast. We can solve this 454 // problem when we move to TableGen. 455 for (; Start != End; ++Start) { 456 unsigned ArgSize = 0; 457 // Scan for first option which is a proper prefix. 458 for (; Start != End; ++Start) 459 if ((ArgSize = matchOption(Start, Str, IgnoreCase))) 460 break; 461 if (Start == End) 462 break; 463 464 Option Opt(Start, this); 465 466 if (ExcludeOption(Opt)) 467 continue; 468 469 // See if this option matches. 470 if (std::unique_ptr<Arg> A = 471 Opt.accept(Args, StringRef(Args.getArgString(Index), ArgSize), 472 /*GroupedShortOption=*/false, Index)) 473 return A; 474 475 // Otherwise, see if this argument was missing values. 476 if (Prev != Index) 477 return nullptr; 478 } 479 480 // If we failed to find an option and this arg started with /, then it's 481 // probably an input path. 482 if (Str[0] == '/') 483 return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, 484 Str.data()); 485 486 return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++, 487 Str.data()); 488 } 489 490 InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args, 491 unsigned &MissingArgIndex, 492 unsigned &MissingArgCount, 493 Visibility VisibilityMask) const { 494 return internalParseArgs( 495 Args, MissingArgIndex, MissingArgCount, 496 [VisibilityMask](const Option &Opt) { 497 return !Opt.hasVisibilityFlag(VisibilityMask); 498 }); 499 } 500 501 InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args, 502 unsigned &MissingArgIndex, 503 unsigned &MissingArgCount, 504 unsigned FlagsToInclude, 505 unsigned FlagsToExclude) const { 506 return internalParseArgs( 507 Args, MissingArgIndex, MissingArgCount, 508 [FlagsToInclude, FlagsToExclude](const Option &Opt) { 509 if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude)) 510 return true; 511 if (Opt.hasFlag(FlagsToExclude)) 512 return true; 513 return false; 514 }); 515 } 516 517 InputArgList OptTable::internalParseArgs( 518 ArrayRef<const char *> ArgArr, unsigned &MissingArgIndex, 519 unsigned &MissingArgCount, 520 std::function<bool(const Option &)> ExcludeOption) const { 521 InputArgList Args(ArgArr.begin(), ArgArr.end()); 522 523 // FIXME: Handle '@' args (or at least error on them). 524 525 MissingArgIndex = MissingArgCount = 0; 526 unsigned Index = 0, End = ArgArr.size(); 527 while (Index < End) { 528 // Ingore nullptrs, they are response file's EOL markers 529 if (Args.getArgString(Index) == nullptr) { 530 ++Index; 531 continue; 532 } 533 // Ignore empty arguments (other things may still take them as arguments). 534 StringRef Str = Args.getArgString(Index); 535 if (Str == "") { 536 ++Index; 537 continue; 538 } 539 540 // In DashDashParsing mode, the first "--" stops option scanning and treats 541 // all subsequent arguments as positional. 542 if (DashDashParsing && Str == "--") { 543 while (++Index < End) { 544 Args.append(new Arg(getOption(InputOptionID), Str, Index, 545 Args.getArgString(Index))); 546 } 547 break; 548 } 549 550 unsigned Prev = Index; 551 std::unique_ptr<Arg> A = GroupedShortOptions 552 ? parseOneArgGrouped(Args, Index) 553 : internalParseOneArg(Args, Index, ExcludeOption); 554 assert((Index > Prev || GroupedShortOptions) && 555 "Parser failed to consume argument."); 556 557 // Check for missing argument error. 558 if (!A) { 559 assert(Index >= End && "Unexpected parser error."); 560 assert(Index - Prev - 1 && "No missing arguments!"); 561 MissingArgIndex = Prev; 562 MissingArgCount = Index - Prev - 1; 563 break; 564 } 565 566 Args.append(A.release()); 567 } 568 569 return Args; 570 } 571 572 InputArgList OptTable::parseArgs(int Argc, char *const *Argv, 573 OptSpecifier Unknown, StringSaver &Saver, 574 std::function<void(StringRef)> ErrorFn) const { 575 SmallVector<const char *, 0> NewArgv; 576 // The environment variable specifies initial options which can be overridden 577 // by commnad line options. 578 cl::expandResponseFiles(Argc, Argv, EnvVar, Saver, NewArgv); 579 580 unsigned MAI, MAC; 581 opt::InputArgList Args = ParseArgs(ArrayRef(NewArgv), MAI, MAC); 582 if (MAC) 583 ErrorFn((Twine(Args.getArgString(MAI)) + ": missing argument").str()); 584 585 // For each unknwon option, call ErrorFn with a formatted error message. The 586 // message includes a suggested alternative option spelling if available. 587 std::string Nearest; 588 for (const opt::Arg *A : Args.filtered(Unknown)) { 589 std::string Spelling = A->getAsString(Args); 590 if (findNearest(Spelling, Nearest) > 1) 591 ErrorFn("unknown argument '" + Spelling + "'"); 592 else 593 ErrorFn("unknown argument '" + Spelling + "', did you mean '" + Nearest + 594 "'?"); 595 } 596 return Args; 597 } 598 599 static std::string getOptionHelpName(const OptTable &Opts, OptSpecifier Id) { 600 const Option O = Opts.getOption(Id); 601 std::string Name = O.getPrefixedName().str(); 602 603 // Add metavar, if used. 604 switch (O.getKind()) { 605 case Option::GroupClass: case Option::InputClass: case Option::UnknownClass: 606 llvm_unreachable("Invalid option with help text."); 607 608 case Option::MultiArgClass: 609 if (const char *MetaVarName = Opts.getOptionMetaVar(Id)) { 610 // For MultiArgs, metavar is full list of all argument names. 611 Name += ' '; 612 Name += MetaVarName; 613 } 614 else { 615 // For MultiArgs<N>, if metavar not supplied, print <value> N times. 616 for (unsigned i=0, e=O.getNumArgs(); i< e; ++i) { 617 Name += " <value>"; 618 } 619 } 620 break; 621 622 case Option::FlagClass: 623 break; 624 625 case Option::ValuesClass: 626 break; 627 628 case Option::SeparateClass: case Option::JoinedOrSeparateClass: 629 case Option::RemainingArgsClass: case Option::RemainingArgsJoinedClass: 630 Name += ' '; 631 [[fallthrough]]; 632 case Option::JoinedClass: case Option::CommaJoinedClass: 633 case Option::JoinedAndSeparateClass: 634 if (const char *MetaVarName = Opts.getOptionMetaVar(Id)) 635 Name += MetaVarName; 636 else 637 Name += "<value>"; 638 break; 639 } 640 641 return Name; 642 } 643 644 namespace { 645 struct OptionInfo { 646 std::string Name; 647 StringRef HelpText; 648 }; 649 } // namespace 650 651 static void PrintHelpOptionList(raw_ostream &OS, StringRef Title, 652 std::vector<OptionInfo> &OptionHelp) { 653 OS << Title << ":\n"; 654 655 // Find the maximum option length. 656 unsigned OptionFieldWidth = 0; 657 for (const OptionInfo &Opt : OptionHelp) { 658 // Limit the amount of padding we are willing to give up for alignment. 659 unsigned Length = Opt.Name.size(); 660 if (Length <= 23) 661 OptionFieldWidth = std::max(OptionFieldWidth, Length); 662 } 663 664 const unsigned InitialPad = 2; 665 for (const OptionInfo &Opt : OptionHelp) { 666 const std::string &Option = Opt.Name; 667 int Pad = OptionFieldWidth + InitialPad; 668 int FirstLinePad = OptionFieldWidth - int(Option.size()); 669 OS.indent(InitialPad) << Option; 670 671 // Break on long option names. 672 if (FirstLinePad < 0) { 673 OS << "\n"; 674 FirstLinePad = OptionFieldWidth + InitialPad; 675 Pad = FirstLinePad; 676 } 677 678 SmallVector<StringRef> Lines; 679 Opt.HelpText.split(Lines, '\n'); 680 assert(Lines.size() && "Expected at least the first line in the help text"); 681 auto *LinesIt = Lines.begin(); 682 OS.indent(FirstLinePad + 1) << *LinesIt << '\n'; 683 while (Lines.end() != ++LinesIt) 684 OS.indent(Pad + 1) << *LinesIt << '\n'; 685 } 686 } 687 688 static const char *getOptionHelpGroup(const OptTable &Opts, OptSpecifier Id) { 689 unsigned GroupID = Opts.getOptionGroupID(Id); 690 691 // If not in a group, return the default help group. 692 if (!GroupID) 693 return "OPTIONS"; 694 695 // Abuse the help text of the option groups to store the "help group" 696 // name. 697 // 698 // FIXME: Split out option groups. 699 if (const char *GroupHelp = Opts.getOptionHelpText(GroupID)) 700 return GroupHelp; 701 702 // Otherwise keep looking. 703 return getOptionHelpGroup(Opts, GroupID); 704 } 705 706 void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title, 707 bool ShowHidden, bool ShowAllAliases, 708 Visibility VisibilityMask) const { 709 return internalPrintHelp( 710 OS, Usage, Title, ShowHidden, ShowAllAliases, 711 [VisibilityMask](const Info &CandidateInfo) -> bool { 712 return (CandidateInfo.Visibility & VisibilityMask) == 0; 713 }); 714 } 715 716 void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title, 717 unsigned FlagsToInclude, unsigned FlagsToExclude, 718 bool ShowAllAliases) const { 719 bool ShowHidden = !(FlagsToExclude & HelpHidden); 720 FlagsToExclude &= ~HelpHidden; 721 return internalPrintHelp( 722 OS, Usage, Title, ShowHidden, ShowAllAliases, 723 [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) { 724 if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude)) 725 return true; 726 if (CandidateInfo.Flags & FlagsToExclude) 727 return true; 728 return false; 729 }); 730 } 731 732 void OptTable::internalPrintHelp( 733 raw_ostream &OS, const char *Usage, const char *Title, bool ShowHidden, 734 bool ShowAllAliases, 735 std::function<bool(const Info &)> ExcludeOption) const { 736 OS << "OVERVIEW: " << Title << "\n\n"; 737 OS << "USAGE: " << Usage << "\n\n"; 738 739 // Render help text into a map of group-name to a list of (option, help) 740 // pairs. 741 std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp; 742 743 for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) { 744 // FIXME: Split out option groups. 745 if (getOptionKind(Id) == Option::GroupClass) 746 continue; 747 748 const Info &CandidateInfo = getInfo(Id); 749 if (!ShowHidden && (CandidateInfo.Flags & opt::HelpHidden)) 750 continue; 751 752 if (ExcludeOption(CandidateInfo)) 753 continue; 754 755 // If an alias doesn't have a help text, show a help text for the aliased 756 // option instead. 757 const char *HelpText = getOptionHelpText(Id); 758 if (!HelpText && ShowAllAliases) { 759 const Option Alias = getOption(Id).getAlias(); 760 if (Alias.isValid()) 761 HelpText = getOptionHelpText(Alias.getID()); 762 } 763 764 if (HelpText && (strlen(HelpText) != 0)) { 765 const char *HelpGroup = getOptionHelpGroup(*this, Id); 766 const std::string &OptName = getOptionHelpName(*this, Id); 767 GroupedOptionHelp[HelpGroup].push_back({OptName, HelpText}); 768 } 769 } 770 771 for (auto& OptionGroup : GroupedOptionHelp) { 772 if (OptionGroup.first != GroupedOptionHelp.begin()->first) 773 OS << "\n"; 774 PrintHelpOptionList(OS, OptionGroup.first, OptionGroup.second); 775 } 776 777 OS.flush(); 778 } 779 780 GenericOptTable::GenericOptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase) 781 : OptTable(OptionInfos, IgnoreCase) { 782 783 std::set<StringLiteral> TmpPrefixesUnion; 784 for (auto const &Info : OptionInfos.drop_front(FirstSearchableIndex)) 785 TmpPrefixesUnion.insert(Info.Prefixes.begin(), Info.Prefixes.end()); 786 PrefixesUnionBuffer.append(TmpPrefixesUnion.begin(), TmpPrefixesUnion.end()); 787 buildPrefixChars(); 788 } 789