aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Kudrin <ikudrin@accesssoftek.com>2023-12-09 01:32:07 +0700
committerGitHub <noreply@github.com>2023-12-09 01:32:07 +0700
commit9d66d263ad4371160320f4f91720a345eb241471 (patch)
tree3592ed3081e4c6b6e6f428dcc35589ed839f08b1
parenta5891fa4d2b76cf9dec96da9ded59fc4937d3342 (diff)
downloadllvm-9d66d263ad4371160320f4f91720a345eb241471.zip
llvm-9d66d263ad4371160320f4f91720a345eb241471.tar.gz
llvm-9d66d263ad4371160320f4f91720a345eb241471.tar.bz2
[CommandLine] Show '[subcommand]' in the help for less than 3 subcommands (#74557)
When a tool defines only one or two subcommands, the `[subcommand]` part is not displayed in the `USAGE` help line. Note that a similar issue for printing the list of the subcommands has been fixed in https://reviews.llvm.org/D25463.
-rw-r--r--llvm/lib/Support/CommandLine.cpp2
-rw-r--r--llvm/unittests/Support/CommandLineTest.cpp73
2 files changed, 57 insertions, 18 deletions
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index a7e0cae..31f7997 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -2372,7 +2372,7 @@ public:
if (Sub == &SubCommand::getTopLevel()) {
outs() << "USAGE: " << GlobalParser->ProgramName;
- if (Subs.size() > 2)
+ if (!Subs.empty())
outs() << " [subcommand]";
outs() << " [options]";
} else {
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 381fe70..a7db564 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -1347,29 +1347,32 @@ struct AutoDeleteFile {
}
};
+static std::string interceptStdout(std::function<void()> F) {
+ outs().flush(); // flush any output from previous tests
+ AutoDeleteFile File;
+ {
+ OutputRedirector Stdout(fileno(stdout));
+ if (!Stdout.Valid)
+ return "";
+ File.FilePath = Stdout.FilePath;
+ F();
+ outs().flush();
+ }
+ auto Buffer = MemoryBuffer::getFile(File.FilePath);
+ if (!Buffer)
+ return "";
+ return Buffer->get()->getBuffer().str();
+}
+
template <void (*Func)(const cl::Option &)>
class PrintOptionTestBase : public ::testing::Test {
public:
// Return std::string because the output of a failing EXPECT check is
// unreadable for StringRef. It also avoids any lifetime issues.
template <typename... Ts> std::string runTest(Ts... OptionAttributes) {
- outs().flush(); // flush any output from previous tests
- AutoDeleteFile File;
- {
- OutputRedirector Stdout(fileno(stdout));
- if (!Stdout.Valid)
- return "";
- File.FilePath = Stdout.FilePath;
-
- StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText),
- OptionAttributes...);
- Func(TestOption);
- outs().flush();
- }
- auto Buffer = MemoryBuffer::getFile(File.FilePath);
- if (!Buffer)
- return "";
- return Buffer->get()->getBuffer().str();
+ StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText),
+ OptionAttributes...);
+ return interceptStdout([&]() { Func(TestOption); });
}
enum class OptionValue { Val };
@@ -2206,4 +2209,40 @@ TEST(CommandLineTest, DefaultValue) {
EXPECT_EQ(1, StrInitOption.getNumOccurrences());
}
+TEST(CommandLineTest, HelpWithoutSubcommands) {
+ // Check that the help message does not contain the "[subcommand]" placeholder
+ // and the "SUBCOMMANDS" section if there are no subcommands.
+ cl::ResetCommandLineParser();
+ StackOption<bool> Opt("opt", cl::init(false));
+ const char *args[] = {"prog"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),
+ &llvm::nulls()));
+ auto Output = interceptStdout([]() { cl::PrintHelpMessage(); });
+ EXPECT_NE(std::string::npos, Output.find("USAGE: prog [options]")) << Output;
+ EXPECT_EQ(std::string::npos, Output.find("SUBCOMMANDS:")) << Output;
+ cl::ResetCommandLineParser();
+}
+
+TEST(CommandLineTest, HelpWithSubcommands) {
+ // Check that the help message contains the "[subcommand]" placeholder in the
+ // "USAGE" line and describes subcommands.
+ cl::ResetCommandLineParser();
+ StackSubCommand SC1("sc1", "First Subcommand");
+ StackSubCommand SC2("sc2", "Second Subcommand");
+ StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
+ StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
+ const char *args[] = {"prog"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),
+ &llvm::nulls()));
+ auto Output = interceptStdout([]() { cl::PrintHelpMessage(); });
+ EXPECT_NE(std::string::npos,
+ Output.find("USAGE: prog [subcommand] [options]"))
+ << Output;
+ EXPECT_NE(std::string::npos, Output.find("SUBCOMMANDS:")) << Output;
+ EXPECT_NE(std::string::npos, Output.find("sc1 - First Subcommand")) << Output;
+ EXPECT_NE(std::string::npos, Output.find("sc2 - Second Subcommand"))
+ << Output;
+ cl::ResetCommandLineParser();
+}
+
} // anonymous namespace