Skip to content

Commit 9d66d26

Browse files
authored
[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.
1 parent a5891fa commit 9d66d26

File tree

2 files changed

+57
-18
lines changed

2 files changed

+57
-18
lines changed

llvm/lib/Support/CommandLine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2372,7 +2372,7 @@ class HelpPrinter {
23722372

23732373
if (Sub == &SubCommand::getTopLevel()) {
23742374
outs() << "USAGE: " << GlobalParser->ProgramName;
2375-
if (Subs.size() > 2)
2375+
if (!Subs.empty())
23762376
outs() << " [subcommand]";
23772377
outs() << " [options]";
23782378
} else {

llvm/unittests/Support/CommandLineTest.cpp

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,29 +1347,32 @@ struct AutoDeleteFile {
13471347
}
13481348
};
13491349

1350+
static std::string interceptStdout(std::function<void()> F) {
1351+
outs().flush(); // flush any output from previous tests
1352+
AutoDeleteFile File;
1353+
{
1354+
OutputRedirector Stdout(fileno(stdout));
1355+
if (!Stdout.Valid)
1356+
return "";
1357+
File.FilePath = Stdout.FilePath;
1358+
F();
1359+
outs().flush();
1360+
}
1361+
auto Buffer = MemoryBuffer::getFile(File.FilePath);
1362+
if (!Buffer)
1363+
return "";
1364+
return Buffer->get()->getBuffer().str();
1365+
}
1366+
13501367
template <void (*Func)(const cl::Option &)>
13511368
class PrintOptionTestBase : public ::testing::Test {
13521369
public:
13531370
// Return std::string because the output of a failing EXPECT check is
13541371
// unreadable for StringRef. It also avoids any lifetime issues.
13551372
template <typename... Ts> std::string runTest(Ts... OptionAttributes) {
1356-
outs().flush(); // flush any output from previous tests
1357-
AutoDeleteFile File;
1358-
{
1359-
OutputRedirector Stdout(fileno(stdout));
1360-
if (!Stdout.Valid)
1361-
return "";
1362-
File.FilePath = Stdout.FilePath;
1363-
1364-
StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText),
1365-
OptionAttributes...);
1366-
Func(TestOption);
1367-
outs().flush();
1368-
}
1369-
auto Buffer = MemoryBuffer::getFile(File.FilePath);
1370-
if (!Buffer)
1371-
return "";
1372-
return Buffer->get()->getBuffer().str();
1373+
StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText),
1374+
OptionAttributes...);
1375+
return interceptStdout([&]() { Func(TestOption); });
13731376
}
13741377

13751378
enum class OptionValue { Val };
@@ -2206,4 +2209,40 @@ TEST(CommandLineTest, DefaultValue) {
22062209
EXPECT_EQ(1, StrInitOption.getNumOccurrences());
22072210
}
22082211

2212+
TEST(CommandLineTest, HelpWithoutSubcommands) {
2213+
// Check that the help message does not contain the "[subcommand]" placeholder
2214+
// and the "SUBCOMMANDS" section if there are no subcommands.
2215+
cl::ResetCommandLineParser();
2216+
StackOption<bool> Opt("opt", cl::init(false));
2217+
const char *args[] = {"prog"};
2218+
EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),
2219+
&llvm::nulls()));
2220+
auto Output = interceptStdout([]() { cl::PrintHelpMessage(); });
2221+
EXPECT_NE(std::string::npos, Output.find("USAGE: prog [options]")) << Output;
2222+
EXPECT_EQ(std::string::npos, Output.find("SUBCOMMANDS:")) << Output;
2223+
cl::ResetCommandLineParser();
2224+
}
2225+
2226+
TEST(CommandLineTest, HelpWithSubcommands) {
2227+
// Check that the help message contains the "[subcommand]" placeholder in the
2228+
// "USAGE" line and describes subcommands.
2229+
cl::ResetCommandLineParser();
2230+
StackSubCommand SC1("sc1", "First Subcommand");
2231+
StackSubCommand SC2("sc2", "Second Subcommand");
2232+
StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
2233+
StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
2234+
const char *args[] = {"prog"};
2235+
EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(args), args, StringRef(),
2236+
&llvm::nulls()));
2237+
auto Output = interceptStdout([]() { cl::PrintHelpMessage(); });
2238+
EXPECT_NE(std::string::npos,
2239+
Output.find("USAGE: prog [subcommand] [options]"))
2240+
<< Output;
2241+
EXPECT_NE(std::string::npos, Output.find("SUBCOMMANDS:")) << Output;
2242+
EXPECT_NE(std::string::npos, Output.find("sc1 - First Subcommand")) << Output;
2243+
EXPECT_NE(std::string::npos, Output.find("sc2 - Second Subcommand"))
2244+
<< Output;
2245+
cl::ResetCommandLineParser();
2246+
}
2247+
22092248
} // anonymous namespace

0 commit comments

Comments
 (0)