481 changes: 383 additions & 98 deletions llvm/lib/Support/CommandLine.cpp

Large diffs are not rendered by default.

216 changes: 213 additions & 3 deletions llvm/unittests/Support/CommandLineTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ class StackOption : public cl::opt<T> {
: Base(M0, M1, M2, M3) {}

~StackOption() override { this->removeArgument(); }

template <class DT> StackOption<T> &operator=(const DT &V) {
this->setValue(V);
return *this;
}
};

class StackSubCommand : public cl::SubCommand {
public:
StackSubCommand(const char *const Name,
const char *const Description = nullptr)
: SubCommand(Name, Description) {}

StackSubCommand() : SubCommand() {}

~StackSubCommand() { unregisterSubCommand(); }
};


Expand All @@ -78,7 +94,8 @@ TEST(CommandLineTest, ModifyExisitingOption) {
const char ArgString[] = "new-test-option";
const char ValueString[] = "Integer";

StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
StringMap<cl::Option *> &Map =
cl::getRegisteredOptions(*cl::TopLevelSubCommand);

ASSERT_TRUE(Map.count("test-option") == 1) <<
"Could not find option in map.";
Expand Down Expand Up @@ -237,7 +254,8 @@ TEST(CommandLineTest, HideUnrelatedOptions) {
ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
<< "Hid extra option that should be visable.";

StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
StringMap<cl::Option *> &Map =
cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
<< "Hid default option that should be visable.";
}
Expand All @@ -261,9 +279,201 @@ TEST(CommandLineTest, HideUnrelatedOptionsMulti) {
ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())
<< "Hid extra option that should be visable.";

StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
StringMap<cl::Option *> &Map =
cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
<< "Hid default option that should be visable.";
}

TEST(CommandLineTest, SetValueInSubcategories) {
cl::ResetCommandLineParser();

StackSubCommand SC1("sc1", "First subcommand");
StackSubCommand SC2("sc2", "Second subcommand");

StackOption<bool> TopLevelOpt("top-level", cl::init(false));
StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));

EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);
const char *args[] = {"prog", "-top-level"};
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
EXPECT_TRUE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);

TopLevelOpt = false;

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);
const char *args2[] = {"prog", "sc1", "-sc1"};
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
EXPECT_FALSE(TopLevelOpt);
EXPECT_TRUE(SC1Opt);
EXPECT_FALSE(SC2Opt);

SC1Opt = false;

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);
const char *args3[] = {"prog", "sc2", "-sc2"};
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_TRUE(SC2Opt);
}

TEST(CommandLineTest, LookupFailsInWrongSubCommand) {
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", "sc1", "-sc2"};
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
}

TEST(CommandLineTest, AddToAllSubCommands) {
cl::ResetCommandLineParser();

StackSubCommand SC1("sc1", "First subcommand");
StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands),
cl::init(false));
StackSubCommand SC2("sc2", "Second subcommand");

const char *args[] = {"prog", "-everywhere"};
const char *args2[] = {"prog", "sc1", "-everywhere"};
const char *args3[] = {"prog", "sc2", "-everywhere"};

EXPECT_FALSE(AllOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
EXPECT_TRUE(AllOpt);

AllOpt = false;

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(AllOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
EXPECT_TRUE(AllOpt);

AllOpt = false;

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(AllOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
EXPECT_TRUE(AllOpt);
}

TEST(CommandLineTest, ReparseCommandLineOptions) {
cl::ResetCommandLineParser();

StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand),
cl::init(false));

const char *args[] = {"prog", "-top-level"};

EXPECT_FALSE(TopLevelOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
EXPECT_TRUE(TopLevelOpt);

TopLevelOpt = false;

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(TopLevelOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
EXPECT_TRUE(TopLevelOpt);
}

TEST(CommandLineTest, RemoveFromRegularSubCommand) {
cl::ResetCommandLineParser();

StackSubCommand SC("sc", "Subcommand");
StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false));
StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false));

const char *args[] = {"prog", "sc", "-remove-option"};

EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, nullptr, true));
EXPECT_TRUE(RemoveOption);

RemoveOption.removeArgument();

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
}

TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {
cl::ResetCommandLineParser();

StackOption<bool> TopLevelRemove(
"top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false));
StackOption<bool> TopLevelKeep(
"top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false));

const char *args[] = {"prog", "-top-level-remove"};

EXPECT_FALSE(TopLevelRemove);
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
EXPECT_TRUE(TopLevelRemove);

TopLevelRemove.removeArgument();

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, nullptr, true));
}

TEST(CommandLineTest, RemoveFromAllSubCommands) {
cl::ResetCommandLineParser();

StackSubCommand SC1("sc1", "First Subcommand");
StackSubCommand SC2("sc2", "Second Subcommand");
StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands),
cl::init(false));
StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands),
cl::init(false));

const char *args0[] = {"prog", "-remove-option"};
const char *args1[] = {"prog", "sc1", "-remove-option"};
const char *args2[] = {"prog", "sc2", "-remove-option"};

// It should work for all subcommands including the top-level.
EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
EXPECT_TRUE(RemoveOption);

RemoveOption = false;

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
EXPECT_TRUE(RemoveOption);

RemoveOption = false;

cl::ResetAllOptionOccurrences();
EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
EXPECT_TRUE(RemoveOption);

RemoveOption.removeArgument();

// It should not work for any subcommands including the top-level.
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
}

} // anonymous namespace
4 changes: 2 additions & 2 deletions llvm/unittests/Support/ProgramTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) {
// LoopCount should only be incremented once.
while (true) {
++LoopCount;
ProcessInfo WaitResult = Wait(PI1, 0, true, &Error);
ProcessInfo WaitResult = llvm::sys::Wait(PI1, 0, true, &Error);
ASSERT_TRUE(Error.empty());
if (WaitResult.Pid == PI1.Pid)
break;
Expand All @@ -248,7 +248,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait) {
// cse, LoopCount should be greater than 1 (more than one increment occurs).
while (true) {
++LoopCount;
ProcessInfo WaitResult = Wait(PI2, 0, false, &Error);
ProcessInfo WaitResult = llvm::sys::Wait(PI2, 0, false, &Error);
ASSERT_TRUE(Error.empty());
if (WaitResult.Pid == PI2.Pid)
break;
Expand Down