Option parsing: add support for alias arguments.
authorHans Wennborg <hans@hanshq.net>
Wed, 31 Jul 2013 22:44:41 +0000 (22:44 +0000)
committerHans Wennborg <hans@hanshq.net>
Wed, 31 Jul 2013 22:44:41 +0000 (22:44 +0000)
This makes option aliases more powerful by enabling them to
pass along arguments to the option they're aliasing.

For example, if we have a joined option "-foo=", we can now
specify a flag option "-bar" to be an alias of that, with the
argument "baz".

This is especially useful for the cl.exe compatible clang driver,
where many options are aliases. For example, this patch enables
us to alias "/Ox" to "-O3" (-O is a joined option), and "/WX" to
"-Werror" (again, -W is a joined option).

Differential Revision: http://llvm-reviews.chandlerc.com/D1245

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@187537 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Option/OptParser.td
include/llvm/Option/OptTable.h
include/llvm/Option/Option.h
lib/Option/Option.cpp
unittests/Option/OptionParsingTest.cpp
unittests/Option/Opts.td
utils/TableGen/OptParserEmitter.cpp

index 394e0b6..32cc2c0 100644 (file)
@@ -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; }
index 8ee219d..a5b59ce 100644 (file)
@@ -44,6 +44,7 @@ public:
     unsigned short Flags;
     unsigned short GroupID;
     unsigned short AliasID;
+    const char *AliasArgs;
   };
 
 private:
index 4861b59..47fd817 100644 (file)
@@ -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;
index 5b418e8..1d6a3d3 100644 (file)
@@ -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);
index 2c4fdcf..101568a 100644 (file)
 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");
+}
index 8e33ba8..986b312 100644 (file)
@@ -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"]>;
index 0c1f623..d37939f 100644 (file)
@@ -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