summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/Option/OptParser.td2
-rw-r--r--include/llvm/Option/OptTable.h1
-rw-r--r--include/llvm/Option/Option.h10
-rw-r--r--lib/Option/Option.cpp22
-rw-r--r--unittests/Option/OptionParsingTest.cpp19
-rw-r--r--unittests/Option/Opts.td3
-rw-r--r--utils/TableGen/OptParserEmitter.cpp33
7 files changed, 82 insertions, 8 deletions
diff --git a/include/llvm/Option/OptParser.td b/include/llvm/Option/OptParser.td
index 394e0b6a4b..32cc2c0cd5 100644
--- a/include/llvm/Option/OptParser.td
+++ b/include/llvm/Option/OptParser.td
@@ -89,6 +89,7 @@ class Option<list<string> prefixes, string name, OptionKind kind> {
list<OptionFlag> Flags = [];
OptionGroup Group = ?;
Option Alias = ?;
+ list<string> AliasArgs = [];
}
// Helpers for defining options.
@@ -113,6 +114,7 @@ class JoinedAndSeparate<list<string> prefixes, string name>
// Mix-ins for adding optional attributes.
class Alias<Option alias> { Option Alias = alias; }
+class AliasArgs<list<string> aliasargs> { list<string> AliasArgs = aliasargs; }
class EnumName<string name> { string EnumName = name; }
class Flags<list<OptionFlag> flags> { list<OptionFlag> Flags = flags; }
class Group<OptionGroup group> { OptionGroup Group = group; }
diff --git a/include/llvm/Option/OptTable.h b/include/llvm/Option/OptTable.h
index 8ee219d37b..a5b59cea3f 100644
--- a/include/llvm/Option/OptTable.h
+++ b/include/llvm/Option/OptTable.h
@@ -44,6 +44,7 @@ public:
unsigned short Flags;
unsigned short GroupID;
unsigned short AliasID;
+ const char *AliasArgs;
};
private:
diff --git a/include/llvm/Option/Option.h b/include/llvm/Option/Option.h
index 4861b59726..47fd8174c5 100644
--- a/include/llvm/Option/Option.h
+++ b/include/llvm/Option/Option.h
@@ -103,6 +103,16 @@ public:
return Owner->getOption(Info->AliasID);
}
+ /// \brief Get the alias arguments as a \0 separated list.
+ /// E.g. ["foo", "bar"] would be returned as "foo\0bar\0".
+ const char *getAliasArgs() const {
+ assert(Info && "Must have a valid info!");
+ assert((!Info->AliasArgs || Info->AliasArgs[0] != 0) &&
+ "AliasArgs should be either 0 or non-empty.");
+
+ return Info->AliasArgs;
+ }
+
/// \brief Get the default prefix for this option.
StringRef getPrefix() const {
const char *Prefix = *Info->Prefixes;
diff --git a/lib/Option/Option.cpp b/lib/Option/Option.cpp
index 5b418e845e..1d6a3d3804 100644
--- a/lib/Option/Option.cpp
+++ b/lib/Option/Option.cpp
@@ -26,6 +26,13 @@ Option::Option(const OptTable::Info *info, const OptTable *owner)
// 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.");
+ }
}
Option::~Option() {
@@ -106,11 +113,22 @@ Arg *Option::accept(const ArgList &Args,
}
switch (getKind()) {
- case FlagClass:
+ case FlagClass: {
if (ArgSize != strlen(Args.getArgString(Index)))
return 0;
- return new Arg(UnaliasedOption, Spelling, Index++);
+ Arg *A = new Arg(UnaliasedOption, Spelling, Index++);
+ if (getAliasArgs()) {
+ const char *Val = getAliasArgs();
+ while (*Val != '\0') {
+ A->getValues().push_back(Val);
+
+ // Move past the '\0' to the next argument.
+ Val += strlen(Val) + 1;
+ }
+ }
+ return A;
+ }
case JoinedClass: {
const char *Value = Args.getArgString(Index) + ArgSize;
return new Arg(UnaliasedOption, Spelling, Index++, Value);
diff --git a/unittests/Option/OptionParsingTest.cpp b/unittests/Option/OptionParsingTest.cpp
index 2c4fdcfa2c..101568a567 100644
--- a/unittests/Option/OptionParsingTest.cpp
+++ b/unittests/Option/OptionParsingTest.cpp
@@ -17,9 +17,11 @@
using namespace llvm;
using namespace llvm::opt;
+#define SUPPORT_ALIASARGS // FIXME: Remove when no longer necessary.
+
enum ID {
OPT_INVALID = 0, // This is not an option ID.
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) OPT_##ID,
#include "Opts.inc"
LastOption
@@ -37,10 +39,10 @@ enum OptionFlags {
};
static const OptTable::Info InfoTable[] = {
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, PARAM, \
- FLAGS, OPT_##GROUP, OPT_##ALIAS },
+ FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
#include "Opts.inc"
#undef OPTION
};
@@ -145,3 +147,14 @@ TEST(Option, ParseAliasInGroup) {
OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC));
EXPECT_TRUE(AL->hasArg(OPT_H));
}
+
+TEST(Option, AliasArgs) {
+ TestOptTable T;
+ unsigned MAI, MAC;
+
+ const char *MyArgs[] = { "-J", "-Joo" };
+ OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC));
+ EXPECT_TRUE(AL->hasArg(OPT_B));
+ EXPECT_EQ(AL->getAllArgValues(OPT_B)[0], "foo");
+ EXPECT_EQ(AL->getAllArgValues(OPT_B)[1], "bar");
+}
diff --git a/unittests/Option/Opts.td b/unittests/Option/Opts.td
index 8e33ba8580..986b3122af 100644
--- a/unittests/Option/Opts.td
+++ b/unittests/Option/Opts.td
@@ -19,3 +19,6 @@ def H : Flag<["-"], "H">, Flags<[HelpHidden]>;
def my_group : OptionGroup<"my group">;
def I : Flag<["-"], "I">, Alias<H>, Group<my_group>;
+
+def J : Flag<["-"], "J">, Alias<B>, AliasArgs<["foo"]>;
+def Joo : Flag<["-"], "Joo">, Alias<B>, AliasArgs<["bar"]>;
diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp
index 0c1f6236e0..d37939f635 100644
--- a/utils/TableGen/OptParserEmitter.cpp
+++ b/utils/TableGen/OptParserEmitter.cpp
@@ -152,11 +152,22 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "/////////\n";
OS << "// Groups\n\n";
OS << "#ifdef OPTION\n";
+
+ // FIXME: Remove when option parsing clients are updated.
+ OS << "#ifdef SUPPORT_ALIASARGS\n";
+ OS << "#define OPTIONX OPTION\n";
+ OS << "#else\n";
+ OS << "#define OPTIONX(prefix, name, id, kind, group, alias, aliasargs, "
+ << "flags, param, helptext, metavar) "
+ << "OPTION(prefix, name, id, kind, "
+ << "group, alias, flags, param, helptext, metavar)\n";
+ OS << "#endif\n";
+
for (unsigned i = 0, e = Groups.size(); i != e; ++i) {
const Record &R = *Groups[i];
// Start a single option entry.
- OS << "OPTION(";
+ OS << "OPTIONX(";
// The option prefix;
OS << "0";
@@ -178,7 +189,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "INVALID";
// The other option arguments (unused for groups).
- OS << ", INVALID, 0, 0";
+ OS << ", INVALID, 0, 0, 0";
// The option help text.
if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
@@ -199,7 +210,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
const Record &R = *Opts[i];
// Start a single option entry.
- OS << "OPTION(";
+ OS << "OPTIONX(";
// The option prefix;
std::vector<std::string> prf = R.getValueAsListOfStrings("Prefixes");
@@ -228,6 +239,21 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
else
OS << "INVALID";
+ // The option alias arguments (if any).
+ // Emitted as a \0 separated list in a string, e.g. ["foo", "bar"]
+ // would become "foo\0bar\0". Note that the compiler adds an implicit
+ // terminating \0 at the end.
+ OS << ", ";
+ std::vector<std::string> AliasArgs = R.getValueAsListOfStrings("AliasArgs");
+ if (AliasArgs.size() == 0) {
+ OS << "0";
+ } else {
+ OS << "\"";
+ for (size_t i = 0, e = AliasArgs.size(); i != e; ++i)
+ OS << AliasArgs[i] << "\\0";
+ OS << "\"";
+ }
+
// The option flags.
const ListInit *LI = R.getValueAsListInit("Flags");
if (LI->empty()) {
@@ -261,6 +287,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << ")\n";
}
+ OS << "#undef OPTIONX\n"; // FIXME: Remove when option clients are updated.
OS << "#endif\n";
}
} // end namespace llvm