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