aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/Option
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests/Option')
-rw-r--r--llvm/unittests/Option/CMakeLists.txt6
-rw-r--r--llvm/unittests/Option/OptionMarshallingTest.cpp5
-rw-r--r--llvm/unittests/Option/OptionSubCommandsTest.cpp252
-rw-r--r--llvm/unittests/Option/SubCommandOpts.td16
4 files changed, 276 insertions, 3 deletions
diff --git a/llvm/unittests/Option/CMakeLists.txt b/llvm/unittests/Option/CMakeLists.txt
index 7be4300..5fefb5e 100644
--- a/llvm/unittests/Option/CMakeLists.txt
+++ b/llvm/unittests/Option/CMakeLists.txt
@@ -4,11 +4,15 @@ set(LLVM_LINK_COMPONENTS
)
set(LLVM_TARGET_DEFINITIONS Opts.td)
-
tablegen(LLVM Opts.inc -gen-opt-parser-defs)
+
+set(LLVM_TARGET_DEFINITIONS SubCommandOpts.td)
+tablegen(LLVM SubCommandOpts.inc -gen-opt-parser-defs)
+
add_public_tablegen_target(OptsTestTableGen)
add_llvm_unittest(OptionTests
OptionParsingTest.cpp
OptionMarshallingTest.cpp
+ OptionSubCommandsTest.cpp
)
diff --git a/llvm/unittests/Option/OptionMarshallingTest.cpp b/llvm/unittests/Option/OptionMarshallingTest.cpp
index 005144b..15917cc 100644
--- a/llvm/unittests/Option/OptionMarshallingTest.cpp
+++ b/llvm/unittests/Option/OptionMarshallingTest.cpp
@@ -29,8 +29,9 @@ static const OptionWithMarshallingInfo MarshallingTable[] = {
#define OPTION_WITH_MARSHALLING( \
PREFIX_TYPE, PREFIXED_NAME_OFFSET, ID, KIND, GROUP, ALIAS, ALIASARGS, \
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
- SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, \
- IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
+ SUBCOMMANDIDS_OFFSET, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
+ IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, \
+ TABLE_INDEX) \
{PREFIXED_NAME_OFFSET, #KEYPATH, #IMPLIED_CHECK, #IMPLIED_VALUE},
#include "Opts.inc"
#undef OPTION_WITH_MARSHALLING
diff --git a/llvm/unittests/Option/OptionSubCommandsTest.cpp b/llvm/unittests/Option/OptionSubCommandsTest.cpp
new file mode 100644
index 0000000..e31a326
--- /dev/null
+++ b/llvm/unittests/Option/OptionSubCommandsTest.cpp
@@ -0,0 +1,252 @@
+//===- unittest/Support/OptionParsingTest.cpp - OptTable tests ------------===//
+//
+// 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/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::opt;
+
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+namespace {
+enum ID {
+ OPT_INVALID = 0,
+#define OPTION(PREFIXES, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
+ VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
+ VALUES, SUBCOMMANDIDS_OFFSET) \
+ OPT_##ID,
+#include "SubCommandOpts.inc"
+#undef OPTION
+};
+#define OPTTABLE_STR_TABLE_CODE
+#include "SubCommandOpts.inc"
+#undef OPTTABLE_STR_TABLE_CODE
+
+#define OPTTABLE_PREFIXES_TABLE_CODE
+#include "SubCommandOpts.inc"
+#undef OPTTABLE_PREFIXES_TABLE_CODE
+
+#define OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
+#include "SubCommandOpts.inc"
+#undef OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
+
+#define OPTTABLE_SUBCOMMANDS_CODE
+#include "SubCommandOpts.inc"
+#undef OPTTABLE_SUBCOMMANDS_CODE
+
+static constexpr OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "SubCommandOpts.inc"
+#undef OPTION
+};
+
+class TestOptSubCommandTable : public GenericOptTable {
+public:
+ TestOptSubCommandTable(bool IgnoreCase = false)
+ : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
+ /*IgnoreCase=*/false, OptionSubCommands,
+ OptionSubCommandIDsTable) {}
+};
+
+// Test fixture
+template <typename T> class OptSubCommandTableTest : public ::testing::Test {};
+
+// Test both precomputed and computed OptTables with the same suite of tests.
+using OptSubCommandTableTestTypes = ::testing::Types<TestOptSubCommandTable>;
+
+TYPED_TEST_SUITE(OptSubCommandTableTest, OptSubCommandTableTestTypes, );
+
+TYPED_TEST(OptSubCommandTableTest, SubCommandParsing) {
+ TypeParam T;
+ unsigned MAI, MAC;
+
+ std::string ErrMsg;
+ raw_string_ostream RSO1(ErrMsg);
+
+ auto HandleMultipleSubcommands = [&](ArrayRef<StringRef> SubCommands) {
+ ErrMsg.clear();
+ RSO1 << "Multiple subcommands passed\n";
+ for (auto SC : SubCommands)
+ RSO1 << "\n" << SC;
+ };
+
+ auto HandleOtherPositionals = [&](ArrayRef<StringRef> Positionals) {
+ ErrMsg.clear();
+ RSO1 << "Unregistered positionals passed\n";
+ for (auto SC : Positionals)
+ RSO1 << "\n" << SC;
+ };
+
+ {
+ // Test case 1: Toplevel option, no subcommand
+ const char *Args[] = {"-version"};
+ InputArgList AL = T.ParseArgs(Args, MAI, MAC);
+ EXPECT_TRUE(AL.hasArg(OPT_version));
+ StringRef SC = AL.getSubCommand(
+ T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ EXPECT_TRUE(SC.empty());
+ EXPECT_FALSE(AL.hasArg(OPT_uppercase));
+ EXPECT_FALSE(AL.hasArg(OPT_lowercase));
+ }
+
+ {
+ // Test case 2: Subcommand 'foo' with its valid options
+ const char *Args[] = {"foo", "-uppercase"};
+ InputArgList AL = T.ParseArgs(Args, MAI, MAC);
+ StringRef SC = AL.getSubCommand(
+ T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ EXPECT_EQ(SC, "foo");
+ EXPECT_TRUE(AL.hasArg(OPT_uppercase));
+ EXPECT_FALSE(AL.hasArg(OPT_lowercase));
+ EXPECT_FALSE(AL.hasArg(OPT_version));
+ EXPECT_EQ(std::string::npos, ErrMsg.find("Multiple subcommands passed"))
+ << "Did not expect error message as this is a valid use case.";
+ EXPECT_EQ(std::string::npos, ErrMsg.find("Unregistered positionals passed"))
+ << "Did not expect error message as this is a valid use case.";
+ }
+
+ {
+ // Test case 3: Check valid use of subcommand which follows a valid
+ // subcommand option.
+ const char *Args[] = {"-uppercase", "foo"};
+ InputArgList AL = T.ParseArgs(Args, MAI, MAC);
+ StringRef SC = AL.getSubCommand(
+ T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ EXPECT_EQ(SC, "foo");
+ EXPECT_TRUE(AL.hasArg(OPT_uppercase));
+ EXPECT_FALSE(AL.hasArg(OPT_lowercase));
+ EXPECT_FALSE(AL.hasArg(OPT_version));
+ EXPECT_EQ(std::string::npos, ErrMsg.find("Multiple subcommands passed"))
+ << "Did not expect error message as this is a valid use case.";
+ EXPECT_EQ(std::string::npos, ErrMsg.find("Unregistered positionals passed"))
+ << "Did not expect error message as this is a valid use case.";
+ }
+
+ {
+ // Test case 4: Check invalid use of passing multiple subcommands.
+ const char *Args[] = {"-uppercase", "foo", "bar"};
+ InputArgList AL = T.ParseArgs(Args, MAI, MAC);
+ StringRef SC = AL.getSubCommand(
+ T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ // No valid subcommand should be returned as this is an invalid invocation.
+ EXPECT_TRUE(SC.empty());
+ // Expect the multiple subcommands error message.
+ EXPECT_NE(std::string::npos, ErrMsg.find("Multiple subcommands passed"));
+ EXPECT_NE(std::string::npos, ErrMsg.find("foo"));
+ EXPECT_NE(std::string::npos, ErrMsg.find("bar"));
+ EXPECT_EQ(std::string::npos, ErrMsg.find("Unregistered positionals passed"))
+ << "Did not expect error message as this is a valid use case.";
+ }
+
+ {
+ // Test case 5: Check invalid use of passing unregistered subcommands.
+ const char *Args[] = {"foobar"};
+ InputArgList AL = T.ParseArgs(Args, MAI, MAC);
+ StringRef SC = AL.getSubCommand(
+ T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ // No valid subcommand should be returned as this is an invalid invocation.
+ EXPECT_TRUE(SC.empty());
+ // Expect the unregistered subcommands error message.
+ EXPECT_NE(std::string::npos,
+ ErrMsg.find("Unregistered positionals passed"));
+ EXPECT_NE(std::string::npos, ErrMsg.find("foobar"));
+ }
+
+ {
+ // Test case 6: Check invalid use of a valid subcommand which follows a
+ // valid subcommand option but the option is not registered with the given
+ // subcommand.
+ const char *Args[] = {"-lowercase", "bar"};
+ InputArgList AL = T.ParseArgs(Args, MAI, MAC);
+ StringRef SC = AL.getSubCommand(
+ T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ auto HandleSubCommandArg = [&](ID OptionType) {
+ if (!AL.hasArg(OptionType))
+ return false;
+ auto O = T.getOption(OptionType);
+ if (!O.isRegisteredSC(SC)) {
+ ErrMsg.clear();
+ RSO1 << "Option [" << O.getName() << "] is not valid for SubCommand ["
+ << SC << "]\n";
+ return false;
+ }
+ return true;
+ };
+ EXPECT_EQ(SC, "bar"); // valid subcommand
+ EXPECT_TRUE(AL.hasArg(OPT_lowercase)); // valid option
+ EXPECT_FALSE(HandleSubCommandArg(OPT_lowercase));
+ EXPECT_NE(
+ std::string::npos,
+ ErrMsg.find("Option [lowercase] is not valid for SubCommand [bar]"));
+ }
+}
+
+TYPED_TEST(OptSubCommandTableTest, SubCommandHelp) {
+ TypeParam T;
+ std::string Help;
+ raw_string_ostream RSO(Help);
+
+ // Toplevel help
+ T.printHelp(RSO, "Test Usage String", "OverviewString");
+ EXPECT_NE(std::string::npos, Help.find("OVERVIEW:"));
+ EXPECT_NE(std::string::npos, Help.find("OverviewString"));
+ EXPECT_NE(std::string::npos, Help.find("USAGE:"));
+ EXPECT_NE(std::string::npos, Help.find("Test Usage String"));
+ EXPECT_NE(std::string::npos, Help.find("SUBCOMMANDS:"));
+ EXPECT_NE(std::string::npos, Help.find("foo"));
+ EXPECT_NE(std::string::npos, Help.find("bar"));
+ EXPECT_NE(std::string::npos, Help.find("HelpText for SubCommand foo."));
+ EXPECT_NE(std::string::npos, Help.find("HelpText for SubCommand bar."));
+ EXPECT_NE(std::string::npos, Help.find("OPTIONS:"));
+ EXPECT_NE(std::string::npos, Help.find("--help"));
+ EXPECT_NE(std::string::npos, Help.find("-version"));
+ // uppercase is not a global option and should not be shown.
+ EXPECT_EQ(std::string::npos, Help.find("-uppercase"));
+
+ // Help for subcommand foo
+ Help.clear();
+ StringRef SC1 = "foo";
+ T.printHelp(RSO, "Test Usage String", "OverviewString", false, false,
+ Visibility(), SC1);
+ EXPECT_NE(std::string::npos, Help.find("OVERVIEW:"));
+ EXPECT_NE(std::string::npos, Help.find("OverviewString"));
+ // SubCommand "foo" definition for tablegen has NO dedicated usage string so
+ // not expected to see USAGE.
+ EXPECT_EQ(std::string::npos, Help.find("USAGE:"));
+ EXPECT_NE(std::string::npos, Help.find("HelpText for SubCommand foo."));
+ EXPECT_NE(std::string::npos, Help.find("-uppercase"));
+ EXPECT_NE(std::string::npos, Help.find("-lowercase"));
+ EXPECT_EQ(std::string::npos, Help.find("-version"));
+ EXPECT_EQ(std::string::npos, Help.find("SUBCOMMANDS:"));
+
+ // Help for subcommand bar
+ Help.clear();
+ StringRef SC2 = "bar";
+ T.printHelp(RSO, "Test Usage String", "OverviewString", false, false,
+ Visibility(), SC2);
+ EXPECT_NE(std::string::npos, Help.find("OVERVIEW:"));
+ EXPECT_NE(std::string::npos, Help.find("OverviewString"));
+ // SubCommand "bar" definition for tablegen has a dedicated usage string.
+ EXPECT_NE(std::string::npos, Help.find("USAGE:"));
+ EXPECT_NE(std::string::npos, Help.find("Subcommand bar <options>"));
+ EXPECT_NE(std::string::npos, Help.find("HelpText for SubCommand bar."));
+ EXPECT_NE(std::string::npos, Help.find("-uppercase"));
+ // lowercase is not an option for bar and should not be shown.
+ EXPECT_EQ(std::string::npos, Help.find("-lowercase"));
+ // version is a global option and should not be shown.
+ EXPECT_EQ(std::string::npos, Help.find("-version"));
+}
+} // end anonymous namespace
diff --git a/llvm/unittests/Option/SubCommandOpts.td b/llvm/unittests/Option/SubCommandOpts.td
new file mode 100644
index 0000000..b9750da
--- /dev/null
+++ b/llvm/unittests/Option/SubCommandOpts.td
@@ -0,0 +1,16 @@
+include "llvm/Option/OptParser.td"
+
+def sc_foo : SubCommand<"foo", "HelpText for SubCommand foo.">;
+
+def sc_bar : SubCommand<"bar", "HelpText for SubCommand bar.",
+ "Subcommand bar <options>">;
+
+def help : Flag<["--"], "help">, HelpText<"Subcommand <subcommand> <options>">;
+
+def version : Flag<["-"], "version">, HelpText<"Display the version number">;
+
+def uppercase : Flag<["-"], "uppercase", [sc_foo, sc_bar]>,
+ HelpText<"Print in uppercase">;
+
+def lowercase : Flag<["-"], "lowercase", [sc_foo]>,
+ HelpText<"Print in lowercase">;