10b57cec5SDimitry Andric //===- OptTable.cpp - Option Table Implementation -------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 9e8d8bef9SDimitry Andric #include "llvm/Option/OptTable.h" 100b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 110b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 120b57cec5SDimitry Andric #include "llvm/Option/Arg.h" 130b57cec5SDimitry Andric #include "llvm/Option/ArgList.h" 140b57cec5SDimitry Andric #include "llvm/Option/OptSpecifier.h" 15e8d8bef9SDimitry Andric #include "llvm/Option/Option.h" 16e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h" // for expandResponseFiles 170b57cec5SDimitry Andric #include "llvm/Support/Compiler.h" 180b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 190b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 200b57cec5SDimitry Andric #include <algorithm> 210b57cec5SDimitry Andric #include <cassert> 220b57cec5SDimitry Andric #include <cctype> 230b57cec5SDimitry Andric #include <cstring> 240b57cec5SDimitry Andric #include <map> 25bdd1243dSDimitry Andric #include <set> 260b57cec5SDimitry Andric #include <string> 270b57cec5SDimitry Andric #include <utility> 280b57cec5SDimitry Andric #include <vector> 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric using namespace llvm; 310b57cec5SDimitry Andric using namespace llvm::opt; 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric namespace llvm { 340b57cec5SDimitry Andric namespace opt { 350b57cec5SDimitry Andric 360b57cec5SDimitry Andric // Ordering on Info. The ordering is *almost* case-insensitive lexicographic, 370b57cec5SDimitry Andric // with an exception. '\0' comes at the end of the alphabet instead of the 380b57cec5SDimitry Andric // beginning (thus options precede any other options which prefix them). 39bdd1243dSDimitry Andric static int StrCmpOptionNameIgnoreCase(StringRef A, StringRef B) { 40bdd1243dSDimitry Andric size_t MinSize = std::min(A.size(), B.size()); 41bdd1243dSDimitry Andric if (int Res = A.substr(0, MinSize).compare_insensitive(B.substr(0, MinSize))) 42bdd1243dSDimitry Andric return Res; 43bdd1243dSDimitry Andric 44bdd1243dSDimitry Andric if (A.size() == B.size()) 450b57cec5SDimitry Andric return 0; 460b57cec5SDimitry Andric 47bdd1243dSDimitry Andric return (A.size() == MinSize) ? 1 /* A is a prefix of B. */ 48bdd1243dSDimitry Andric : -1 /* B is a prefix of A */; 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric #ifndef NDEBUG 52bdd1243dSDimitry Andric static int StrCmpOptionName(StringRef A, StringRef B) { 530b57cec5SDimitry Andric if (int N = StrCmpOptionNameIgnoreCase(A, B)) 540b57cec5SDimitry Andric return N; 55bdd1243dSDimitry Andric return A.compare(B); 560b57cec5SDimitry Andric } 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) { 590b57cec5SDimitry Andric if (&A == &B) 600b57cec5SDimitry Andric return false; 610b57cec5SDimitry Andric 625f757f3fSDimitry Andric if (int N = StrCmpOptionName(A.getName(), B.getName())) 630b57cec5SDimitry Andric return N < 0; 640b57cec5SDimitry Andric 65bdd1243dSDimitry Andric for (size_t I = 0, K = std::min(A.Prefixes.size(), B.Prefixes.size()); I != K; 66bdd1243dSDimitry Andric ++I) 67bdd1243dSDimitry Andric if (int N = StrCmpOptionName(A.Prefixes[I], B.Prefixes[I])) 680b57cec5SDimitry Andric return N < 0; 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric // Names are the same, check that classes are in order; exactly one 710b57cec5SDimitry Andric // should be joined, and it should succeed the other. 720b57cec5SDimitry Andric assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) && 730b57cec5SDimitry Andric "Unexpected classes for options with same name."); 740b57cec5SDimitry Andric return B.Kind == Option::JoinedClass; 750b57cec5SDimitry Andric } 760b57cec5SDimitry Andric #endif 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric // Support lower_bound between info and an option name. 79bdd1243dSDimitry Andric static inline bool operator<(const OptTable::Info &I, StringRef Name) { 805f757f3fSDimitry Andric return StrCmpOptionNameIgnoreCase(I.getName(), Name) < 0; 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric } // end namespace opt 840b57cec5SDimitry Andric } // end namespace llvm 850b57cec5SDimitry Andric 860b57cec5SDimitry Andric OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {} 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric OptTable::OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase) 890b57cec5SDimitry Andric : OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) { 900b57cec5SDimitry Andric // Explicitly zero initialize the error to work around a bug in array 910b57cec5SDimitry Andric // value-initialization on MinGW with gcc 4.3.5. 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric // Find start of normal options. 940b57cec5SDimitry Andric for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { 950b57cec5SDimitry Andric unsigned Kind = getInfo(i + 1).Kind; 960b57cec5SDimitry Andric if (Kind == Option::InputClass) { 97349cc55cSDimitry Andric assert(!InputOptionID && "Cannot have multiple input options!"); 98349cc55cSDimitry Andric InputOptionID = getInfo(i + 1).ID; 990b57cec5SDimitry Andric } else if (Kind == Option::UnknownClass) { 100349cc55cSDimitry Andric assert(!UnknownOptionID && "Cannot have multiple unknown options!"); 101349cc55cSDimitry Andric UnknownOptionID = getInfo(i + 1).ID; 1020b57cec5SDimitry Andric } else if (Kind != Option::GroupClass) { 1030b57cec5SDimitry Andric FirstSearchableIndex = i; 1040b57cec5SDimitry Andric break; 1050b57cec5SDimitry Andric } 1060b57cec5SDimitry Andric } 1070b57cec5SDimitry Andric assert(FirstSearchableIndex != 0 && "No searchable options?"); 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric #ifndef NDEBUG 1100b57cec5SDimitry Andric // Check that everything after the first searchable option is a 1110b57cec5SDimitry Andric // regular option class. 1120b57cec5SDimitry Andric for (unsigned i = FirstSearchableIndex, e = getNumOptions(); i != e; ++i) { 1130b57cec5SDimitry Andric Option::OptionClass Kind = (Option::OptionClass) getInfo(i + 1).Kind; 1140b57cec5SDimitry Andric assert((Kind != Option::InputClass && Kind != Option::UnknownClass && 1150b57cec5SDimitry Andric Kind != Option::GroupClass) && 1160b57cec5SDimitry Andric "Special options should be defined first!"); 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric // Check that options are in order. 1200b57cec5SDimitry Andric for (unsigned i = FirstSearchableIndex + 1, e = getNumOptions(); i != e; ++i){ 1210b57cec5SDimitry Andric if (!(getInfo(i) < getInfo(i + 1))) { 1220b57cec5SDimitry Andric getOption(i).dump(); 1230b57cec5SDimitry Andric getOption(i + 1).dump(); 1240b57cec5SDimitry Andric llvm_unreachable("Options are not in order!"); 1250b57cec5SDimitry Andric } 1260b57cec5SDimitry Andric } 1270b57cec5SDimitry Andric #endif 128bdd1243dSDimitry Andric } 1290b57cec5SDimitry Andric 130bdd1243dSDimitry Andric void OptTable::buildPrefixChars() { 131bdd1243dSDimitry Andric assert(PrefixChars.empty() && "rebuilding a non-empty prefix char"); 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric // Build prefix chars. 134bdd1243dSDimitry Andric for (const StringLiteral &Prefix : getPrefixesUnion()) { 1354824e7fdSDimitry Andric for (char C : Prefix) 1364824e7fdSDimitry Andric if (!is_contained(PrefixChars, C)) 1374824e7fdSDimitry Andric PrefixChars.push_back(C); 1380b57cec5SDimitry Andric } 1390b57cec5SDimitry Andric } 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric OptTable::~OptTable() = default; 1420b57cec5SDimitry Andric 1430b57cec5SDimitry Andric const Option OptTable::getOption(OptSpecifier Opt) const { 1440b57cec5SDimitry Andric unsigned id = Opt.getID(); 1450b57cec5SDimitry Andric if (id == 0) 1460b57cec5SDimitry Andric return Option(nullptr, nullptr); 1470b57cec5SDimitry Andric assert((unsigned) (id - 1) < getNumOptions() && "Invalid ID."); 1480b57cec5SDimitry Andric return Option(&getInfo(id), this); 1490b57cec5SDimitry Andric } 1500b57cec5SDimitry Andric 151bdd1243dSDimitry Andric static bool isInput(const ArrayRef<StringLiteral> &Prefixes, StringRef Arg) { 1520b57cec5SDimitry Andric if (Arg == "-") 1530b57cec5SDimitry Andric return true; 154bdd1243dSDimitry Andric for (const StringRef &Prefix : Prefixes) 1555f757f3fSDimitry Andric if (Arg.starts_with(Prefix)) 1560b57cec5SDimitry Andric return false; 1570b57cec5SDimitry Andric return true; 1580b57cec5SDimitry Andric } 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric /// \returns Matched size. 0 means no match. 1610b57cec5SDimitry Andric static unsigned matchOption(const OptTable::Info *I, StringRef Str, 1620b57cec5SDimitry Andric bool IgnoreCase) { 163bdd1243dSDimitry Andric for (auto Prefix : I->Prefixes) { 1645f757f3fSDimitry Andric if (Str.starts_with(Prefix)) { 1650b57cec5SDimitry Andric StringRef Rest = Str.substr(Prefix.size()); 1665f757f3fSDimitry Andric bool Matched = IgnoreCase ? Rest.starts_with_insensitive(I->getName()) 1675f757f3fSDimitry Andric : Rest.starts_with(I->getName()); 1680b57cec5SDimitry Andric if (Matched) 1695f757f3fSDimitry Andric return Prefix.size() + StringRef(I->getName()).size(); 1700b57cec5SDimitry Andric } 1710b57cec5SDimitry Andric } 1720b57cec5SDimitry Andric return 0; 1730b57cec5SDimitry Andric } 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric // Returns true if one of the Prefixes + In.Names matches Option 1760b57cec5SDimitry Andric static bool optionMatches(const OptTable::Info &In, StringRef Option) { 177bdd1243dSDimitry Andric for (auto Prefix : In.Prefixes) 1785f757f3fSDimitry Andric if (Option.ends_with(In.getName())) 1795f757f3fSDimitry Andric if (Option.slice(0, Option.size() - In.getName().size()) == Prefix) 1800b57cec5SDimitry Andric return true; 1810b57cec5SDimitry Andric return false; 1820b57cec5SDimitry Andric } 1830b57cec5SDimitry Andric 1840b57cec5SDimitry Andric // This function is for flag value completion. 1850b57cec5SDimitry Andric // Eg. When "-stdlib=" and "l" was passed to this function, it will return 1860b57cec5SDimitry Andric // appropiriate values for stdlib, which starts with l. 1870b57cec5SDimitry Andric std::vector<std::string> 1880b57cec5SDimitry Andric OptTable::suggestValueCompletions(StringRef Option, StringRef Arg) const { 1890b57cec5SDimitry Andric // Search all options and return possible values. 1900b57cec5SDimitry Andric for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) { 1910b57cec5SDimitry Andric const Info &In = OptionInfos[I]; 1920b57cec5SDimitry Andric if (!In.Values || !optionMatches(In, Option)) 1930b57cec5SDimitry Andric continue; 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric SmallVector<StringRef, 8> Candidates; 1960b57cec5SDimitry Andric StringRef(In.Values).split(Candidates, ",", -1, false); 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric std::vector<std::string> Result; 1990b57cec5SDimitry Andric for (StringRef Val : Candidates) 200*0fca6ea1SDimitry Andric if (Val.starts_with(Arg) && Arg != Val) 2015ffd83dbSDimitry Andric Result.push_back(std::string(Val)); 2020b57cec5SDimitry Andric return Result; 2030b57cec5SDimitry Andric } 2040b57cec5SDimitry Andric return {}; 2050b57cec5SDimitry Andric } 2060b57cec5SDimitry Andric 2070b57cec5SDimitry Andric std::vector<std::string> 2085f757f3fSDimitry Andric OptTable::findByPrefix(StringRef Cur, Visibility VisibilityMask, 2095f757f3fSDimitry Andric unsigned int DisableFlags) const { 2100b57cec5SDimitry Andric std::vector<std::string> Ret; 2110b57cec5SDimitry Andric for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) { 2120b57cec5SDimitry Andric const Info &In = OptionInfos[I]; 213bdd1243dSDimitry Andric if (In.Prefixes.empty() || (!In.HelpText && !In.GroupID)) 2140b57cec5SDimitry Andric continue; 2155f757f3fSDimitry Andric if (!(In.Visibility & VisibilityMask)) 2165f757f3fSDimitry Andric continue; 2170b57cec5SDimitry Andric if (In.Flags & DisableFlags) 2180b57cec5SDimitry Andric continue; 2190b57cec5SDimitry Andric 220bdd1243dSDimitry Andric for (auto Prefix : In.Prefixes) { 2215f757f3fSDimitry Andric std::string S = (Prefix + In.getName() + "\t").str(); 2220b57cec5SDimitry Andric if (In.HelpText) 2230b57cec5SDimitry Andric S += In.HelpText; 2245f757f3fSDimitry Andric if (StringRef(S).starts_with(Cur) && S != std::string(Cur) + "\t") 2250b57cec5SDimitry Andric Ret.push_back(S); 2260b57cec5SDimitry Andric } 2270b57cec5SDimitry Andric } 2280b57cec5SDimitry Andric return Ret; 2290b57cec5SDimitry Andric } 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric unsigned OptTable::findNearest(StringRef Option, std::string &NearestString, 2325f757f3fSDimitry Andric Visibility VisibilityMask, 2335f757f3fSDimitry Andric unsigned MinimumLength, 2345f757f3fSDimitry Andric unsigned MaximumDistance) const { 2355f757f3fSDimitry Andric return internalFindNearest( 2365f757f3fSDimitry Andric Option, NearestString, MinimumLength, MaximumDistance, 2375f757f3fSDimitry Andric [VisibilityMask](const Info &CandidateInfo) { 2385f757f3fSDimitry Andric return (CandidateInfo.Visibility & VisibilityMask) == 0; 2395f757f3fSDimitry Andric }); 2405f757f3fSDimitry Andric } 2415f757f3fSDimitry Andric 2425f757f3fSDimitry Andric unsigned OptTable::findNearest(StringRef Option, std::string &NearestString, 2430b57cec5SDimitry Andric unsigned FlagsToInclude, unsigned FlagsToExclude, 244bdd1243dSDimitry Andric unsigned MinimumLength, 245bdd1243dSDimitry Andric unsigned MaximumDistance) const { 2465f757f3fSDimitry Andric return internalFindNearest( 2475f757f3fSDimitry Andric Option, NearestString, MinimumLength, MaximumDistance, 2485f757f3fSDimitry Andric [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) { 2495f757f3fSDimitry Andric if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude)) 2505f757f3fSDimitry Andric return true; 2515f757f3fSDimitry Andric if (CandidateInfo.Flags & FlagsToExclude) 2525f757f3fSDimitry Andric return true; 2535f757f3fSDimitry Andric return false; 2545f757f3fSDimitry Andric }); 2555f757f3fSDimitry Andric } 2565f757f3fSDimitry Andric 2575f757f3fSDimitry Andric unsigned OptTable::internalFindNearest( 2585f757f3fSDimitry Andric StringRef Option, std::string &NearestString, unsigned MinimumLength, 2595f757f3fSDimitry Andric unsigned MaximumDistance, 2605f757f3fSDimitry Andric std::function<bool(const Info &)> ExcludeOption) const { 2610b57cec5SDimitry Andric assert(!Option.empty()); 2620b57cec5SDimitry Andric 2630b57cec5SDimitry Andric // Consider each [option prefix + option name] pair as a candidate, finding 2640b57cec5SDimitry Andric // the closest match. 265bdd1243dSDimitry Andric unsigned BestDistance = 266bdd1243dSDimitry Andric MaximumDistance == UINT_MAX ? UINT_MAX : MaximumDistance + 1; 267bdd1243dSDimitry Andric SmallString<16> Candidate; 268bdd1243dSDimitry Andric SmallString<16> NormalizedName; 269bdd1243dSDimitry Andric 2700b57cec5SDimitry Andric for (const Info &CandidateInfo : 2710b57cec5SDimitry Andric ArrayRef<Info>(OptionInfos).drop_front(FirstSearchableIndex)) { 2725f757f3fSDimitry Andric StringRef CandidateName = CandidateInfo.getName(); 2730b57cec5SDimitry Andric 2740b57cec5SDimitry Andric // We can eliminate some option prefix/name pairs as candidates right away: 2750b57cec5SDimitry Andric // * Ignore option candidates with empty names, such as "--", or names 2760b57cec5SDimitry Andric // that do not meet the minimum length. 277bdd1243dSDimitry Andric if (CandidateName.size() < MinimumLength) 2780b57cec5SDimitry Andric continue; 2790b57cec5SDimitry Andric 2805f757f3fSDimitry Andric // Ignore options that are excluded via masks 2815f757f3fSDimitry Andric if (ExcludeOption(CandidateInfo)) 2820b57cec5SDimitry Andric continue; 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric // * Ignore positional argument option candidates (which do not 2850b57cec5SDimitry Andric // have prefixes). 286bdd1243dSDimitry Andric if (CandidateInfo.Prefixes.empty()) 2870b57cec5SDimitry Andric continue; 2880b57cec5SDimitry Andric 2890b57cec5SDimitry Andric // Now check if the candidate ends with a character commonly used when 2900b57cec5SDimitry Andric // delimiting an option from its value, such as '=' or ':'. If it does, 2910b57cec5SDimitry Andric // attempt to split the given option based on that delimiter. 2920b57cec5SDimitry Andric char Last = CandidateName.back(); 2930b57cec5SDimitry Andric bool CandidateHasDelimiter = Last == '=' || Last == ':'; 294bdd1243dSDimitry Andric StringRef RHS; 2950b57cec5SDimitry Andric if (CandidateHasDelimiter) { 296bdd1243dSDimitry Andric std::tie(NormalizedName, RHS) = Option.split(Last); 297bdd1243dSDimitry Andric if (Option.find(Last) == NormalizedName.size()) 2980b57cec5SDimitry Andric NormalizedName += Last; 299bdd1243dSDimitry Andric } else 300bdd1243dSDimitry Andric NormalizedName = Option; 3010b57cec5SDimitry Andric 3020b57cec5SDimitry Andric // Consider each possible prefix for each candidate to find the most 3030b57cec5SDimitry Andric // appropriate one. For example, if a user asks for "--helm", suggest 3040b57cec5SDimitry Andric // "--help" over "-help". 305bdd1243dSDimitry Andric for (auto CandidatePrefix : CandidateInfo.Prefixes) { 306bdd1243dSDimitry Andric // If Candidate and NormalizedName have more than 'BestDistance' 307bdd1243dSDimitry Andric // characters of difference, no need to compute the edit distance, it's 308bdd1243dSDimitry Andric // going to be greater than BestDistance. Don't bother computing Candidate 309bdd1243dSDimitry Andric // at all. 310bdd1243dSDimitry Andric size_t CandidateSize = CandidatePrefix.size() + CandidateName.size(), 311bdd1243dSDimitry Andric NormalizedSize = NormalizedName.size(); 312bdd1243dSDimitry Andric size_t AbsDiff = CandidateSize > NormalizedSize 313bdd1243dSDimitry Andric ? CandidateSize - NormalizedSize 314bdd1243dSDimitry Andric : NormalizedSize - CandidateSize; 315bdd1243dSDimitry Andric if (AbsDiff > BestDistance) { 316bdd1243dSDimitry Andric continue; 317bdd1243dSDimitry Andric } 318bdd1243dSDimitry Andric Candidate = CandidatePrefix; 319bdd1243dSDimitry Andric Candidate += CandidateName; 320bdd1243dSDimitry Andric unsigned Distance = StringRef(Candidate).edit_distance( 321bdd1243dSDimitry Andric NormalizedName, /*AllowReplacements=*/true, 3220b57cec5SDimitry Andric /*MaxEditDistance=*/BestDistance); 3230b57cec5SDimitry Andric if (RHS.empty() && CandidateHasDelimiter) { 3240b57cec5SDimitry Andric // The Candidate ends with a = or : delimiter, but the option passed in 3250b57cec5SDimitry Andric // didn't contain the delimiter (or doesn't have anything after it). 3260b57cec5SDimitry Andric // In that case, penalize the correction: `-nodefaultlibs` is more 3270b57cec5SDimitry Andric // likely to be a spello for `-nodefaultlib` than `-nodefaultlib:` even 3280b57cec5SDimitry Andric // though both have an unmodified editing distance of 1, since the 3290b57cec5SDimitry Andric // latter would need an argument. 3300b57cec5SDimitry Andric ++Distance; 3310b57cec5SDimitry Andric } 3320b57cec5SDimitry Andric if (Distance < BestDistance) { 3330b57cec5SDimitry Andric BestDistance = Distance; 3340b57cec5SDimitry Andric NearestString = (Candidate + RHS).str(); 3350b57cec5SDimitry Andric } 3360b57cec5SDimitry Andric } 3370b57cec5SDimitry Andric } 3380b57cec5SDimitry Andric return BestDistance; 3390b57cec5SDimitry Andric } 3400b57cec5SDimitry Andric 341e8d8bef9SDimitry Andric // Parse a single argument, return the new argument, and update Index. If 342e8d8bef9SDimitry Andric // GroupedShortOptions is true, -a matches "-abc" and the argument in Args will 3435f757f3fSDimitry Andric // be updated to "-bc". This overload does not support VisibilityMask or case 3445f757f3fSDimitry Andric // insensitive options. 345349cc55cSDimitry Andric std::unique_ptr<Arg> OptTable::parseOneArgGrouped(InputArgList &Args, 346349cc55cSDimitry Andric unsigned &Index) const { 347e8d8bef9SDimitry Andric // Anything that doesn't start with PrefixesUnion is an input, as is '-' 348e8d8bef9SDimitry Andric // itself. 349e8d8bef9SDimitry Andric const char *CStr = Args.getArgString(Index); 350e8d8bef9SDimitry Andric StringRef Str(CStr); 351bdd1243dSDimitry Andric if (isInput(getPrefixesUnion(), Str)) 352349cc55cSDimitry Andric return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, CStr); 353e8d8bef9SDimitry Andric 354e8d8bef9SDimitry Andric const Info *End = OptionInfos.data() + OptionInfos.size(); 355e8d8bef9SDimitry Andric StringRef Name = Str.ltrim(PrefixChars); 356bdd1243dSDimitry Andric const Info *Start = 357bdd1243dSDimitry Andric std::lower_bound(OptionInfos.data() + FirstSearchableIndex, End, Name); 358e8d8bef9SDimitry Andric const Info *Fallback = nullptr; 359e8d8bef9SDimitry Andric unsigned Prev = Index; 360e8d8bef9SDimitry Andric 361e8d8bef9SDimitry Andric // Search for the option which matches Str. 362e8d8bef9SDimitry Andric for (; Start != End; ++Start) { 363e8d8bef9SDimitry Andric unsigned ArgSize = matchOption(Start, Str, IgnoreCase); 364e8d8bef9SDimitry Andric if (!ArgSize) 365e8d8bef9SDimitry Andric continue; 366e8d8bef9SDimitry Andric 367e8d8bef9SDimitry Andric Option Opt(Start, this); 368349cc55cSDimitry Andric if (std::unique_ptr<Arg> A = 369349cc55cSDimitry Andric Opt.accept(Args, StringRef(Args.getArgString(Index), ArgSize), 370349cc55cSDimitry Andric /*GroupedShortOption=*/false, Index)) 371e8d8bef9SDimitry Andric return A; 372e8d8bef9SDimitry Andric 373e8d8bef9SDimitry Andric // If Opt is a Flag of length 2 (e.g. "-a"), we know it is a prefix of 374e8d8bef9SDimitry Andric // the current argument (e.g. "-abc"). Match it as a fallback if no longer 375e8d8bef9SDimitry Andric // option (e.g. "-ab") exists. 376e8d8bef9SDimitry Andric if (ArgSize == 2 && Opt.getKind() == Option::FlagClass) 377e8d8bef9SDimitry Andric Fallback = Start; 378e8d8bef9SDimitry Andric 379e8d8bef9SDimitry Andric // Otherwise, see if the argument is missing. 380e8d8bef9SDimitry Andric if (Prev != Index) 381e8d8bef9SDimitry Andric return nullptr; 382e8d8bef9SDimitry Andric } 383e8d8bef9SDimitry Andric if (Fallback) { 384e8d8bef9SDimitry Andric Option Opt(Fallback, this); 385349cc55cSDimitry Andric // Check that the last option isn't a flag wrongly given an argument. 386349cc55cSDimitry Andric if (Str[2] == '=') 387349cc55cSDimitry Andric return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++, 388349cc55cSDimitry Andric CStr); 389349cc55cSDimitry Andric 390349cc55cSDimitry Andric if (std::unique_ptr<Arg> A = Opt.accept( 391349cc55cSDimitry Andric Args, Str.substr(0, 2), /*GroupedShortOption=*/true, Index)) { 392e8d8bef9SDimitry Andric Args.replaceArgString(Index, Twine('-') + Str.substr(2)); 393e8d8bef9SDimitry Andric return A; 394e8d8bef9SDimitry Andric } 395e8d8bef9SDimitry Andric } 396e8d8bef9SDimitry Andric 397349cc55cSDimitry Andric // In the case of an incorrect short option extract the character and move to 398349cc55cSDimitry Andric // the next one. 399349cc55cSDimitry Andric if (Str[1] != '-') { 400349cc55cSDimitry Andric CStr = Args.MakeArgString(Str.substr(0, 2)); 401349cc55cSDimitry Andric Args.replaceArgString(Index, Twine('-') + Str.substr(2)); 402349cc55cSDimitry Andric return std::make_unique<Arg>(getOption(UnknownOptionID), CStr, Index, CStr); 403e8d8bef9SDimitry Andric } 404e8d8bef9SDimitry Andric 405349cc55cSDimitry Andric return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++, CStr); 406349cc55cSDimitry Andric } 407349cc55cSDimitry Andric 408349cc55cSDimitry Andric std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index, 4095f757f3fSDimitry Andric Visibility VisibilityMask) const { 4105f757f3fSDimitry Andric return internalParseOneArg(Args, Index, [VisibilityMask](const Option &Opt) { 4115f757f3fSDimitry Andric return !Opt.hasVisibilityFlag(VisibilityMask); 4125f757f3fSDimitry Andric }); 4135f757f3fSDimitry Andric } 4145f757f3fSDimitry Andric 4155f757f3fSDimitry Andric std::unique_ptr<Arg> OptTable::ParseOneArg(const ArgList &Args, unsigned &Index, 4160b57cec5SDimitry Andric unsigned FlagsToInclude, 4170b57cec5SDimitry Andric unsigned FlagsToExclude) const { 4185f757f3fSDimitry Andric return internalParseOneArg( 4195f757f3fSDimitry Andric Args, Index, [FlagsToInclude, FlagsToExclude](const Option &Opt) { 4205f757f3fSDimitry Andric if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude)) 4215f757f3fSDimitry Andric return true; 4225f757f3fSDimitry Andric if (Opt.hasFlag(FlagsToExclude)) 4235f757f3fSDimitry Andric return true; 4245f757f3fSDimitry Andric return false; 4255f757f3fSDimitry Andric }); 4265f757f3fSDimitry Andric } 4275f757f3fSDimitry Andric 4285f757f3fSDimitry Andric std::unique_ptr<Arg> OptTable::internalParseOneArg( 4295f757f3fSDimitry Andric const ArgList &Args, unsigned &Index, 4305f757f3fSDimitry Andric std::function<bool(const Option &)> ExcludeOption) const { 4310b57cec5SDimitry Andric unsigned Prev = Index; 432bdd1243dSDimitry Andric StringRef Str = Args.getArgString(Index); 4330b57cec5SDimitry Andric 4340b57cec5SDimitry Andric // Anything that doesn't start with PrefixesUnion is an input, as is '-' 4350b57cec5SDimitry Andric // itself. 436bdd1243dSDimitry Andric if (isInput(getPrefixesUnion(), Str)) 437bdd1243dSDimitry Andric return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, 438bdd1243dSDimitry Andric Str.data()); 4390b57cec5SDimitry Andric 4400b57cec5SDimitry Andric const Info *Start = OptionInfos.data() + FirstSearchableIndex; 4410b57cec5SDimitry Andric const Info *End = OptionInfos.data() + OptionInfos.size(); 442bdd1243dSDimitry Andric StringRef Name = Str.ltrim(PrefixChars); 4430b57cec5SDimitry Andric 4440b57cec5SDimitry Andric // Search for the first next option which could be a prefix. 445bdd1243dSDimitry Andric Start = std::lower_bound(Start, End, Name); 4460b57cec5SDimitry Andric 4470b57cec5SDimitry Andric // Options are stored in sorted order, with '\0' at the end of the 4480b57cec5SDimitry Andric // alphabet. Since the only options which can accept a string must 4490b57cec5SDimitry Andric // prefix it, we iteratively search for the next option which could 4500b57cec5SDimitry Andric // be a prefix. 4510b57cec5SDimitry Andric // 4520b57cec5SDimitry Andric // FIXME: This is searching much more than necessary, but I am 4530b57cec5SDimitry Andric // blanking on the simplest way to make it fast. We can solve this 4540b57cec5SDimitry Andric // problem when we move to TableGen. 4550b57cec5SDimitry Andric for (; Start != End; ++Start) { 4560b57cec5SDimitry Andric unsigned ArgSize = 0; 4570b57cec5SDimitry Andric // Scan for first option which is a proper prefix. 4580b57cec5SDimitry Andric for (; Start != End; ++Start) 4590b57cec5SDimitry Andric if ((ArgSize = matchOption(Start, Str, IgnoreCase))) 4600b57cec5SDimitry Andric break; 4610b57cec5SDimitry Andric if (Start == End) 4620b57cec5SDimitry Andric break; 4630b57cec5SDimitry Andric 4640b57cec5SDimitry Andric Option Opt(Start, this); 4650b57cec5SDimitry Andric 4665f757f3fSDimitry Andric if (ExcludeOption(Opt)) 4670b57cec5SDimitry Andric continue; 4680b57cec5SDimitry Andric 4690b57cec5SDimitry Andric // See if this option matches. 470349cc55cSDimitry Andric if (std::unique_ptr<Arg> A = 471349cc55cSDimitry Andric Opt.accept(Args, StringRef(Args.getArgString(Index), ArgSize), 472349cc55cSDimitry Andric /*GroupedShortOption=*/false, Index)) 4730b57cec5SDimitry Andric return A; 4740b57cec5SDimitry Andric 4750b57cec5SDimitry Andric // Otherwise, see if this argument was missing values. 4760b57cec5SDimitry Andric if (Prev != Index) 4770b57cec5SDimitry Andric return nullptr; 4780b57cec5SDimitry Andric } 4790b57cec5SDimitry Andric 4800b57cec5SDimitry Andric // If we failed to find an option and this arg started with /, then it's 4810b57cec5SDimitry Andric // probably an input path. 4820b57cec5SDimitry Andric if (Str[0] == '/') 483bdd1243dSDimitry Andric return std::make_unique<Arg>(getOption(InputOptionID), Str, Index++, 484bdd1243dSDimitry Andric Str.data()); 4850b57cec5SDimitry Andric 486bdd1243dSDimitry Andric return std::make_unique<Arg>(getOption(UnknownOptionID), Str, Index++, 487bdd1243dSDimitry Andric Str.data()); 4880b57cec5SDimitry Andric } 4890b57cec5SDimitry Andric 4905f757f3fSDimitry Andric InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args, 4915f757f3fSDimitry Andric unsigned &MissingArgIndex, 4925f757f3fSDimitry Andric unsigned &MissingArgCount, 4935f757f3fSDimitry Andric Visibility VisibilityMask) const { 4945f757f3fSDimitry Andric return internalParseArgs( 4955f757f3fSDimitry Andric Args, MissingArgIndex, MissingArgCount, 4965f757f3fSDimitry Andric [VisibilityMask](const Option &Opt) { 4975f757f3fSDimitry Andric return !Opt.hasVisibilityFlag(VisibilityMask); 4985f757f3fSDimitry Andric }); 4995f757f3fSDimitry Andric } 5005f757f3fSDimitry Andric 5015f757f3fSDimitry Andric InputArgList OptTable::ParseArgs(ArrayRef<const char *> Args, 5020b57cec5SDimitry Andric unsigned &MissingArgIndex, 5030b57cec5SDimitry Andric unsigned &MissingArgCount, 5040b57cec5SDimitry Andric unsigned FlagsToInclude, 5050b57cec5SDimitry Andric unsigned FlagsToExclude) const { 5065f757f3fSDimitry Andric return internalParseArgs( 5075f757f3fSDimitry Andric Args, MissingArgIndex, MissingArgCount, 5085f757f3fSDimitry Andric [FlagsToInclude, FlagsToExclude](const Option &Opt) { 5095f757f3fSDimitry Andric if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude)) 5105f757f3fSDimitry Andric return true; 5115f757f3fSDimitry Andric if (Opt.hasFlag(FlagsToExclude)) 5125f757f3fSDimitry Andric return true; 5135f757f3fSDimitry Andric return false; 5145f757f3fSDimitry Andric }); 5155f757f3fSDimitry Andric } 5165f757f3fSDimitry Andric 5175f757f3fSDimitry Andric InputArgList OptTable::internalParseArgs( 5185f757f3fSDimitry Andric ArrayRef<const char *> ArgArr, unsigned &MissingArgIndex, 5195f757f3fSDimitry Andric unsigned &MissingArgCount, 5205f757f3fSDimitry Andric std::function<bool(const Option &)> ExcludeOption) const { 5210b57cec5SDimitry Andric InputArgList Args(ArgArr.begin(), ArgArr.end()); 5220b57cec5SDimitry Andric 5230b57cec5SDimitry Andric // FIXME: Handle '@' args (or at least error on them). 5240b57cec5SDimitry Andric 5250b57cec5SDimitry Andric MissingArgIndex = MissingArgCount = 0; 5260b57cec5SDimitry Andric unsigned Index = 0, End = ArgArr.size(); 5270b57cec5SDimitry Andric while (Index < End) { 5280b57cec5SDimitry Andric // Ingore nullptrs, they are response file's EOL markers 5290b57cec5SDimitry Andric if (Args.getArgString(Index) == nullptr) { 5300b57cec5SDimitry Andric ++Index; 5310b57cec5SDimitry Andric continue; 5320b57cec5SDimitry Andric } 5330b57cec5SDimitry Andric // Ignore empty arguments (other things may still take them as arguments). 5340b57cec5SDimitry Andric StringRef Str = Args.getArgString(Index); 5350b57cec5SDimitry Andric if (Str == "") { 5360b57cec5SDimitry Andric ++Index; 5370b57cec5SDimitry Andric continue; 5380b57cec5SDimitry Andric } 5390b57cec5SDimitry Andric 54006c3fb27SDimitry Andric // In DashDashParsing mode, the first "--" stops option scanning and treats 54106c3fb27SDimitry Andric // all subsequent arguments as positional. 54206c3fb27SDimitry Andric if (DashDashParsing && Str == "--") { 54306c3fb27SDimitry Andric while (++Index < End) { 54406c3fb27SDimitry Andric Args.append(new Arg(getOption(InputOptionID), Str, Index, 54506c3fb27SDimitry Andric Args.getArgString(Index))); 54606c3fb27SDimitry Andric } 54706c3fb27SDimitry Andric break; 54806c3fb27SDimitry Andric } 54906c3fb27SDimitry Andric 5500b57cec5SDimitry Andric unsigned Prev = Index; 551349cc55cSDimitry Andric std::unique_ptr<Arg> A = GroupedShortOptions 552e8d8bef9SDimitry Andric ? parseOneArgGrouped(Args, Index) 5535f757f3fSDimitry Andric : internalParseOneArg(Args, Index, ExcludeOption); 554e8d8bef9SDimitry Andric assert((Index > Prev || GroupedShortOptions) && 555e8d8bef9SDimitry Andric "Parser failed to consume argument."); 5560b57cec5SDimitry Andric 5570b57cec5SDimitry Andric // Check for missing argument error. 5580b57cec5SDimitry Andric if (!A) { 5590b57cec5SDimitry Andric assert(Index >= End && "Unexpected parser error."); 5600b57cec5SDimitry Andric assert(Index - Prev - 1 && "No missing arguments!"); 5610b57cec5SDimitry Andric MissingArgIndex = Prev; 5620b57cec5SDimitry Andric MissingArgCount = Index - Prev - 1; 5630b57cec5SDimitry Andric break; 5640b57cec5SDimitry Andric } 5650b57cec5SDimitry Andric 566349cc55cSDimitry Andric Args.append(A.release()); 5670b57cec5SDimitry Andric } 5680b57cec5SDimitry Andric 5690b57cec5SDimitry Andric return Args; 5700b57cec5SDimitry Andric } 5710b57cec5SDimitry Andric 572e8d8bef9SDimitry Andric InputArgList OptTable::parseArgs(int Argc, char *const *Argv, 573e8d8bef9SDimitry Andric OptSpecifier Unknown, StringSaver &Saver, 5745f757f3fSDimitry Andric std::function<void(StringRef)> ErrorFn) const { 575e8d8bef9SDimitry Andric SmallVector<const char *, 0> NewArgv; 576e8d8bef9SDimitry Andric // The environment variable specifies initial options which can be overridden 577e8d8bef9SDimitry Andric // by commnad line options. 578e8d8bef9SDimitry Andric cl::expandResponseFiles(Argc, Argv, EnvVar, Saver, NewArgv); 579e8d8bef9SDimitry Andric 580e8d8bef9SDimitry Andric unsigned MAI, MAC; 581bdd1243dSDimitry Andric opt::InputArgList Args = ParseArgs(ArrayRef(NewArgv), MAI, MAC); 582e8d8bef9SDimitry Andric if (MAC) 583e8d8bef9SDimitry Andric ErrorFn((Twine(Args.getArgString(MAI)) + ": missing argument").str()); 584e8d8bef9SDimitry Andric 585e8d8bef9SDimitry Andric // For each unknwon option, call ErrorFn with a formatted error message. The 586e8d8bef9SDimitry Andric // message includes a suggested alternative option spelling if available. 587e8d8bef9SDimitry Andric std::string Nearest; 588e8d8bef9SDimitry Andric for (const opt::Arg *A : Args.filtered(Unknown)) { 589e8d8bef9SDimitry Andric std::string Spelling = A->getAsString(Args); 590e8d8bef9SDimitry Andric if (findNearest(Spelling, Nearest) > 1) 591bdd1243dSDimitry Andric ErrorFn("unknown argument '" + Spelling + "'"); 592e8d8bef9SDimitry Andric else 593bdd1243dSDimitry Andric ErrorFn("unknown argument '" + Spelling + "', did you mean '" + Nearest + 594bdd1243dSDimitry Andric "'?"); 595e8d8bef9SDimitry Andric } 596e8d8bef9SDimitry Andric return Args; 597e8d8bef9SDimitry Andric } 598e8d8bef9SDimitry Andric 5990b57cec5SDimitry Andric static std::string getOptionHelpName(const OptTable &Opts, OptSpecifier Id) { 6000b57cec5SDimitry Andric const Option O = Opts.getOption(Id); 6015f757f3fSDimitry Andric std::string Name = O.getPrefixedName().str(); 6020b57cec5SDimitry Andric 6030b57cec5SDimitry Andric // Add metavar, if used. 6040b57cec5SDimitry Andric switch (O.getKind()) { 6050b57cec5SDimitry Andric case Option::GroupClass: case Option::InputClass: case Option::UnknownClass: 6060b57cec5SDimitry Andric llvm_unreachable("Invalid option with help text."); 6070b57cec5SDimitry Andric 6080b57cec5SDimitry Andric case Option::MultiArgClass: 6090b57cec5SDimitry Andric if (const char *MetaVarName = Opts.getOptionMetaVar(Id)) { 6100b57cec5SDimitry Andric // For MultiArgs, metavar is full list of all argument names. 6110b57cec5SDimitry Andric Name += ' '; 6120b57cec5SDimitry Andric Name += MetaVarName; 6130b57cec5SDimitry Andric } 6140b57cec5SDimitry Andric else { 6150b57cec5SDimitry Andric // For MultiArgs<N>, if metavar not supplied, print <value> N times. 6160b57cec5SDimitry Andric for (unsigned i=0, e=O.getNumArgs(); i< e; ++i) { 6170b57cec5SDimitry Andric Name += " <value>"; 6180b57cec5SDimitry Andric } 6190b57cec5SDimitry Andric } 6200b57cec5SDimitry Andric break; 6210b57cec5SDimitry Andric 6220b57cec5SDimitry Andric case Option::FlagClass: 6230b57cec5SDimitry Andric break; 6240b57cec5SDimitry Andric 6250b57cec5SDimitry Andric case Option::ValuesClass: 6260b57cec5SDimitry Andric break; 6270b57cec5SDimitry Andric 6280b57cec5SDimitry Andric case Option::SeparateClass: case Option::JoinedOrSeparateClass: 6290b57cec5SDimitry Andric case Option::RemainingArgsClass: case Option::RemainingArgsJoinedClass: 6300b57cec5SDimitry Andric Name += ' '; 631bdd1243dSDimitry Andric [[fallthrough]]; 6320b57cec5SDimitry Andric case Option::JoinedClass: case Option::CommaJoinedClass: 6330b57cec5SDimitry Andric case Option::JoinedAndSeparateClass: 6340b57cec5SDimitry Andric if (const char *MetaVarName = Opts.getOptionMetaVar(Id)) 6350b57cec5SDimitry Andric Name += MetaVarName; 6360b57cec5SDimitry Andric else 6370b57cec5SDimitry Andric Name += "<value>"; 6380b57cec5SDimitry Andric break; 6390b57cec5SDimitry Andric } 6400b57cec5SDimitry Andric 6410b57cec5SDimitry Andric return Name; 6420b57cec5SDimitry Andric } 6430b57cec5SDimitry Andric 6440b57cec5SDimitry Andric namespace { 6450b57cec5SDimitry Andric struct OptionInfo { 6460b57cec5SDimitry Andric std::string Name; 6470b57cec5SDimitry Andric StringRef HelpText; 6480b57cec5SDimitry Andric }; 6490b57cec5SDimitry Andric } // namespace 6500b57cec5SDimitry Andric 6510b57cec5SDimitry Andric static void PrintHelpOptionList(raw_ostream &OS, StringRef Title, 6520b57cec5SDimitry Andric std::vector<OptionInfo> &OptionHelp) { 6530b57cec5SDimitry Andric OS << Title << ":\n"; 6540b57cec5SDimitry Andric 6550b57cec5SDimitry Andric // Find the maximum option length. 6560b57cec5SDimitry Andric unsigned OptionFieldWidth = 0; 6570eae32dcSDimitry Andric for (const OptionInfo &Opt : OptionHelp) { 6580b57cec5SDimitry Andric // Limit the amount of padding we are willing to give up for alignment. 6590eae32dcSDimitry Andric unsigned Length = Opt.Name.size(); 6600b57cec5SDimitry Andric if (Length <= 23) 6610b57cec5SDimitry Andric OptionFieldWidth = std::max(OptionFieldWidth, Length); 6620b57cec5SDimitry Andric } 6630b57cec5SDimitry Andric 6640b57cec5SDimitry Andric const unsigned InitialPad = 2; 6650eae32dcSDimitry Andric for (const OptionInfo &Opt : OptionHelp) { 6660eae32dcSDimitry Andric const std::string &Option = Opt.Name; 6675f757f3fSDimitry Andric int Pad = OptionFieldWidth + InitialPad; 6685f757f3fSDimitry Andric int FirstLinePad = OptionFieldWidth - int(Option.size()); 6690b57cec5SDimitry Andric OS.indent(InitialPad) << Option; 6700b57cec5SDimitry Andric 6710b57cec5SDimitry Andric // Break on long option names. 6725f757f3fSDimitry Andric if (FirstLinePad < 0) { 6730b57cec5SDimitry Andric OS << "\n"; 6745f757f3fSDimitry Andric FirstLinePad = OptionFieldWidth + InitialPad; 6755f757f3fSDimitry Andric Pad = FirstLinePad; 6760b57cec5SDimitry Andric } 6775f757f3fSDimitry Andric 6785f757f3fSDimitry Andric SmallVector<StringRef> Lines; 6795f757f3fSDimitry Andric Opt.HelpText.split(Lines, '\n'); 6805f757f3fSDimitry Andric assert(Lines.size() && "Expected at least the first line in the help text"); 6815f757f3fSDimitry Andric auto *LinesIt = Lines.begin(); 6825f757f3fSDimitry Andric OS.indent(FirstLinePad + 1) << *LinesIt << '\n'; 6835f757f3fSDimitry Andric while (Lines.end() != ++LinesIt) 6845f757f3fSDimitry Andric OS.indent(Pad + 1) << *LinesIt << '\n'; 6850b57cec5SDimitry Andric } 6860b57cec5SDimitry Andric } 6870b57cec5SDimitry Andric 6880b57cec5SDimitry Andric static const char *getOptionHelpGroup(const OptTable &Opts, OptSpecifier Id) { 6890b57cec5SDimitry Andric unsigned GroupID = Opts.getOptionGroupID(Id); 6900b57cec5SDimitry Andric 6910b57cec5SDimitry Andric // If not in a group, return the default help group. 6920b57cec5SDimitry Andric if (!GroupID) 6930b57cec5SDimitry Andric return "OPTIONS"; 6940b57cec5SDimitry Andric 6950b57cec5SDimitry Andric // Abuse the help text of the option groups to store the "help group" 6960b57cec5SDimitry Andric // name. 6970b57cec5SDimitry Andric // 6980b57cec5SDimitry Andric // FIXME: Split out option groups. 6990b57cec5SDimitry Andric if (const char *GroupHelp = Opts.getOptionHelpText(GroupID)) 7000b57cec5SDimitry Andric return GroupHelp; 7010b57cec5SDimitry Andric 7020b57cec5SDimitry Andric // Otherwise keep looking. 7030b57cec5SDimitry Andric return getOptionHelpGroup(Opts, GroupID); 7040b57cec5SDimitry Andric } 7050b57cec5SDimitry Andric 706fe6060f1SDimitry Andric void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title, 7075f757f3fSDimitry Andric bool ShowHidden, bool ShowAllAliases, 7085f757f3fSDimitry Andric Visibility VisibilityMask) const { 7095f757f3fSDimitry Andric return internalPrintHelp( 7105f757f3fSDimitry Andric OS, Usage, Title, ShowHidden, ShowAllAliases, 7115f757f3fSDimitry Andric [VisibilityMask](const Info &CandidateInfo) -> bool { 7125f757f3fSDimitry Andric return (CandidateInfo.Visibility & VisibilityMask) == 0; 713*0fca6ea1SDimitry Andric }, 714*0fca6ea1SDimitry Andric VisibilityMask); 7150b57cec5SDimitry Andric } 7160b57cec5SDimitry Andric 717fe6060f1SDimitry Andric void OptTable::printHelp(raw_ostream &OS, const char *Usage, const char *Title, 7180b57cec5SDimitry Andric unsigned FlagsToInclude, unsigned FlagsToExclude, 7190b57cec5SDimitry Andric bool ShowAllAliases) const { 7205f757f3fSDimitry Andric bool ShowHidden = !(FlagsToExclude & HelpHidden); 7215f757f3fSDimitry Andric FlagsToExclude &= ~HelpHidden; 7225f757f3fSDimitry Andric return internalPrintHelp( 7235f757f3fSDimitry Andric OS, Usage, Title, ShowHidden, ShowAllAliases, 7245f757f3fSDimitry Andric [FlagsToInclude, FlagsToExclude](const Info &CandidateInfo) { 7255f757f3fSDimitry Andric if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude)) 7265f757f3fSDimitry Andric return true; 7275f757f3fSDimitry Andric if (CandidateInfo.Flags & FlagsToExclude) 7285f757f3fSDimitry Andric return true; 7295f757f3fSDimitry Andric return false; 730*0fca6ea1SDimitry Andric }, 731*0fca6ea1SDimitry Andric Visibility(0)); 7325f757f3fSDimitry Andric } 7335f757f3fSDimitry Andric 7345f757f3fSDimitry Andric void OptTable::internalPrintHelp( 7355f757f3fSDimitry Andric raw_ostream &OS, const char *Usage, const char *Title, bool ShowHidden, 736*0fca6ea1SDimitry Andric bool ShowAllAliases, std::function<bool(const Info &)> ExcludeOption, 737*0fca6ea1SDimitry Andric Visibility VisibilityMask) const { 7380b57cec5SDimitry Andric OS << "OVERVIEW: " << Title << "\n\n"; 7390b57cec5SDimitry Andric OS << "USAGE: " << Usage << "\n\n"; 7400b57cec5SDimitry Andric 7410b57cec5SDimitry Andric // Render help text into a map of group-name to a list of (option, help) 7420b57cec5SDimitry Andric // pairs. 7430b57cec5SDimitry Andric std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp; 7440b57cec5SDimitry Andric 7450b57cec5SDimitry Andric for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) { 7460b57cec5SDimitry Andric // FIXME: Split out option groups. 7470b57cec5SDimitry Andric if (getOptionKind(Id) == Option::GroupClass) 7480b57cec5SDimitry Andric continue; 7490b57cec5SDimitry Andric 7505f757f3fSDimitry Andric const Info &CandidateInfo = getInfo(Id); 7515f757f3fSDimitry Andric if (!ShowHidden && (CandidateInfo.Flags & opt::HelpHidden)) 7520b57cec5SDimitry Andric continue; 7535f757f3fSDimitry Andric 7545f757f3fSDimitry Andric if (ExcludeOption(CandidateInfo)) 7550b57cec5SDimitry Andric continue; 7560b57cec5SDimitry Andric 7570b57cec5SDimitry Andric // If an alias doesn't have a help text, show a help text for the aliased 7580b57cec5SDimitry Andric // option instead. 759*0fca6ea1SDimitry Andric const char *HelpText = getOptionHelpText(Id, VisibilityMask); 7600b57cec5SDimitry Andric if (!HelpText && ShowAllAliases) { 7610b57cec5SDimitry Andric const Option Alias = getOption(Id).getAlias(); 7620b57cec5SDimitry Andric if (Alias.isValid()) 763*0fca6ea1SDimitry Andric HelpText = getOptionHelpText(Alias.getID(), VisibilityMask); 7640b57cec5SDimitry Andric } 7650b57cec5SDimitry Andric 766349cc55cSDimitry Andric if (HelpText && (strlen(HelpText) != 0)) { 7670b57cec5SDimitry Andric const char *HelpGroup = getOptionHelpGroup(*this, Id); 7680b57cec5SDimitry Andric const std::string &OptName = getOptionHelpName(*this, Id); 7690b57cec5SDimitry Andric GroupedOptionHelp[HelpGroup].push_back({OptName, HelpText}); 7700b57cec5SDimitry Andric } 7710b57cec5SDimitry Andric } 7720b57cec5SDimitry Andric 7730b57cec5SDimitry Andric for (auto& OptionGroup : GroupedOptionHelp) { 7740b57cec5SDimitry Andric if (OptionGroup.first != GroupedOptionHelp.begin()->first) 7750b57cec5SDimitry Andric OS << "\n"; 7760b57cec5SDimitry Andric PrintHelpOptionList(OS, OptionGroup.first, OptionGroup.second); 7770b57cec5SDimitry Andric } 7780b57cec5SDimitry Andric 7790b57cec5SDimitry Andric OS.flush(); 7800b57cec5SDimitry Andric } 781bdd1243dSDimitry Andric 782bdd1243dSDimitry Andric GenericOptTable::GenericOptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase) 783bdd1243dSDimitry Andric : OptTable(OptionInfos, IgnoreCase) { 784bdd1243dSDimitry Andric 785bdd1243dSDimitry Andric std::set<StringLiteral> TmpPrefixesUnion; 786bdd1243dSDimitry Andric for (auto const &Info : OptionInfos.drop_front(FirstSearchableIndex)) 787bdd1243dSDimitry Andric TmpPrefixesUnion.insert(Info.Prefixes.begin(), Info.Prefixes.end()); 788bdd1243dSDimitry Andric PrefixesUnionBuffer.append(TmpPrefixesUnion.begin(), TmpPrefixesUnion.end()); 789bdd1243dSDimitry Andric buildPrefixChars(); 790bdd1243dSDimitry Andric } 791