[llvm] Reland "[Support]Look up in top-level subcommand as a fallback when looking options for a custom subcommand (PR #71981)

Mingming Liu via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 10 14:00:02 PST 2023


https://github.com/minglotus-6 updated https://github.com/llvm/llvm-project/pull/71981

>From 63844d6f48b99b41894c799baa408e9f83ac7212 Mon Sep 17 00:00:00 2001
From: Mingming Liu <minglotus6 at gmail.com>
Date: Fri, 10 Nov 2023 12:44:43 -0800
Subject: [PATCH 1/3] Revert "Revert "[Support]Look up in top-level subcommand
 as a fallback when looking options for a custom subcommand (#71975)"

This reverts commit 2e912a2b82dde3940e77b62832629ecf0a8b9f14.
---
 llvm/lib/Support/CommandLine.cpp           |  7 +++
 llvm/unittests/Support/CommandLineTest.cpp | 53 ++++++++++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index 55633d7cafa4791..a7e0cae8b855d7c 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -1667,6 +1667,13 @@ bool CommandLineParser::ParseCommandLineOptions(int argc,
       Handler = LookupLongOption(*ChosenSubCommand, ArgName, Value,
                                  LongOptionsUseDoubleDash, HaveDoubleDash);
 
+      // If Handler is not found in a specialized subcommand, look up handler
+      // in the top-level subcommand.
+      // cl::opt without cl::sub belongs to top-level subcommand.
+      if (!Handler && ChosenSubCommand != &SubCommand::getTopLevel())
+        Handler = LookupLongOption(SubCommand::getTopLevel(), ArgName, Value,
+                                   LongOptionsUseDoubleDash, HaveDoubleDash);
+
       // Check to see if this "option" is really a prefixed or grouped argument.
       if (!Handler && !(LongOptionsUseDoubleDash && HaveDoubleDash))
         Handler = HandlePrefixedOrGroupedOption(ArgName, Value, ErrorParsing,
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 41cc8260acfedf7..4411ed0f83928ad 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -525,6 +525,59 @@ TEST(CommandLineTest, LookupFailsInWrongSubCommand) {
   EXPECT_FALSE(Errs.empty());
 }
 
+TEST(CommandLineTest, TopLevelOptInSubcommand) {
+  enum LiteralOptionEnum {
+    foo,
+    bar,
+    baz,
+  };
+
+  cl::ResetCommandLineParser();
+
+  // This is a top-level option and not associated with a subcommand.
+  // A command line using subcommand should parse both subcommand options and
+  // top-level options.  A valid use case is that users of llvm command line
+  // tools should be able to specify top-level options defined in any library.
+  cl::opt<std::string> TopLevelOpt("str", cl::init("txt"),
+                                   cl::desc("A top-level option."));
+
+  StackSubCommand SC("sc", "Subcommand");
+  StackOption<std::string> PositionalOpt(
+      cl::Positional, cl::desc("positional argument test coverage"),
+      cl::sub(SC));
+  StackOption<LiteralOptionEnum> LiteralOpt(
+      cl::desc("literal argument test coverage"), cl::sub(SC), cl::init(bar),
+      cl::values(clEnumVal(foo, "foo"), clEnumVal(bar, "bar"),
+                 clEnumVal(baz, "baz")));
+  StackOption<bool> EnableOpt("enable", cl::sub(SC), cl::init(false));
+  StackOption<int> ThresholdOpt("threshold", cl::sub(SC), cl::init(1));
+
+  const char *PositionalOptVal = "input-file";
+  const char *args[] = {"prog",    "sc",        PositionalOptVal,
+                        "-enable", "--str=csv", "--threshold=2"};
+
+  // cl::ParseCommandLineOptions returns true on success. Otherwise, it will
+  // print the error message to stderr and exit in this setting (`Errs` ostream
+  // is not set).
+  ASSERT_TRUE(cl::ParseCommandLineOptions(sizeof(args) / sizeof(args[0]), args,
+                                          StringRef()));
+  EXPECT_STREQ(PositionalOpt.getValue().c_str(), PositionalOptVal);
+  EXPECT_TRUE(EnableOpt);
+  // Tests that the value of `str` option is `csv` as specified.
+  EXPECT_STREQ(TopLevelOpt.getValue().c_str(), "csv");
+  EXPECT_EQ(ThresholdOpt, 2);
+
+  for (auto &[LiteralOptVal, WantLiteralOpt] :
+       {std::pair{"--bar", bar}, {"--foo", foo}, {"--baz", baz}}) {
+    const char *args[] = {"prog", "sc", LiteralOptVal};
+    ASSERT_TRUE(cl::ParseCommandLineOptions(sizeof(args) / sizeof(args[0]),
+                                            args, StringRef()));
+
+    // Tests that literal options are parsed correctly.
+    EXPECT_EQ(LiteralOpt, WantLiteralOpt);
+  }
+}
+
 TEST(CommandLineTest, AddToAllSubCommands) {
   cl::ResetCommandLineParser();
 

>From 1a86a5b4493f558b6d4e50aaea46d64539426aa2 Mon Sep 17 00:00:00 2001
From: Mingming Liu <mingmingl at google.com>
Date: Fri, 10 Nov 2023 13:09:14 -0800
Subject: [PATCH 2/3] Update CommandLineTest.cpp

---
 llvm/unittests/Support/CommandLineTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 4411ed0f83928ad..b6542363dc61e3b 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -538,7 +538,7 @@ TEST(CommandLineTest, TopLevelOptInSubcommand) {
   // A command line using subcommand should parse both subcommand options and
   // top-level options.  A valid use case is that users of llvm command line
   // tools should be able to specify top-level options defined in any library.
-  cl::opt<std::string> TopLevelOpt("str", cl::init("txt"),
+  StackOption<std::string> TopLevelOpt("str", cl::init("txt"),
                                    cl::desc("A top-level option."));
 
   StackSubCommand SC("sc", "Subcommand");

>From fc33a5a537c2b4feb42104711d4ae84c0363de60 Mon Sep 17 00:00:00 2001
From: Mingming Liu <mingmingl at google.com>
Date: Fri, 10 Nov 2023 13:59:56 -0800
Subject: [PATCH 3/3] Apply clang-format chnage.

---
 llvm/unittests/Support/CommandLineTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index b6542363dc61e3b..381fe70b6b48156 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -539,7 +539,7 @@ TEST(CommandLineTest, TopLevelOptInSubcommand) {
   // top-level options.  A valid use case is that users of llvm command line
   // tools should be able to specify top-level options defined in any library.
   StackOption<std::string> TopLevelOpt("str", cl::init("txt"),
-                                   cl::desc("A top-level option."));
+                                       cl::desc("A top-level option."));
 
   StackSubCommand SC("sc", "Subcommand");
   StackOption<std::string> PositionalOpt(



More information about the llvm-commits mailing list