summaryrefslogtreecommitdiff
path: root/llvm/lib/Option
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Option')
-rw-r--r--llvm/lib/Option/Arg.cpp125
-rw-r--r--llvm/lib/Option/ArgList.cpp274
-rw-r--r--llvm/lib/Option/OptTable.cpp583
-rw-r--r--llvm/lib/Option/Option.cpp290
4 files changed, 1272 insertions, 0 deletions
diff --git a/llvm/lib/Option/Arg.cpp b/llvm/lib/Option/Arg.cpp
new file mode 100644
index 0000000000000..ea382b347345b
--- /dev/null
+++ b/llvm/lib/Option/Arg.cpp
@@ -0,0 +1,125 @@
+//===- Arg.cpp - Argument Implementations ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::opt;
+
+Arg::Arg(const Option Opt, StringRef S, unsigned Index, const Arg *BaseArg)
+ : Opt(Opt), BaseArg(BaseArg), Spelling(S), Index(Index), Claimed(false),
+ OwnsValues(false) {}
+
+Arg::Arg(const Option Opt, StringRef S, unsigned Index, const char *Value0,
+ const Arg *BaseArg)
+ : Opt(Opt), BaseArg(BaseArg), Spelling(S), Index(Index), Claimed(false),
+ OwnsValues(false) {
+ Values.push_back(Value0);
+}
+
+Arg::Arg(const Option Opt, StringRef S, unsigned Index, const char *Value0,
+ const char *Value1, const Arg *BaseArg)
+ : Opt(Opt), BaseArg(BaseArg), Spelling(S), Index(Index), Claimed(false),
+ OwnsValues(false) {
+ Values.push_back(Value0);
+ Values.push_back(Value1);
+}
+
+Arg::~Arg() {
+ if (OwnsValues) {
+ for (unsigned i = 0, e = Values.size(); i != e; ++i)
+ delete[] Values[i];
+ }
+}
+
+void Arg::print(raw_ostream& O) const {
+ O << "<";
+
+ O << " Opt:";
+ Opt.print(O);
+
+ O << " Index:" << Index;
+
+ O << " Values: [";
+ for (unsigned i = 0, e = Values.size(); i != e; ++i) {
+ if (i) O << ", ";
+ O << "'" << Values[i] << "'";
+ }
+
+ O << "]>\n";
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void Arg::dump() const { print(dbgs()); }
+#endif
+
+std::string Arg::getAsString(const ArgList &Args) const {
+ if (Alias)
+ return Alias->getAsString(Args);
+
+ SmallString<256> Res;
+ raw_svector_ostream OS(Res);
+
+ ArgStringList ASL;
+ render(Args, ASL);
+ for (ArgStringList::iterator
+ it = ASL.begin(), ie = ASL.end(); it != ie; ++it) {
+ if (it != ASL.begin())
+ OS << ' ';
+ OS << *it;
+ }
+
+ return OS.str();
+}
+
+void Arg::renderAsInput(const ArgList &Args, ArgStringList &Output) const {
+ if (!getOption().hasNoOptAsInput()) {
+ render(Args, Output);
+ return;
+ }
+
+ Output.append(Values.begin(), Values.end());
+}
+
+void Arg::render(const ArgList &Args, ArgStringList &Output) const {
+ switch (getOption().getRenderStyle()) {
+ case Option::RenderValuesStyle:
+ Output.append(Values.begin(), Values.end());
+ break;
+
+ case Option::RenderCommaJoinedStyle: {
+ SmallString<256> Res;
+ raw_svector_ostream OS(Res);
+ OS << getSpelling();
+ for (unsigned i = 0, e = getNumValues(); i != e; ++i) {
+ if (i) OS << ',';
+ OS << getValue(i);
+ }
+ Output.push_back(Args.MakeArgString(OS.str()));
+ break;
+ }
+
+ case Option::RenderJoinedStyle:
+ Output.push_back(Args.GetOrMakeJoinedArgString(
+ getIndex(), getSpelling(), getValue(0)));
+ Output.append(Values.begin() + 1, Values.end());
+ break;
+
+ case Option::RenderSeparateStyle:
+ Output.push_back(Args.MakeArgString(getSpelling()));
+ Output.append(Values.begin(), Values.end());
+ break;
+ }
+}
diff --git a/llvm/lib/Option/ArgList.cpp b/llvm/lib/Option/ArgList.cpp
new file mode 100644
index 0000000000000..09e921502eb67
--- /dev/null
+++ b/llvm/lib/Option/ArgList.cpp
@@ -0,0 +1,274 @@
+//===- ArgList.cpp - Argument List Management -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Option/OptSpecifier.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::opt;
+
+void ArgList::append(Arg *A) {
+ Args.push_back(A);
+
+ // Update ranges for the option and all of its groups.
+ for (Option O = A->getOption().getUnaliasedOption(); O.isValid();
+ O = O.getGroup()) {
+ auto &R =
+ OptRanges.insert(std::make_pair(O.getID(), emptyRange())).first->second;
+ R.first = std::min<unsigned>(R.first, Args.size() - 1);
+ R.second = Args.size();
+ }
+}
+
+void ArgList::eraseArg(OptSpecifier Id) {
+ // Zero out the removed entries but keep them around so that we don't
+ // need to invalidate OptRanges.
+ for (Arg *const &A : filtered(Id)) {
+ // Avoid the need for a non-const filtered iterator variant.
+ Arg **ArgsBegin = Args.data();
+ ArgsBegin[&A - ArgsBegin] = nullptr;
+ }
+ OptRanges.erase(Id.getID());
+}
+
+ArgList::OptRange
+ArgList::getRange(std::initializer_list<OptSpecifier> Ids) const {
+ OptRange R = emptyRange();
+ for (auto Id : Ids) {
+ auto I = OptRanges.find(Id.getID());
+ if (I != OptRanges.end()) {
+ R.first = std::min(R.first, I->second.first);
+ R.second = std::max(R.second, I->second.second);
+ }
+ }
+ // Map an empty {-1, 0} range to {0, 0} so it can be used to form iterators.
+ if (R.first == -1u)
+ R.first = 0;
+ return R;
+}
+
+bool ArgList::hasFlag(OptSpecifier Pos, OptSpecifier Neg, bool Default) const {
+ if (Arg *A = getLastArg(Pos, Neg))
+ return A->getOption().matches(Pos);
+ return Default;
+}
+
+bool ArgList::hasFlag(OptSpecifier Pos, OptSpecifier PosAlias, OptSpecifier Neg,
+ bool Default) const {
+ if (Arg *A = getLastArg(Pos, PosAlias, Neg))
+ return A->getOption().matches(Pos) || A->getOption().matches(PosAlias);
+ return Default;
+}
+
+StringRef ArgList::getLastArgValue(OptSpecifier Id, StringRef Default) const {
+ if (Arg *A = getLastArg(Id))
+ return A->getValue();
+ return Default;
+}
+
+std::vector<std::string> ArgList::getAllArgValues(OptSpecifier Id) const {
+ SmallVector<const char *, 16> Values;
+ AddAllArgValues(Values, Id);
+ return std::vector<std::string>(Values.begin(), Values.end());
+}
+
+void ArgList::AddAllArgsExcept(ArgStringList &Output,
+ ArrayRef<OptSpecifier> Ids,
+ ArrayRef<OptSpecifier> ExcludeIds) const {
+ for (const Arg *Arg : *this) {
+ bool Excluded = false;
+ for (OptSpecifier Id : ExcludeIds) {
+ if (Arg->getOption().matches(Id)) {
+ Excluded = true;
+ break;
+ }
+ }
+ if (!Excluded) {
+ for (OptSpecifier Id : Ids) {
+ if (Arg->getOption().matches(Id)) {
+ Arg->claim();
+ Arg->render(*this, Output);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/// This is a nicer interface when you don't have a list of Ids to exclude.
+void ArgList::AddAllArgs(ArgStringList &Output,
+ ArrayRef<OptSpecifier> Ids) const {
+ ArrayRef<OptSpecifier> Exclude = None;
+ AddAllArgsExcept(Output, Ids, Exclude);
+}
+
+/// This 3-opt variant of AddAllArgs could be eliminated in favor of one
+/// that accepts a single specifier, given the above which accepts any number.
+void ArgList::AddAllArgs(ArgStringList &Output, OptSpecifier Id0,
+ OptSpecifier Id1, OptSpecifier Id2) const {
+ for (auto Arg: filtered(Id0, Id1, Id2)) {
+ Arg->claim();
+ Arg->render(*this, Output);
+ }
+}
+
+void ArgList::AddAllArgValues(ArgStringList &Output, OptSpecifier Id0,
+ OptSpecifier Id1, OptSpecifier Id2) const {
+ for (auto Arg : filtered(Id0, Id1, Id2)) {
+ Arg->claim();
+ const auto &Values = Arg->getValues();
+ Output.append(Values.begin(), Values.end());
+ }
+}
+
+void ArgList::AddAllArgsTranslated(ArgStringList &Output, OptSpecifier Id0,
+ const char *Translation,
+ bool Joined) const {
+ for (auto Arg: filtered(Id0)) {
+ Arg->claim();
+
+ if (Joined) {
+ Output.push_back(MakeArgString(StringRef(Translation) +
+ Arg->getValue(0)));
+ } else {
+ Output.push_back(Translation);
+ Output.push_back(Arg->getValue(0));
+ }
+ }
+}
+
+void ArgList::ClaimAllArgs(OptSpecifier Id0) const {
+ for (auto *Arg : filtered(Id0))
+ Arg->claim();
+}
+
+void ArgList::ClaimAllArgs() const {
+ for (auto *Arg : *this)
+ if (!Arg->isClaimed())
+ Arg->claim();
+}
+
+const char *ArgList::GetOrMakeJoinedArgString(unsigned Index,
+ StringRef LHS,
+ StringRef RHS) const {
+ StringRef Cur = getArgString(Index);
+ if (Cur.size() == LHS.size() + RHS.size() &&
+ Cur.startswith(LHS) && Cur.endswith(RHS))
+ return Cur.data();
+
+ return MakeArgString(LHS + RHS);
+}
+
+void ArgList::print(raw_ostream &O) const {
+ for (Arg *A : *this) {
+ O << "* ";
+ A->print(O);
+ }
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void ArgList::dump() const { print(dbgs()); }
+#endif
+
+void InputArgList::releaseMemory() {
+ // An InputArgList always owns its arguments.
+ for (Arg *A : *this)
+ delete A;
+}
+
+InputArgList::InputArgList(const char* const *ArgBegin,
+ const char* const *ArgEnd)
+ : NumInputArgStrings(ArgEnd - ArgBegin) {
+ ArgStrings.append(ArgBegin, ArgEnd);
+}
+
+unsigned InputArgList::MakeIndex(StringRef String0) const {
+ unsigned Index = ArgStrings.size();
+
+ // Tuck away so we have a reliable const char *.
+ SynthesizedStrings.push_back(String0);
+ ArgStrings.push_back(SynthesizedStrings.back().c_str());
+
+ return Index;
+}
+
+unsigned InputArgList::MakeIndex(StringRef String0,
+ StringRef String1) const {
+ unsigned Index0 = MakeIndex(String0);
+ unsigned Index1 = MakeIndex(String1);
+ assert(Index0 + 1 == Index1 && "Unexpected non-consecutive indices!");
+ (void) Index1;
+ return Index0;
+}
+
+const char *InputArgList::MakeArgStringRef(StringRef Str) const {
+ return getArgString(MakeIndex(Str));
+}
+
+DerivedArgList::DerivedArgList(const InputArgList &BaseArgs)
+ : BaseArgs(BaseArgs) {}
+
+const char *DerivedArgList::MakeArgStringRef(StringRef Str) const {
+ return BaseArgs.MakeArgString(Str);
+}
+
+void DerivedArgList::AddSynthesizedArg(Arg *A) {
+ SynthesizedArgs.push_back(std::unique_ptr<Arg>(A));
+}
+
+Arg *DerivedArgList::MakeFlagArg(const Arg *BaseArg, const Option Opt) const {
+ SynthesizedArgs.push_back(
+ std::make_unique<Arg>(Opt, MakeArgString(Opt.getPrefix() + Opt.getName()),
+ BaseArgs.MakeIndex(Opt.getName()), BaseArg));
+ return SynthesizedArgs.back().get();
+}
+
+Arg *DerivedArgList::MakePositionalArg(const Arg *BaseArg, const Option Opt,
+ StringRef Value) const {
+ unsigned Index = BaseArgs.MakeIndex(Value);
+ SynthesizedArgs.push_back(
+ std::make_unique<Arg>(Opt, MakeArgString(Opt.getPrefix() + Opt.getName()),
+ Index, BaseArgs.getArgString(Index), BaseArg));
+ return SynthesizedArgs.back().get();
+}
+
+Arg *DerivedArgList::MakeSeparateArg(const Arg *BaseArg, const Option Opt,
+ StringRef Value) const {
+ unsigned Index = BaseArgs.MakeIndex(Opt.getName(), Value);
+ SynthesizedArgs.push_back(
+ std::make_unique<Arg>(Opt, MakeArgString(Opt.getPrefix() + Opt.getName()),
+ Index, BaseArgs.getArgString(Index + 1), BaseArg));
+ return SynthesizedArgs.back().get();
+}
+
+Arg *DerivedArgList::MakeJoinedArg(const Arg *BaseArg, const Option Opt,
+ StringRef Value) const {
+ unsigned Index = BaseArgs.MakeIndex((Opt.getName() + Value).str());
+ SynthesizedArgs.push_back(std::make_unique<Arg>(
+ Opt, MakeArgString(Opt.getPrefix() + Opt.getName()), Index,
+ BaseArgs.getArgString(Index) + Opt.getName().size(), BaseArg));
+ return SynthesizedArgs.back().get();
+}
diff --git a/llvm/lib/Option/OptTable.cpp b/llvm/lib/Option/OptTable.cpp
new file mode 100644
index 0000000000000..5833d03069f86
--- /dev/null
+++ b/llvm/lib/Option/OptTable.cpp
@@ -0,0 +1,583 @@
+//===- OptTable.cpp - Option Table Implementation -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Option/OptSpecifier.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstring>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::opt;
+
+namespace llvm {
+namespace opt {
+
+// Ordering on Info. The ordering is *almost* case-insensitive lexicographic,
+// with an exception. '\0' comes at the end of the alphabet instead of the
+// beginning (thus options precede any other options which prefix them).
+static int StrCmpOptionNameIgnoreCase(const char *A, const char *B) {
+ const char *X = A, *Y = B;
+ char a = tolower(*A), b = tolower(*B);
+ while (a == b) {
+ if (a == '\0')
+ return 0;
+
+ a = tolower(*++X);
+ b = tolower(*++Y);
+ }
+
+ if (a == '\0') // A is a prefix of B.
+ return 1;
+ if (b == '\0') // B is a prefix of A.
+ return -1;
+
+ // Otherwise lexicographic.
+ return (a < b) ? -1 : 1;
+}
+
+#ifndef NDEBUG
+static int StrCmpOptionName(const char *A, const char *B) {
+ if (int N = StrCmpOptionNameIgnoreCase(A, B))
+ return N;
+ return strcmp(A, B);
+}
+
+static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) {
+ if (&A == &B)
+ return false;
+
+ if (int N = StrCmpOptionName(A.Name, B.Name))
+ return N < 0;
+
+ for (const char * const *APre = A.Prefixes,
+ * const *BPre = B.Prefixes;
+ *APre != nullptr && *BPre != nullptr; ++APre, ++BPre){
+ if (int N = StrCmpOptionName(*APre, *BPre))
+ return N < 0;
+ }
+
+ // Names are the same, check that classes are in order; exactly one
+ // should be joined, and it should succeed the other.
+ assert(((A.Kind == Option::JoinedClass) ^ (B.Kind == Option::JoinedClass)) &&
+ "Unexpected classes for options with same name.");
+ return B.Kind == Option::JoinedClass;
+}
+#endif
+
+// Support lower_bound between info and an option name.
+static inline bool operator<(const OptTable::Info &I, const char *Name) {
+ return StrCmpOptionNameIgnoreCase(I.Name, Name) < 0;
+}
+
+} // end namespace opt
+} // end namespace llvm
+
+OptSpecifier::OptSpecifier(const Option *Opt) : ID(Opt->getID()) {}
+
+OptTable::OptTable(ArrayRef<Info> OptionInfos, bool IgnoreCase)
+ : OptionInfos(OptionInfos), IgnoreCase(IgnoreCase) {
+ // Explicitly zero initialize the error to work around a bug in array
+ // value-initialization on MinGW with gcc 4.3.5.
+
+ // Find start of normal options.
+ for (unsigned i = 0, e = getNumOptions(); i != e; ++i) {
+ unsigned Kind = getInfo(i + 1).Kind;
+ if (Kind == Option::InputClass) {
+ assert(!TheInputOptionID && "Cannot have multiple input options!");
+ TheInputOptionID = getInfo(i + 1).ID;
+ } else if (Kind == Option::UnknownClass) {
+ assert(!TheUnknownOptionID && "Cannot have multiple unknown options!");
+ TheUnknownOptionID = getInfo(i + 1).ID;
+ } else if (Kind != Option::GroupClass) {
+ FirstSearchableIndex = i;
+ break;
+ }
+ }
+ assert(FirstSearchableIndex != 0 && "No searchable options?");
+
+#ifndef NDEBUG
+ // Check that everything after the first searchable option is a
+ // regular option class.
+ for (unsigned i = FirstSearchableIndex, e = getNumOptions(); i != e; ++i) {
+ Option::OptionClass Kind = (Option::OptionClass) getInfo(i + 1).Kind;
+ assert((Kind != Option::InputClass && Kind != Option::UnknownClass &&
+ Kind != Option::GroupClass) &&
+ "Special options should be defined first!");
+ }
+
+ // Check that options are in order.
+ for (unsigned i = FirstSearchableIndex + 1, e = getNumOptions(); i != e; ++i){
+ if (!(getInfo(i) < getInfo(i + 1))) {
+ getOption(i).dump();
+ getOption(i + 1).dump();
+ llvm_unreachable("Options are not in order!");
+ }
+ }
+#endif
+
+ // Build prefixes.
+ for (unsigned i = FirstSearchableIndex + 1, e = getNumOptions() + 1;
+ i != e; ++i) {
+ if (const char *const *P = getInfo(i).Prefixes) {
+ for (; *P != nullptr; ++P) {
+ PrefixesUnion.insert(*P);
+ }
+ }
+ }
+
+ // Build prefix chars.
+ for (StringSet<>::const_iterator I = PrefixesUnion.begin(),
+ E = PrefixesUnion.end(); I != E; ++I) {
+ StringRef Prefix = I->getKey();
+ for (StringRef::const_iterator C = Prefix.begin(), CE = Prefix.end();
+ C != CE; ++C)
+ if (!is_contained(PrefixChars, *C))
+ PrefixChars.push_back(*C);
+ }
+}
+
+OptTable::~OptTable() = default;
+
+const Option OptTable::getOption(OptSpecifier Opt) const {
+ unsigned id = Opt.getID();
+ if (id == 0)
+ return Option(nullptr, nullptr);
+ assert((unsigned) (id - 1) < getNumOptions() && "Invalid ID.");
+ return Option(&getInfo(id), this);
+}
+
+static bool isInput(const StringSet<> &Prefixes, StringRef Arg) {
+ if (Arg == "-")
+ return true;
+ for (StringSet<>::const_iterator I = Prefixes.begin(),
+ E = Prefixes.end(); I != E; ++I)
+ if (Arg.startswith(I->getKey()))
+ return false;
+ return true;
+}
+
+/// \returns Matched size. 0 means no match.
+static unsigned matchOption(const OptTable::Info *I, StringRef Str,
+ bool IgnoreCase) {
+ for (const char * const *Pre = I->Prefixes; *Pre != nullptr; ++Pre) {
+ StringRef Prefix(*Pre);
+ if (Str.startswith(Prefix)) {
+ StringRef Rest = Str.substr(Prefix.size());
+ bool Matched = IgnoreCase
+ ? Rest.startswith_lower(I->Name)
+ : Rest.startswith(I->Name);
+ if (Matched)
+ return Prefix.size() + StringRef(I->Name).size();
+ }
+ }
+ return 0;
+}
+
+// Returns true if one of the Prefixes + In.Names matches Option
+static bool optionMatches(const OptTable::Info &In, StringRef Option) {
+ if (In.Prefixes)
+ for (size_t I = 0; In.Prefixes[I]; I++)
+ if (Option == std::string(In.Prefixes[I]) + In.Name)
+ return true;
+ return false;
+}
+
+// This function is for flag value completion.
+// Eg. When "-stdlib=" and "l" was passed to this function, it will return
+// appropiriate values for stdlib, which starts with l.
+std::vector<std::string>
+OptTable::suggestValueCompletions(StringRef Option, StringRef Arg) const {
+ // Search all options and return possible values.
+ for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) {
+ const Info &In = OptionInfos[I];
+ if (!In.Values || !optionMatches(In, Option))
+ continue;
+
+ SmallVector<StringRef, 8> Candidates;
+ StringRef(In.Values).split(Candidates, ",", -1, false);
+
+ std::vector<std::string> Result;
+ for (StringRef Val : Candidates)
+ if (Val.startswith(Arg) && Arg.compare(Val))
+ Result.push_back(Val);
+ return Result;
+ }
+ return {};
+}
+
+std::vector<std::string>
+OptTable::findByPrefix(StringRef Cur, unsigned short DisableFlags) const {
+ std::vector<std::string> Ret;
+ for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) {
+ const Info &In = OptionInfos[I];
+ if (!In.Prefixes || (!In.HelpText && !In.GroupID))
+ continue;
+ if (In.Flags & DisableFlags)
+ continue;
+
+ for (int I = 0; In.Prefixes[I]; I++) {
+ std::string S = std::string(In.Prefixes[I]) + std::string(In.Name) + "\t";
+ if (In.HelpText)
+ S += In.HelpText;
+ if (StringRef(S).startswith(Cur) && S.compare(std::string(Cur) + "\t"))
+ Ret.push_back(S);
+ }
+ }
+ return Ret;
+}
+
+unsigned OptTable::findNearest(StringRef Option, std::string &NearestString,
+ unsigned FlagsToInclude, unsigned FlagsToExclude,
+ unsigned MinimumLength) const {
+ assert(!Option.empty());
+
+ // Consider each [option prefix + option name] pair as a candidate, finding
+ // the closest match.
+ unsigned BestDistance = UINT_MAX;
+ for (const Info &CandidateInfo :
+ ArrayRef<Info>(OptionInfos).drop_front(FirstSearchableIndex)) {
+ StringRef CandidateName = CandidateInfo.Name;
+
+ // We can eliminate some option prefix/name pairs as candidates right away:
+ // * Ignore option candidates with empty names, such as "--", or names
+ // that do not meet the minimum length.
+ if (CandidateName.empty() || CandidateName.size() < MinimumLength)
+ continue;
+
+ // * If FlagsToInclude were specified, ignore options that don't include
+ // those flags.
+ if (FlagsToInclude && !(CandidateInfo.Flags & FlagsToInclude))
+ continue;
+ // * Ignore options that contain the FlagsToExclude.
+ if (CandidateInfo.Flags & FlagsToExclude)
+ continue;
+
+ // * Ignore positional argument option candidates (which do not
+ // have prefixes).
+ if (!CandidateInfo.Prefixes)
+ continue;
+
+ // Now check if the candidate ends with a character commonly used when
+ // delimiting an option from its value, such as '=' or ':'. If it does,
+ // attempt to split the given option based on that delimiter.
+ StringRef LHS, RHS;
+ char Last = CandidateName.back();
+ bool CandidateHasDelimiter = Last == '=' || Last == ':';
+ std::string NormalizedName = Option;
+ if (CandidateHasDelimiter) {
+ std::tie(LHS, RHS) = Option.split(Last);
+ NormalizedName = LHS;
+ if (Option.find(Last) == LHS.size())
+ NormalizedName += Last;
+ }
+
+ // Consider each possible prefix for each candidate to find the most
+ // appropriate one. For example, if a user asks for "--helm", suggest
+ // "--help" over "-help".
+ for (int P = 0;
+ const char *const CandidatePrefix = CandidateInfo.Prefixes[P]; P++) {
+ std::string Candidate = (CandidatePrefix + CandidateName).str();
+ StringRef CandidateRef = Candidate;
+ unsigned Distance =
+ CandidateRef.edit_distance(NormalizedName, /*AllowReplacements=*/true,
+ /*MaxEditDistance=*/BestDistance);
+ if (RHS.empty() && CandidateHasDelimiter) {
+ // The Candidate ends with a = or : delimiter, but the option passed in
+ // didn't contain the delimiter (or doesn't have anything after it).
+ // In that case, penalize the correction: `-nodefaultlibs` is more
+ // likely to be a spello for `-nodefaultlib` than `-nodefaultlib:` even
+ // though both have an unmodified editing distance of 1, since the
+ // latter would need an argument.
+ ++Distance;
+ }
+ if (Distance < BestDistance) {
+ BestDistance = Distance;
+ NearestString = (Candidate + RHS).str();
+ }
+ }
+ }
+ return BestDistance;
+}
+
+bool OptTable::addValues(const char *Option, const char *Values) {
+ for (size_t I = FirstSearchableIndex, E = OptionInfos.size(); I < E; I++) {
+ Info &In = OptionInfos[I];
+ if (optionMatches(In, Option)) {
+ In.Values = Values;
+ return true;
+ }
+ }
+ return false;
+}
+
+Arg *OptTable::ParseOneArg(const ArgList &Args, unsigned &Index,
+ unsigned FlagsToInclude,
+ unsigned FlagsToExclude) const {
+ unsigned Prev = Index;
+ const char *Str = Args.getArgString(Index);
+
+ // Anything that doesn't start with PrefixesUnion is an input, as is '-'
+ // itself.
+ if (isInput(PrefixesUnion, Str))
+ return new Arg(getOption(TheInputOptionID), Str, Index++, Str);
+
+ const Info *Start = OptionInfos.data() + FirstSearchableIndex;
+ const Info *End = OptionInfos.data() + OptionInfos.size();
+ StringRef Name = StringRef(Str).ltrim(PrefixChars);
+
+ // Search for the first next option which could be a prefix.
+ Start = std::lower_bound(Start, End, Name.data());
+
+ // Options are stored in sorted order, with '\0' at the end of the
+ // alphabet. Since the only options which can accept a string must
+ // prefix it, we iteratively search for the next option which could
+ // be a prefix.
+ //
+ // FIXME: This is searching much more than necessary, but I am
+ // blanking on the simplest way to make it fast. We can solve this
+ // problem when we move to TableGen.
+ for (; Start != End; ++Start) {
+ unsigned ArgSize = 0;
+ // Scan for first option which is a proper prefix.
+ for (; Start != End; ++Start)
+ if ((ArgSize = matchOption(Start, Str, IgnoreCase)))
+ break;
+ if (Start == End)
+ break;
+
+ Option Opt(Start, this);
+
+ if (FlagsToInclude && !Opt.hasFlag(FlagsToInclude))
+ continue;
+ if (Opt.hasFlag(FlagsToExclude))
+ continue;
+
+ // See if this option matches.
+ if (Arg *A = Opt.accept(Args, Index, ArgSize))
+ return A;
+
+ // Otherwise, see if this argument was missing values.
+ if (Prev != Index)
+ return nullptr;
+ }
+
+ // If we failed to find an option and this arg started with /, then it's
+ // probably an input path.
+ if (Str[0] == '/')
+ return new Arg(getOption(TheInputOptionID), Str, Index++, Str);
+
+ return new Arg(getOption(TheUnknownOptionID), Str, Index++, Str);
+}
+
+InputArgList OptTable::ParseArgs(ArrayRef<const char *> ArgArr,
+ unsigned &MissingArgIndex,
+ unsigned &MissingArgCount,
+ unsigned FlagsToInclude,
+ unsigned FlagsToExclude) const {
+ InputArgList Args(ArgArr.begin(), ArgArr.end());
+
+ // FIXME: Handle '@' args (or at least error on them).
+
+ MissingArgIndex = MissingArgCount = 0;
+ unsigned Index = 0, End = ArgArr.size();
+ while (Index < End) {
+ // Ingore nullptrs, they are response file's EOL markers
+ if (Args.getArgString(Index) == nullptr) {
+ ++Index;
+ continue;
+ }
+ // Ignore empty arguments (other things may still take them as arguments).
+ StringRef Str = Args.getArgString(Index);
+ if (Str == "") {
+ ++Index;
+ continue;
+ }
+
+ unsigned Prev = Index;
+ Arg *A = ParseOneArg(Args, Index, FlagsToInclude, FlagsToExclude);
+ assert(Index > Prev && "Parser failed to consume argument.");
+
+ // Check for missing argument error.
+ if (!A) {
+ assert(Index >= End && "Unexpected parser error.");
+ assert(Index - Prev - 1 && "No missing arguments!");
+ MissingArgIndex = Prev;
+ MissingArgCount = Index - Prev - 1;
+ break;
+ }
+
+ Args.append(A);
+ }
+
+ return Args;
+}
+
+static std::string getOptionHelpName(const OptTable &Opts, OptSpecifier Id) {
+ const Option O = Opts.getOption(Id);
+ std::string Name = O.getPrefixedName();
+
+ // Add metavar, if used.
+ switch (O.getKind()) {
+ case Option::GroupClass: case Option::InputClass: case Option::UnknownClass:
+ llvm_unreachable("Invalid option with help text.");
+
+ case Option::MultiArgClass:
+ if (const char *MetaVarName = Opts.getOptionMetaVar(Id)) {
+ // For MultiArgs, metavar is full list of all argument names.
+ Name += ' ';
+ Name += MetaVarName;
+ }
+ else {
+ // For MultiArgs<N>, if metavar not supplied, print <value> N times.
+ for (unsigned i=0, e=O.getNumArgs(); i< e; ++i) {
+ Name += " <value>";
+ }
+ }
+ break;
+
+ case Option::FlagClass:
+ break;
+
+ case Option::ValuesClass:
+ break;
+
+ case Option::SeparateClass: case Option::JoinedOrSeparateClass:
+ case Option::RemainingArgsClass: case Option::RemainingArgsJoinedClass:
+ Name += ' ';
+ LLVM_FALLTHROUGH;
+ case Option::JoinedClass: case Option::CommaJoinedClass:
+ case Option::JoinedAndSeparateClass:
+ if (const char *MetaVarName = Opts.getOptionMetaVar(Id))
+ Name += MetaVarName;
+ else
+ Name += "<value>";
+ break;
+ }
+
+ return Name;
+}
+
+namespace {
+struct OptionInfo {
+ std::string Name;
+ StringRef HelpText;
+};
+} // namespace
+
+static void PrintHelpOptionList(raw_ostream &OS, StringRef Title,
+ std::vector<OptionInfo> &OptionHelp) {
+ OS << Title << ":\n";
+
+ // Find the maximum option length.
+ unsigned OptionFieldWidth = 0;
+ for (unsigned i = 0, e = OptionHelp.size(); i != e; ++i) {
+ // Limit the amount of padding we are willing to give up for alignment.
+ unsigned Length = OptionHelp[i].Name.size();
+ if (Length <= 23)
+ OptionFieldWidth = std::max(OptionFieldWidth, Length);
+ }
+
+ const unsigned InitialPad = 2;
+ for (unsigned i = 0, e = OptionHelp.size(); i != e; ++i) {
+ const std::string &Option = OptionHelp[i].Name;
+ int Pad = OptionFieldWidth - int(Option.size());
+ OS.indent(InitialPad) << Option;
+
+ // Break on long option names.
+ if (Pad < 0) {
+ OS << "\n";
+ Pad = OptionFieldWidth + InitialPad;
+ }
+ OS.indent(Pad + 1) << OptionHelp[i].HelpText << '\n';
+ }
+}
+
+static const char *getOptionHelpGroup(const OptTable &Opts, OptSpecifier Id) {
+ unsigned GroupID = Opts.getOptionGroupID(Id);
+
+ // If not in a group, return the default help group.
+ if (!GroupID)
+ return "OPTIONS";
+
+ // Abuse the help text of the option groups to store the "help group"
+ // name.
+ //
+ // FIXME: Split out option groups.
+ if (const char *GroupHelp = Opts.getOptionHelpText(GroupID))
+ return GroupHelp;
+
+ // Otherwise keep looking.
+ return getOptionHelpGroup(Opts, GroupID);
+}
+
+void OptTable::PrintHelp(raw_ostream &OS, const char *Usage, const char *Title,
+ bool ShowHidden, bool ShowAllAliases) const {
+ PrintHelp(OS, Usage, Title, /*Include*/ 0, /*Exclude*/
+ (ShowHidden ? 0 : HelpHidden), ShowAllAliases);
+}
+
+void OptTable::PrintHelp(raw_ostream &OS, const char *Usage, const char *Title,
+ unsigned FlagsToInclude, unsigned FlagsToExclude,
+ bool ShowAllAliases) const {
+ OS << "OVERVIEW: " << Title << "\n\n";
+ OS << "USAGE: " << Usage << "\n\n";
+
+ // Render help text into a map of group-name to a list of (option, help)
+ // pairs.
+ std::map<std::string, std::vector<OptionInfo>> GroupedOptionHelp;
+
+ for (unsigned Id = 1, e = getNumOptions() + 1; Id != e; ++Id) {
+ // FIXME: Split out option groups.
+ if (getOptionKind(Id) == Option::GroupClass)
+ continue;
+
+ unsigned Flags = getInfo(Id).Flags;
+ if (FlagsToInclude && !(Flags & FlagsToInclude))
+ continue;
+ if (Flags & FlagsToExclude)
+ continue;
+
+ // If an alias doesn't have a help text, show a help text for the aliased
+ // option instead.
+ const char *HelpText = getOptionHelpText(Id);
+ if (!HelpText && ShowAllAliases) {
+ const Option Alias = getOption(Id).getAlias();
+ if (Alias.isValid())
+ HelpText = getOptionHelpText(Alias.getID());
+ }
+
+ if (HelpText) {
+ const char *HelpGroup = getOptionHelpGroup(*this, Id);
+ const std::string &OptName = getOptionHelpName(*this, Id);
+ GroupedOptionHelp[HelpGroup].push_back({OptName, HelpText});
+ }
+ }
+
+ for (auto& OptionGroup : GroupedOptionHelp) {
+ if (OptionGroup.first != GroupedOptionHelp.begin()->first)
+ OS << "\n";
+ PrintHelpOptionList(OS, OptionGroup.first, OptionGroup.second);
+ }
+
+ OS.flush();
+}
diff --git a/llvm/lib/Option/Option.cpp b/llvm/lib/Option/Option.cpp
new file mode 100644
index 0000000000000..9abc9fdce4c72
--- /dev/null
+++ b/llvm/lib/Option/Option.cpp
@@ -0,0 +1,290 @@
+//===- Option.cpp - Abstract Driver Options -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstring>
+
+using namespace llvm;
+using namespace llvm::opt;
+
+Option::Option(const OptTable::Info *info, const OptTable *owner)
+ : Info(info), Owner(owner) {
+ // Multi-level aliases are not supported. This just simplifies option
+ // tracking, it is not an inherent limitation.
+ assert((!Info || !getAlias().isValid() || !getAlias().getAlias().isValid()) &&
+ "Multi-level aliases are not supported.");
+
+ if (Info && getAliasArgs()) {
+ assert(getAlias().isValid() && "Only alias options can have alias args.");
+ assert(getKind() == FlagClass && "Only Flag aliases can have alias args.");
+ assert(getAlias().getKind() != FlagClass &&
+ "Cannot provide alias args to a flag option.");
+ }
+}
+
+void Option::print(raw_ostream &O) const {
+ O << "<";
+ switch (getKind()) {
+#define P(N) case N: O << #N; break
+ P(GroupClass);
+ P(InputClass);
+ P(UnknownClass);
+ P(FlagClass);
+ P(JoinedClass);
+ P(ValuesClass);
+ P(SeparateClass);
+ P(CommaJoinedClass);
+ P(MultiArgClass);
+ P(JoinedOrSeparateClass);
+ P(JoinedAndSeparateClass);
+ P(RemainingArgsClass);
+ P(RemainingArgsJoinedClass);
+#undef P
+ }
+
+ if (Info->Prefixes) {
+ O << " Prefixes:[";
+ for (const char *const *Pre = Info->Prefixes; *Pre != nullptr; ++Pre) {
+ O << '"' << *Pre << (*(Pre + 1) == nullptr ? "\"" : "\", ");
+ }
+ O << ']';
+ }
+
+ O << " Name:\"" << getName() << '"';
+
+ const Option Group = getGroup();
+ if (Group.isValid()) {
+ O << " Group:";
+ Group.print(O);
+ }
+
+ const Option Alias = getAlias();
+ if (Alias.isValid()) {
+ O << " Alias:";
+ Alias.print(O);
+ }
+
+ if (getKind() == MultiArgClass)
+ O << " NumArgs:" << getNumArgs();
+
+ O << ">\n";
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void Option::dump() const { print(dbgs()); }
+#endif
+
+bool Option::matches(OptSpecifier Opt) const {
+ // Aliases are never considered in matching, look through them.
+ const Option Alias = getAlias();
+ if (Alias.isValid())
+ return Alias.matches(Opt);
+
+ // Check exact match.
+ if (getID() == Opt.getID())
+ return true;
+
+ const Option Group = getGroup();
+ if (Group.isValid())
+ return Group.matches(Opt);
+ return false;
+}
+
+Arg *Option::acceptInternal(const ArgList &Args, unsigned &Index,
+ unsigned ArgSize) const {
+ StringRef Spelling = StringRef(Args.getArgString(Index), ArgSize);
+ switch (getKind()) {
+ case FlagClass: {
+ if (ArgSize != strlen(Args.getArgString(Index)))
+ return nullptr;
+ return new Arg(*this, Spelling, Index++);
+ }
+ case JoinedClass: {
+ const char *Value = Args.getArgString(Index) + ArgSize;
+ return new Arg(*this, Spelling, Index++, Value);
+ }
+ case CommaJoinedClass: {
+ // Always matches.
+ const char *Str = Args.getArgString(Index) + ArgSize;
+ Arg *A = new Arg(*this, Spelling, Index++);
+
+ // Parse out the comma separated values.
+ const char *Prev = Str;
+ for (;; ++Str) {
+ char c = *Str;
+
+ if (!c || c == ',') {
+ if (Prev != Str) {
+ char *Value = new char[Str - Prev + 1];
+ memcpy(Value, Prev, Str - Prev);
+ Value[Str - Prev] = '\0';
+ A->getValues().push_back(Value);
+ }
+
+ if (!c)
+ break;
+
+ Prev = Str + 1;
+ }
+ }
+ A->setOwnsValues(true);
+
+ return A;
+ }
+ case SeparateClass:
+ // Matches iff this is an exact match.
+ // FIXME: Avoid strlen.
+ if (ArgSize != strlen(Args.getArgString(Index)))
+ return nullptr;
+
+ Index += 2;
+ if (Index > Args.getNumInputArgStrings() ||
+ Args.getArgString(Index - 1) == nullptr)
+ return nullptr;
+
+ return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
+ case MultiArgClass: {
+ // Matches iff this is an exact match.
+ // FIXME: Avoid strlen.
+ if (ArgSize != strlen(Args.getArgString(Index)))
+ return nullptr;
+
+ Index += 1 + getNumArgs();
+ if (Index > Args.getNumInputArgStrings())
+ return nullptr;
+
+ Arg *A = new Arg(*this, Spelling, Index - 1 - getNumArgs(),
+ Args.getArgString(Index - getNumArgs()));
+ for (unsigned i = 1; i != getNumArgs(); ++i)
+ A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i));
+ return A;
+ }
+ case JoinedOrSeparateClass: {
+ // If this is not an exact match, it is a joined arg.
+ // FIXME: Avoid strlen.
+ if (ArgSize != strlen(Args.getArgString(Index))) {
+ const char *Value = Args.getArgString(Index) + ArgSize;
+ return new Arg(*this, Spelling, Index++, Value);
+ }
+
+ // Otherwise it must be separate.
+ Index += 2;
+ if (Index > Args.getNumInputArgStrings() ||
+ Args.getArgString(Index - 1) == nullptr)
+ return nullptr;
+
+ return new Arg(*this, Spelling, Index - 2, Args.getArgString(Index - 1));
+ }
+ case JoinedAndSeparateClass:
+ // Always matches.
+ Index += 2;
+ if (Index > Args.getNumInputArgStrings() ||
+ Args.getArgString(Index - 1) == nullptr)
+ return nullptr;
+
+ return new Arg(*this, Spelling, Index - 2,
+ Args.getArgString(Index - 2) + ArgSize,
+ Args.getArgString(Index - 1));
+ case RemainingArgsClass: {
+ // Matches iff this is an exact match.
+ // FIXME: Avoid strlen.
+ if (ArgSize != strlen(Args.getArgString(Index)))
+ return nullptr;
+ Arg *A = new Arg(*this, Spelling, Index++);
+ while (Index < Args.getNumInputArgStrings() &&
+ Args.getArgString(Index) != nullptr)
+ A->getValues().push_back(Args.getArgString(Index++));
+ return A;
+ }
+ case RemainingArgsJoinedClass: {
+ Arg *A = new Arg(*this, Spelling, Index);
+ if (ArgSize != strlen(Args.getArgString(Index))) {
+ // An inexact match means there is a joined arg.
+ A->getValues().push_back(Args.getArgString(Index) + ArgSize);
+ }
+ Index++;
+ while (Index < Args.getNumInputArgStrings() &&
+ Args.getArgString(Index) != nullptr)
+ A->getValues().push_back(Args.getArgString(Index++));
+ return A;
+ }
+
+ default:
+ llvm_unreachable("Invalid option kind!");
+ }
+}
+
+Arg *Option::accept(const ArgList &Args,
+ unsigned &Index,
+ unsigned ArgSize) const {
+ std::unique_ptr<Arg> A(acceptInternal(Args, Index, ArgSize));
+ if (!A)
+ return nullptr;
+
+ const Option &UnaliasedOption = getUnaliasedOption();
+ if (getID() == UnaliasedOption.getID())
+ return A.release();
+
+ // "A" is an alias for a different flag. For most clients it's more convenient
+ // if this function returns unaliased Args, so create an unaliased arg for
+ // returning.
+
+ // This creates a completely new Arg object for the unaliased Arg because
+ // the alias and the unaliased arg can have different Kinds and different
+ // Values (due to AliasArgs<>).
+
+ // Get the spelling from the unaliased option.
+ StringRef UnaliasedSpelling = Args.MakeArgString(
+ Twine(UnaliasedOption.getPrefix()) + Twine(UnaliasedOption.getName()));
+
+ // It's a bit weird that aliased and unaliased arg share one index, but
+ // the index is mostly use as a memory optimization in render().
+ // Due to this, ArgList::getArgString(A->getIndex()) will return the spelling
+ // of the aliased arg always, while A->getSpelling() returns either the
+ // unaliased or the aliased arg, depending on which Arg object it's called on.
+ Arg *UnaliasedA = new Arg(UnaliasedOption, UnaliasedSpelling, A->getIndex());
+ Arg *RawA = A.get();
+ UnaliasedA->setAlias(std::move(A));
+
+ if (getKind() != FlagClass) {
+ // Values are usually owned by the ArgList. The exception are
+ // CommaJoined flags, where the Arg owns the values. For aliased flags,
+ // make the unaliased Arg the owner of the values.
+ // FIXME: There aren't many uses of CommaJoined -- try removing
+ // CommaJoined in favor of just calling StringRef::split(',') instead.
+ UnaliasedA->getValues() = RawA->getValues();
+ UnaliasedA->setOwnsValues(RawA->getOwnsValues());
+ RawA->setOwnsValues(false);
+ return UnaliasedA;
+ }
+
+ // FlagClass aliases can have AliasArgs<>; add those to the unaliased arg.
+ if (const char *Val = getAliasArgs()) {
+ while (*Val != '\0') {
+ UnaliasedA->getValues().push_back(Val);
+
+ // Move past the '\0' to the next argument.
+ Val += strlen(Val) + 1;
+ }
+ }
+ if (UnaliasedOption.getKind() == JoinedClass && !getAliasArgs())
+ // A Flag alias for a Joined option must provide an argument.
+ UnaliasedA->getValues().push_back("");
+ return UnaliasedA;
+}