brintos

brintos / llvm-project-archived public Read only

0
0
Text · 30.6 KiB · 065036c Raw
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