[llvm] 9d66d26 - [CommandLine] Show '[subcommand]' in the help for less than 3 subcommands (#74557)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 8 10:32:10 PST 2023
Author: Igor Kudrin
Date: 2023-12-09T01:32:07+07:00
New Revision: 9d66d263ad4371160320f4f91720a345eb241471
URL: https://github.com/llvm/llvm-project/commit/9d66d263ad4371160320f4f91720a345eb241471
DIFF: https://github.com/llvm/llvm-project/commit/9d66d263ad4371160320f4f91720a345eb241471.diff
LOG: [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.
Added:
Modified:
llvm/lib/Support/CommandLine.cpp
llvm/unittests/Support/CommandLineTest.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index a7e0cae8b855d..31f79972125da 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -2372,7 +2372,7 @@ class HelpPrinter {
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 381fe70b6b481..a7db564e5fa24 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
More information about the llvm-commits
mailing list