Path: blob/main/contrib/llvm-project/llvm/lib/Option/OptTable.cpp
35232 views
//===- OptTable.cpp - Option Table Implementation -------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/Option/OptTable.h"9#include "llvm/ADT/STLExtras.h"10#include "llvm/ADT/StringRef.h"11#include "llvm/Option/Arg.h"12#include "llvm/Option/ArgList.h"13#include "llvm/Option/OptSpecifier.h"14#include "llvm/Option/Option.h"15#include "llvm/Support/CommandLine.h" // for expandResponseFiles16#include "llvm/Support/Compiler.h"17#include "llvm/Support/ErrorHandling.h"18#include "llvm/Support/raw_ostream.h"19#include <algorithm>20#include <cassert>21#include <cctype>22#include <cstring>23#include <map>24#include <set>25#include <string>26#include <utility>27#include <vector>2829using namespace llvm;30using namespace llvm::opt;3132namespace llvm {33namespace opt {3435// Ordering on Info. The ordering is *almost* case-insensitive lexicographic,36// with an exception. '\0' comes at the end of the alphabet instead of the37// beginning (thus options precede any other options which prefix them).38static int StrCmpOptionNameIgnoreCase(StringRef A, StringRef B) {39size_t MinSize = std::min(A.size(), B.size());40if (int Res = A.substr(0, MinSize).compare_insensitive(B.substr(0, MinSize)))41return Res;4243if (A.size() == B.size())44return 0;4546return (A.size() == MinSize) ? 1 /* A is a prefix of B. */47: -1 /* B is a prefix of A */;48}4950#ifndef NDEBUG51static int StrCmpOptionName(StringRef A, StringRef B) {52if (int N = StrCmpOptionNameIgnoreCase(A, B))53return N;54return A.compare(B);55}5657static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) {58if (&A == &B)59return false;6061if (int N = StrCmpOptionName(A.getName(), B.getName()))62return N < 0;6364for (size_t I = 0, K = std::min(A.Prefixes.size(), B.Prefixes.size()); I != K;65++I)66if (int N = StrCmpOptionName(A.Prefixes[I], B.Prefixes[I]))67return N < 0;6869// Names are the same, check that classes are in order; exactly one70// should be joined, and it should succeed the other.71assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) &&72"Unexpected classes for options with same name.");73return B.Kind == Option::JoinedClass;74}75#endif7677// Support lower_bound between info and an option name.78static inline bool operator<(const OptTable::Info &I, StringRef Name) {79return StrCmpOptionNameIgnoreCase(I.getName(), Name) < 0;80}8182} // end namespace opt83} // end namespace llvm8485OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {}8687OptTable::OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase)88: OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) {89// Explicitly zero initialize the error to work around a bug in array90// value-initialization on MinGW with gcc 4.3.5.9192// Find start of normal options.93for (unsigned i = 0, e = getNumOptions(); i != e; ++i) {94unsigned Kind = getInfo(i + 1).Kind;95if (Kind == Option::InputClass) {96assert(!InputOptionID && "Cannot have multiple input options!");97InputOptionID = getInfo(i + 1).ID;98} else if (Kind == Option::UnknownClass) {99assert(!UnknownOptionID && "Cannot have multiple unknown options!");100UnknownOptionID = getInfo(i + 1).ID;101} else if (Kind != Option::GroupClass) {102FirstSearchableIndex = i;103break;104}105}106assert(FirstSearchableIndex != 0 && "No searchable options?");107108#ifndef NDEBUG109// Check that everything after the first searchable option is a110// regular option class.111for (unsigned i = FirstSearchableIndex, e = getNumOptions(); i != e; ++i) {112Option::OptionClass Kind = (Option::OptionClass) getInfo(i + 1).Kind;113assert((Kind != Option::InputClass && Kind != Option::UnknownClass &&114Kind != Option::GroupClass) &&115"Special options should be defined first!");116}117118// Check that options are in order.119for (unsigned i = FirstSearchableIndex + 1, e = getNumOptions(); i != e; ++i){120if (!(getInfo(i) < getInfo(i + 1))) {121getOption(i).dump();122getOption(i + 1).dump();123llvm_unreachable("Options are not in order!");124}125}126#endif127}128129void OptTable::buildPrefixChars() {130assert(PrefixChars.empty() && "rebuilding a non-empty prefix char");131132// Build prefix chars.133for (const StringLiteral &Prefix : getPrefixesUnion()) {134for (char C : Prefix)135if (!is_contained(PrefixChars, C))136PrefixChars.push_back(C);137}138}139140OptTable::~OptTable() = default;141142const Option OptTable::getOption(OptSpecifier Opt) const {143unsigned id = Opt.getID();144if (id == 0)145return Option(nullptr, nullptr);146assert((unsigned) (id - 1) < getNumOptions() && "Invalid ID.");147return Option(&getInfo(id), this);148}149150static bool isInput(const ArrayRef<StringLiteral> &Prefixes, StringRef Arg) {151if (Arg == "-")152return true;153for (const StringRef &Prefix : Prefixes)154if (Arg.starts_with(Prefix))155return false;156return true;157}158159/// \returns Matched size. 0 means no match.160static unsigned matchOption(const OptTable::Info *I, StringRef Str,161bool IgnoreCase) {162for (auto Prefix : I->Prefixes) {163if (Str.starts_with(Prefix)) {164StringRef Rest = Str.substr(Prefix.size());165bool Matched = IgnoreCase ? Rest.starts_with_insensitive(I->getName())166: Rest.starts_with(I->getName());167if (Matched)168return Prefix.size() + StringRef(I->getName()).size();169}170}171return 0;172}173174// Returns true if one of the Prefixes + In.Names matches Option175static bool optionMatches(const OptTable::Info &In, StringRef Option) {176for (auto Prefix : In.Prefixes)177if (Option.ends_with(In.getName()))178if (Option.slice(0, Option.size() - In.getName().size()) == Prefix)179return true;180return false;181}182183// This function is for flag value completion.184// Eg. When "-stdlib=" and "l" was passed to this function, it will return185// appropiriate values for stdlib, which starts with l.186std::vector<std::string>187OptTable::suggestValueCompletions(StringRef Option, StringRef Arg) const {188// Search all options and return possible values.189for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) {190const Info &In = OptionInfos[I];191if (!In.Values || !optionMatches(In, Option))192continue;193194SmallVector<StringRef, 8> Candidates;195StringRef(In.Values).split(Candidates, ",", -1, false);196197std::vector<std::string> Result;198for (StringRef Val : Candidates)199if (Val.starts_with(Arg) && Arg != Val)200Result.push_back(std::string(Val));201return Result;202}203return {};204}205206std::vector<std::string>207OptTable::findByPrefix(StringRef Cur, Visibility VisibilityMask,208unsigned int DisableFlags) const {209std::vector<std::string> Ret;210for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) {211const Info &In = OptionInfos[I];212if (In.Prefixes.empty() || (!In.HelpText && !In.GroupID))213continue;214if (!(In.Visibility & VisibilityMask))215continue;216if (In.Flags & DisableFlags)217continue;218219for (auto Prefix : In.Prefixes) {220std::string S = (Prefix + In.getName() + "\t").str();221if (In.HelpText)222S += In.HelpText;223if (StringRef(S).starts_with(Cur) && S != std::string(Cur) + "\t")224Ret.push_back(S);225}226}227return Ret;228}229230unsigned OptTable::findNearest(StringRef Option, std::string &NearestString,231Visibility VisibilityMask,232unsigned MinimumLength,233unsigned MaximumDistance) const {234return internalFindNearest(235Option, NearestString, MinimumLength, MaximumDistance,236[VisibilityMask](const Info &CandidateInfo) {237return (CandidateInfo.Visibility & VisibilityMask) == 0;238});239}240241unsigned OptTable::findNearest(StringRef Option, std::string &NearestString,242unsigned FlagsToInclude, unsigned FlagsToExclude,243unsigned MinimumLength,244unsigned MaximumDistance) const {245return internalFindNearest(246Option, NearestString, MinimumLength, MaximumDistance,247[FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {248if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude))249return true;250if (CandidateInfo.Flags & FlagsToExclude)251return true;252return false;253});254}255256unsigned OptTable::internalFindNearest(257StringRef Option, std::string &NearestString, unsigned MinimumLength,258unsigned MaximumDistance,259std::function<bool(const Info &)> ExcludeOption) const {260assert(!Option.empty());261262// Consider each [option prefix + option name] pair as a candidate, finding263// the closest match.264unsigned BestDistance =265MaximumDistance == UINT_MAX ? UINT_MAX : MaximumDistance + 1;266SmallString<16> Candidate;267SmallString<16> NormalizedName;268269for (const Info &CandidateInfo :270ArrayRef<Info>(OptionInfos).drop_front(FirstSearchableIndex)) {271StringRef CandidateName = CandidateInfo.getName();272273// We can eliminate some option prefix/name pairs as candidates right away:274// * Ignore option candidates with empty names, such as "--", or names275// that do not meet the minimum length.276if (CandidateName.size() < MinimumLength)277continue;278279// Ignore options that are excluded via masks280if (ExcludeOption(CandidateInfo))281continue;282283// * Ignore positional argument option candidates (which do not284// have prefixes).285if (CandidateInfo.Prefixes.empty())286continue;287288// Now check if the candidate ends with a character commonly used when289// delimiting an option from its value, such as '=' or ':'. If it does,290// attempt to split the given option based on that delimiter.291char Last = CandidateName.back();292bool CandidateHasDelimiter = Last == '=' || Last == ':';293StringRef RHS;294if (CandidateHasDelimiter) {295std::tie(NormalizedName, RHS) = Option.split(Last);296if (Option.find(Last) == NormalizedName.size())297NormalizedName += Last;298} else299NormalizedName = Option;300301// Consider each possible prefix for each candidate to find the most302// appropriate one. For example, if a user asks for "--helm", suggest303// "--help" over "-help".304for (auto CandidatePrefix : CandidateInfo.Prefixes) {305// If Candidate and NormalizedName have more than 'BestDistance'306// characters of difference, no need to compute the edit distance, it's307// going to be greater than BestDistance. Don't bother computing Candidate308// at all.309size_t CandidateSize = CandidatePrefix.size() + CandidateName.size(),310NormalizedSize = NormalizedName.size();311size_t AbsDiff = CandidateSize > NormalizedSize312? CandidateSize - NormalizedSize313: NormalizedSize - CandidateSize;314if (AbsDiff > BestDistance) {315continue;316}317Candidate = CandidatePrefix;318Candidate += CandidateName;319unsigned Distance = StringRef(Candidate).edit_distance(320NormalizedName, /*AllowReplacements=*/true,321/*MaxEditDistance=*/BestDistance);322if (RHS.empty() && CandidateHasDelimiter) {323// The Candidate ends with a = or : delimiter, but the option passed in324// didn't contain the delimiter (or doesn't have anything after it).325// In that case, penalize the correction: `-nodefaultlibs` is more326// likely to be a spello for `-nodefaultlib` than `-nodefaultlib:` even327// though both have an unmodified editing distance of 1, since the328// latter would need an argument.329++Distance;330}331if (Distance < BestDistance) {332BestDistance = Distance;333NearestString = (Candidate + RHS).str();334}335}336}337return BestDistance;338}339340// Parse a single argument, return the new argument, and update Index. If341// GroupedShortOptions is true, -a matches "-abc" and the argument in Args will342// be updated to "-bc". This overload does not support VisibilityMask or case343// insensitive options.344std::unique_ptr<Arg> OptTable::parseOneArgGrouped(InputArgList &Args,345unsigned &Index) const {346// Anything that doesn't start with PrefixesUnion is an input, as is '-'347// itself.348const char *CStr = Args.getArgString(Index);349StringRef Str(CStr);350if (isInput(getPrefixesUnion(), Str))351return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, CStr);352353const Info *End = OptionInfos.data() + OptionInfos.size();354StringRef Name = Str.ltrim(PrefixChars);355const Info *Start =356std::lower_bound(OptionInfos.data() + FirstSearchableIndex, End, Name);357const Info *Fallback = nullptr;358unsigned Prev = Index;359360// Search for the option which matches Str.361for (; Start != End; ++Start) {362unsigned ArgSize = matchOption(Start, Str, IgnoreCase);363if (!ArgSize)364continue;365366Option Opt(Start, this);367if (std::unique_ptr<Arg> A =368Opt.accept(Args, StringRef(Args.getArgString(Index), ArgSize),369/*GroupedShortOption=*/false, Index))370return A;371372// If Opt is a Flag of length 2 (e.g. "-a"), we know it is a prefix of373// the current argument (e.g. "-abc"). Match it as a fallback if no longer374// option (e.g. "-ab") exists.375if (ArgSize == 2 && Opt.getKind() == Option::FlagClass)376Fallback = Start;377378// Otherwise, see if the argument is missing.379if (Prev != Index)380return nullptr;381}382if (Fallback) {383Option Opt(Fallback, this);384// Check that the last option isn't a flag wrongly given an argument.385if (Str[2] == '=')386return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++,387CStr);388389if (std::unique_ptr<Arg> A = Opt.accept(390Args, Str.substr(0, 2), /*GroupedShortOption=*/true, Index)) {391Args.replaceArgString(Index, Twine('-') + Str.substr(2));392return A;393}394}395396// In the case of an incorrect short option extract the character and move to397// the next one.398if (Str[1] != '-') {399CStr = Args.MakeArgString(Str.substr(0, 2));400Args.replaceArgString(Index, Twine('-') + Str.substr(2));401return std::make_unique<Arg>(getOption(UnknownOptionID), CStr, Index, CStr);402}403404return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++, CStr);405}406407std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index,408Visibility VisibilityMask) const {409return internalParseOneArg(Args, Index, [VisibilityMask](const Option &Opt) {410return !Opt.hasVisibilityFlag(VisibilityMask);411});412}413414std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index,415unsigned FlagsToInclude,416unsigned FlagsToExclude) const {417return internalParseOneArg(418Args, Index, [FlagsToInclude, FlagsToExclude](const Option &Opt) {419if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude))420return true;421if (Opt.hasFlag(FlagsToExclude))422return true;423return false;424});425}426427std::unique_ptr<Arg> OptTable::internalParseOneArg(428const ArgList &Args, unsigned &Index,429std::function<bool(const Option &)> ExcludeOption) const {430unsigned Prev = Index;431StringRef Str = Args.getArgString(Index);432433// Anything that doesn't start with PrefixesUnion is an input, as is '-'434// itself.435if (isInput(getPrefixesUnion(), Str))436return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++,437Str.data());438439const Info *Start = OptionInfos.data() + FirstSearchableIndex;440const Info *End = OptionInfos.data() + OptionInfos.size();441StringRef Name = Str.ltrim(PrefixChars);442443// Search for the first next option which could be a prefix.444Start = std::lower_bound(Start, End, Name);445446// Options are stored in sorted order, with '\0' at the end of the447// alphabet. Since the only options which can accept a string must448// prefix it, we iteratively search for the next option which could449// be a prefix.450//451// FIXME: This is searching much more than necessary, but I am452// blanking on the simplest way to make it fast. We can solve this453// problem when we move to TableGen.454for (; Start != End; ++Start) {455unsigned ArgSize = 0;456// Scan for first option which is a proper prefix.457for (; Start != End; ++Start)458if ((ArgSize = matchOption(Start, Str, IgnoreCase)))459break;460if (Start == End)461break;462463Option Opt(Start, this);464465if (ExcludeOption(Opt))466continue;467468// See if this option matches.469if (std::unique_ptr<Arg> A =470Opt.accept(Args, StringRef(Args.getArgString(Index), ArgSize),471/*GroupedShortOption=*/false, Index))472return A;473474// Otherwise, see if this argument was missing values.475if (Prev != Index)476return nullptr;477}478479// If we failed to find an option and this arg started with /, then it's480// probably an input path.481if (Str[0] == '/')482return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++,483Str.data());484485return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++,486Str.data());487}488489InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args,490unsigned &MissingArgIndex,491unsigned &MissingArgCount,492Visibility VisibilityMask) const {493return internalParseArgs(494Args, MissingArgIndex, MissingArgCount,495[VisibilityMask](const Option &Opt) {496return !Opt.hasVisibilityFlag(VisibilityMask);497});498}499500InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args,501unsigned &MissingArgIndex,502unsigned &MissingArgCount,503unsigned FlagsToInclude,504unsigned FlagsToExclude) const {505return internalParseArgs(506Args, MissingArgIndex, MissingArgCount,507[FlagsToInclude, FlagsToExclude](const Option &Opt) {508if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude))509return true;510if (Opt.hasFlag(FlagsToExclude))511return true;512return false;513});514}515516InputArgList OptTable::internalParseArgs(517ArrayRef<const char *> ArgArr, unsigned &MissingArgIndex,518unsigned &MissingArgCount,519std::function<bool(const Option &)> ExcludeOption) const {520InputArgList Args(ArgArr.begin(), ArgArr.end());521522// FIXME: Handle '@' args (or at least error on them).523524MissingArgIndex = MissingArgCount = 0;525unsigned Index = 0, End = ArgArr.size();526while (Index < End) {527// Ingore nullptrs, they are response file's EOL markers528if (Args.getArgString(Index) == nullptr) {529++Index;530continue;531}532// Ignore empty arguments (other things may still take them as arguments).533StringRef Str = Args.getArgString(Index);534if (Str == "") {535++Index;536continue;537}538539// In DashDashParsing mode, the first "--" stops option scanning and treats540// all subsequent arguments as positional.541if (DashDashParsing && Str == "--") {542while (++Index < End) {543Args.append(new Arg(getOption(InputOptionID), Str, Index,544Args.getArgString(Index)));545}546break;547}548549unsigned Prev = Index;550std::unique_ptr<Arg> A = GroupedShortOptions551? parseOneArgGrouped(Args, Index)552: internalParseOneArg(Args, Index, ExcludeOption);553assert((Index > Prev || GroupedShortOptions) &&554"Parser failed to consume argument.");555556// Check for missing argument error.557if (!A) {558assert(Index >= End && "Unexpected parser error.");559assert(Index - Prev - 1 && "No missing arguments!");560MissingArgIndex = Prev;561MissingArgCount = Index - Prev - 1;562break;563}564565Args.append(A.release());566}567568return Args;569}570571InputArgList OptTable::parseArgs(int Argc, char *const *Argv,572OptSpecifier Unknown, StringSaver &Saver,573std::function<void(StringRef)> ErrorFn) const {574SmallVector<const char *, 0> NewArgv;575// The environment variable specifies initial options which can be overridden576// by commnad line options.577cl::expandResponseFiles(Argc, Argv, EnvVar, Saver, NewArgv);578579unsigned MAI, MAC;580opt::InputArgList Args = ParseArgs(ArrayRef(NewArgv), MAI, MAC);581if (MAC)582ErrorFn((Twine(Args.getArgString(MAI)) + ": missing argument").str());583584// For each unknwon option, call ErrorFn with a formatted error message. The585// message includes a suggested alternative option spelling if available.586std::string Nearest;587for (const opt::Arg *A : Args.filtered(Unknown)) {588std::string Spelling = A->getAsString(Args);589if (findNearest(Spelling, Nearest) > 1)590ErrorFn("unknown argument '" + Spelling + "'");591else592ErrorFn("unknown argument '" + Spelling + "', did you mean '" + Nearest +593"'?");594}595return Args;596}597598static std::string getOptionHelpName(const OptTable &Opts, OptSpecifier Id) {599const Option O = Opts.getOption(Id);600std::string Name = O.getPrefixedName().str();601602// Add metavar, if used.603switch (O.getKind()) {604case Option::GroupClass: case Option::InputClass: case Option::UnknownClass:605llvm_unreachable("Invalid option with help text.");606607case Option::MultiArgClass:608if (const char *MetaVarName = Opts.getOptionMetaVar(Id)) {609// For MultiArgs, metavar is full list of all argument names.610Name += ' ';611Name += MetaVarName;612}613else {614// For MultiArgs<N>, if metavar not supplied, print <value> N times.615for (unsigned i=0, e=O.getNumArgs(); i< e; ++i) {616Name += " <value>";617}618}619break;620621case Option::FlagClass:622break;623624case Option::ValuesClass:625break;626627case Option::SeparateClass: case Option::JoinedOrSeparateClass:628case Option::RemainingArgsClass: case Option::RemainingArgsJoinedClass:629Name += ' ';630[[fallthrough]];631case Option::JoinedClass: case Option::CommaJoinedClass:632case Option::JoinedAndSeparateClass:633if (const char *MetaVarName = Opts.getOptionMetaVar(Id))634Name += MetaVarName;635else636Name += "<value>";637break;638}639640return Name;641}642643namespace {644struct OptionInfo {645std::string Name;646StringRef HelpText;647};648} // namespace649650static void PrintHelpOptionList(raw_ostream &OS, StringRef Title,651std::vector<OptionInfo> &OptionHelp) {652OS << Title << ":\n";653654// Find the maximum option length.655unsigned OptionFieldWidth = 0;656for (const OptionInfo &Opt : OptionHelp) {657// Limit the amount of padding we are willing to give up for alignment.658unsigned Length = Opt.Name.size();659if (Length <= 23)660OptionFieldWidth = std::max(OptionFieldWidth, Length);661}662663const unsigned InitialPad = 2;664for (const OptionInfo &Opt : OptionHelp) {665const std::string &Option = Opt.Name;666int Pad = OptionFieldWidth + InitialPad;667int FirstLinePad = OptionFieldWidth - int(Option.size());668OS.indent(InitialPad) << Option;669670// Break on long option names.671if (FirstLinePad < 0) {672OS << "\n";673FirstLinePad = OptionFieldWidth + InitialPad;674Pad = FirstLinePad;675}676677SmallVector<StringRef> Lines;678Opt.HelpText.split(Lines, '\n');679assert(Lines.size() && "Expected at least the first line in the help text");680auto *LinesIt = Lines.begin();681OS.indent(FirstLinePad + 1) << *LinesIt << '\n';682while (Lines.end() != ++LinesIt)683OS.indent(Pad + 1) << *LinesIt << '\n';684}685}686687static const char *getOptionHelpGroup(const OptTable &Opts, OptSpecifier Id) {688unsigned GroupID = Opts.getOptionGroupID(Id);689690// If not in a group, return the default help group.691if (!GroupID)692return "OPTIONS";693694// Abuse the help text of the option groups to store the "help group"695// name.696//697// FIXME: Split out option groups.698if (const char *GroupHelp = Opts.getOptionHelpText(GroupID))699return GroupHelp;700701// Otherwise keep looking.702return getOptionHelpGroup(Opts, GroupID);703}704705void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title,706bool ShowHidden, bool ShowAllAliases,707Visibility VisibilityMask) const {708return internalPrintHelp(709OS, Usage, Title, ShowHidden, ShowAllAliases,710[VisibilityMask](const Info &CandidateInfo) -> bool {711return (CandidateInfo.Visibility & VisibilityMask) == 0;712},713VisibilityMask);714}715716void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title,717unsigned FlagsToInclude, unsigned FlagsToExclude,718bool ShowAllAliases) const {719bool ShowHidden = !(FlagsToExclude & HelpHidden);720FlagsToExclude &= ~HelpHidden;721return internalPrintHelp(722OS, Usage, Title, ShowHidden, ShowAllAliases,723[FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) {724if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude))725return true;726if (CandidateInfo.Flags & FlagsToExclude)727return true;728return false;729},730Visibility(0));731}732733void OptTable::internalPrintHelp(734raw_ostream &OS, const char *Usage, const char *Title, bool ShowHidden,735bool ShowAllAliases, std::function<bool(const Info &)> ExcludeOption,736Visibility VisibilityMask) const {737OS << "OVERVIEW: " << Title << "\n\n";738OS << "USAGE: " << Usage << "\n\n";739740// Render help text into a map of group-name to a list of (option, help)741// pairs.742std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp;743744for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) {745// FIXME: Split out option groups.746if (getOptionKind(Id) == Option::GroupClass)747continue;748749const Info &CandidateInfo = getInfo(Id);750if (!ShowHidden && (CandidateInfo.Flags & opt::HelpHidden))751continue;752753if (ExcludeOption(CandidateInfo))754continue;755756// If an alias doesn't have a help text, show a help text for the aliased757// option instead.758const char *HelpText = getOptionHelpText(Id, VisibilityMask);759if (!HelpText && ShowAllAliases) {760const Option Alias = getOption(Id).getAlias();761if (Alias.isValid())762HelpText = getOptionHelpText(Alias.getID(), VisibilityMask);763}764765if (HelpText && (strlen(HelpText) != 0)) {766const char *HelpGroup = getOptionHelpGroup(*this, Id);767const std::string &OptName = getOptionHelpName(*this, Id);768GroupedOptionHelp[HelpGroup].push_back({OptName, HelpText});769}770}771772for (auto& OptionGroup : GroupedOptionHelp) {773if (OptionGroup.first != GroupedOptionHelp.begin()->first)774OS << "\n";775PrintHelpOptionList(OS, OptionGroup.first, OptionGroup.second);776}777778OS.flush();779}780781GenericOptTable::GenericOptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase)782: OptTable(OptionInfos, IgnoreCase) {783784std::set<StringLiteral> TmpPrefixesUnion;785for (auto const &Info : OptionInfos.drop_front(FirstSearchableIndex))786TmpPrefixesUnion.insert(Info.Prefixes.begin(), Info.Prefixes.end());787PrefixesUnionBuffer.append(TmpPrefixesUnion.begin(), TmpPrefixesUnion.end());788buildPrefixChars();789}790791792