//
class OptionCategory {
private:
- StringRef const Name;
- StringRef const Description;
+ StringRef Name = "General Category";
+ StringRef Description;
void registerCategory();
public:
- OptionCategory(StringRef const Name,
- StringRef const Description = "")
+ OptionCategory(StringRef Name,
+ StringRef Description = "")
: Name(Name), Description(Description) {
registerCategory();
}
+ OptionCategory() = default;
StringRef getName() const { return Name; }
StringRef getDescription() const { return Description; }
+
+ SmallPtrSet<Option *, 16> MemberOptions;
};
// The general Option Category (used as default category).
-extern OptionCategory GeneralCategory;
+extern ManagedStatic<OptionCategory> GeneralCategory;
//===----------------------------------------------------------------------===//
// SubCommand class
StringRef ArgStr; // The argument string itself (ex: "help", "o")
StringRef HelpStr; // The descriptive text message for -help
StringRef ValueStr; // String describing what the value of this option is
- SmallVector<OptionCategory *, 1>
- Categories; // The Categories this option belongs to
- SmallPtrSet<SubCommand *, 1> Subs; // The subcommands this option belongs to.
+
+ // Return the set of OptionCategories that this Option belongs to.
+ SmallPtrSet<OptionCategory *, 1> getCategories() const;
+
+ // Return the set of SubCommands that this Option belongs to.
+ SmallPtrSet<SubCommand *, 1> getSubCommands() const;
inline enum NumOccurrencesFlag getNumOccurrencesFlag() const {
return (enum NumOccurrencesFlag)Occurrences;
return getNumOccurrencesFlag() == cl::ConsumeAfter;
}
- bool isInAllSubCommands() const {
- return any_of(Subs, [](const SubCommand *SC) {
- return SC == &*AllSubCommands;
- });
- }
-
//-------------------------------------------------------------------------===
// Accessor functions set by OptionModifiers
//
void setMiscFlag(enum MiscFlags M) { Misc |= M; }
void setPosition(unsigned pos) { Position = pos; }
void addCategory(OptionCategory &C);
- void addSubCommand(SubCommand &S) { Subs.insert(&S); }
protected:
explicit Option(enum NumOccurrencesFlag OccurrencesFlag,
enum OptionHidden Hidden)
: NumOccurrences(0), Occurrences(OccurrencesFlag), Value(0),
HiddenFlag(Hidden), Formatting(NormalFormatting), Misc(0),
- FullyInitialized(false), Position(0), AdditionalVals(0) {
- Categories.push_back(&GeneralCategory);
- }
+ FullyInitialized(false), Position(0), AdditionalVals(0) {}
inline void setNumAdditionalVals(unsigned n) { AdditionalVals = n; }
// addArgument - Register this argument with the commandline system.
//
- void addArgument();
+ virtual void addArgument(SubCommand &SC);
+
+ // addArgument - Only called in done() method to add default
+ // TopLevelSubCommand.
+ void addArgument() {
+ if (!FullyInitialized)
+ addArgument(*TopLevelSubCommand);
+ }
/// Unregisters this option from the CommandLine system.
///
sub(SubCommand &S) : Sub(S) {}
- template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); }
+ template <class Opt> void apply(Opt &O) const { O.addArgument(Sub); }
};
//===----------------------------------------------------------------------===//
error("cl::alias must have argument name specified!");
if (!AliasFor)
error("cl::alias must have an cl::aliasopt(option) specified!");
- if (!Subs.empty())
- error("cl::alias must not have cl::sub(), aliased option's cl::sub() will be used!");
- Subs = AliasFor->Subs;
- Categories = AliasFor->Categories;
- addArgument();
+ for(OptionCategory *Cat: AliasFor->getCategories())
+ addCategory(*Cat);
+ for(SubCommand *SC: AliasFor->getSubCommands())
+ Option::addArgument(*SC);
}
public:
AliasFor = &O;
}
+ // Does nothing when called via apply. Aliases call Option::addArgument
+ // directly in the done() method to actually add the option..
+ void addArgument(SubCommand &SC) override {}
+
template <class... Mods>
explicit alias(const Mods &... Ms)
: Option(Optional, Hidden), AliasFor(nullptr) {
// This collects Options added with the cl::DefaultOption flag. Since they can
// be overridden, they are not added to the appropriate SubCommands until
// ParseCommandLineOptions actually runs.
- SmallVector<Option*, 4> DefaultOptions;
+ SmallVector<std::pair<Option*, SubCommand*>, 4> DefaultOptions;
// This collects the different option categories that have been registered.
SmallPtrSet<OptionCategory *, 16> RegisteredOptionCategories;
SmallPtrSet<SubCommand *, 4> RegisteredSubCommands;
CommandLineParser() : ActiveSubCommand(nullptr) {
+ RegisteredOptionCategories.insert(&*GeneralCategory);
registerSubCommand(&*TopLevelSubCommand);
registerSubCommand(&*AllSubCommands);
}
}
void addLiteralOption(Option &Opt, StringRef Name) {
- if (Opt.Subs.empty())
- addLiteralOption(Opt, &*TopLevelSubCommand, Name);
- else {
- for (auto SC : Opt.Subs)
- addLiteralOption(Opt, SC, Name);
- }
+ for(SubCommand *SC: Opt.getSubCommands())
+ addLiteralOption(Opt, SC, Name);
}
- void addOption(Option *O, SubCommand *SC) {
+ void addOption(Option *O, SubCommand *SC, bool ProcessDefaultOptions = false) {
+ if (!ProcessDefaultOptions && O->isDefaultOption()) {
+ DefaultOptions.push_back(std::make_pair(O, SC));
+ return;
+ }
+
bool HadErrors = false;
if (O->hasArgStr()) {
// If it's a DefaultOption, check to make sure it isn't already there.
for (const auto &Sub : RegisteredSubCommands) {
if (SC == Sub)
continue;
- addOption(O, Sub);
+ addOption(O, Sub, ProcessDefaultOptions);
}
}
}
- void addOption(Option *O, bool ProcessDefaultOption = false) {
- if (!ProcessDefaultOption && O->isDefaultOption()) {
- DefaultOptions.push_back(O);
- return;
- }
-
- if (O->Subs.empty()) {
- addOption(O, &*TopLevelSubCommand);
- } else {
- for (auto SC : O->Subs)
- addOption(O, SC);
+ void addDefaultOptions() {
+ for (std::pair<Option *, SubCommand *> &DO : DefaultOptions) {
+ addOption(DO.first, DO.second, true);
}
}
}
void removeOption(Option *O) {
- if (O->Subs.empty())
- removeOption(O, &*TopLevelSubCommand);
- else {
- if (O->isInAllSubCommands()) {
- for (auto SC : RegisteredSubCommands)
- removeOption(O, SC);
- } else {
- for (auto SC : O->Subs)
- removeOption(O, SC);
- }
- }
+ for (auto SC : RegisteredSubCommands)
+ removeOption(O, SC);
}
bool hasOptions(const SubCommand &Sub) const {
}
void updateArgStr(Option *O, StringRef NewName) {
- if (O->Subs.empty())
- updateArgStr(O, NewName, &*TopLevelSubCommand);
- else {
- if (O->isInAllSubCommands()) {
- for (auto SC : RegisteredSubCommands)
- updateArgStr(O, NewName, SC);
- } else {
- for (auto SC : O->Subs)
- updateArgStr(O, NewName, SC);
- }
- }
+ for (auto SC : RegisteredSubCommands)
+ updateArgStr(O, NewName, SC);
}
void printOptionValues();
MoreHelp.clear();
RegisteredOptionCategories.clear();
+ RegisteredOptionCategories.insert(&*GeneralCategory);
ResetAllOptionOccurrences();
RegisteredSubCommands.clear();
GlobalParser->MoreHelp.push_back(Help);
}
-void Option::addArgument() {
- GlobalParser->addOption(this);
+void Option::addArgument(SubCommand &SC) {
+ GlobalParser->addOption(this, &SC);
FullyInitialized = true;
}
void Option::removeArgument() { GlobalParser->removeOption(this); }
+SmallPtrSet<OptionCategory *, 1> Option::getCategories() const {
+ SmallPtrSet<OptionCategory *, 1> Cats;
+ for (OptionCategory *C: GlobalParser->RegisteredOptionCategories) {
+ if (C->MemberOptions.find(this) != C->MemberOptions.end())
+ Cats.insert(C);
+ }
+ if (Cats.empty())
+ Cats.insert(&*GeneralCategory);
+ return Cats;
+}
+
+SmallPtrSet<SubCommand *, 1> Option::getSubCommands() const {
+ // This can happen for enums and literal options.
+ if (ArgStr.empty())
+ return SmallPtrSet<SubCommand *, 1>{&*TopLevelSubCommand};
+
+ SmallPtrSet<SubCommand *, 1> Subs;
+ for (SubCommand *SC : GlobalParser->getRegisteredSubcommands()) {
+ auto I = SC->OptionsMap.find(ArgStr);
+ if (I != SC->OptionsMap.end() && I->getValue() == this)
+ Subs.insert(SC);
+ }
+ return Subs;
+}
+
void Option::setArgStr(StringRef S) {
if (FullyInitialized)
GlobalParser->updateArgStr(this, S);
}
void Option::addCategory(OptionCategory &C) {
- assert(!Categories.empty() && "Categories cannot be empty.");
- // Maintain backward compatibility by replacing the default GeneralCategory
- // if it's still set. Otherwise, just add the new one. The GeneralCategory
- // must be explicitly added if you want multiple categories that include it.
- if (&C != &GeneralCategory && Categories[0] == &GeneralCategory)
- Categories[0] = &C;
- else if (find(Categories, &C) == Categories.end())
- Categories.push_back(&C);
+ C.MemberOptions.insert(this);
}
void Option::reset() {
}
// Initialise the general option category.
-OptionCategory llvm::cl::GeneralCategory("General options");
+LLVM_REQUIRE_CONSTANT_INITIALIZATION
+ManagedStatic<OptionCategory> llvm::cl::GeneralCategory;
void OptionCategory::registerCategory() {
GlobalParser->registerCategory(this);
auto &SinkOpts = ChosenSubCommand->SinkOpts;
auto &OptionsMap = ChosenSubCommand->OptionsMap;
- for (auto O: DefaultOptions) {
- addOption(O, true);
- }
+ addDefaultOptions();
if (ConsumeAfterOpt) {
assert(PositionalOpts.size() > 0 &&
// options within categories will also be alphabetically sorted.
for (size_t I = 0, E = Opts.size(); I != E; ++I) {
Option *Opt = Opts[I].second;
- for (auto &Cat : Opt->Categories) {
+ for (auto *Cat : Opt->getCategories()) {
assert(CategorizedOptions.count(Cat) > 0 &&
"Option has an unregistered category");
CategorizedOptions[Cat].push_back(Opt);
void cl::HideUnrelatedOptions(cl::OptionCategory &Category, SubCommand &Sub) {
for (auto &I : Sub.OptionsMap) {
- for (auto &Cat : I.second->Categories) {
+ for (OptionCategory *Cat : I.second->getCategories()) {
if (Cat != &Category &&
Cat != &GenericCategory)
I.second->setHiddenFlag(cl::ReallyHidden);
void cl::HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories,
SubCommand &Sub) {
for (auto &I : Sub.OptionsMap) {
- for (auto &Cat : I.second->Categories) {
+ for (OptionCategory *Cat : I.second->getCategories()) {
if (find(Categories, Cat) == Categories.end() && Cat != &GenericCategory)
I.second->setHiddenFlag(cl::ReallyHidden);
}
cl::Option *Retrieved = Map["test-option"];
ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";
- ASSERT_NE(Retrieved->Categories.end(),
- find_if(Retrieved->Categories,
+ ASSERT_NE(Retrieved->getCategories().end(),
+ find_if(Retrieved->getCategories(),
[&](const llvm::cl::OptionCategory *Cat) {
- return Cat == &cl::GeneralCategory;
+ return Cat == &*cl::GeneralCategory;
}))
<< "Incorrect default option category.";
Retrieved->addCategory(TestCategory);
- ASSERT_NE(Retrieved->Categories.end(),
- find_if(Retrieved->Categories,
+ ASSERT_NE(Retrieved->getCategories().end(),
+ find_if(Retrieved->getCategories(),
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
TEST(CommandLineTest, UseOptionCategory) {
StackOption<int> TestOption2("test-option", cl::cat(TestCategory));
- ASSERT_NE(TestOption2.Categories.end(),
- find_if(TestOption2.Categories,
+ ASSERT_NE(TestOption2.getCategories().end(),
+ find_if(TestOption2.getCategories(),
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
TEST(CommandLineTest, UseMultipleCategories) {
StackOption<int> TestOption2("test-option2", cl::cat(TestCategory),
- cl::cat(cl::GeneralCategory),
- cl::cat(cl::GeneralCategory));
+ cl::cat(*cl::GeneralCategory),
+ cl::cat(*cl::GeneralCategory));
+ auto TestOption2Categories = TestOption2.getCategories();
// Make sure cl::GeneralCategory wasn't added twice.
- ASSERT_EQ(TestOption2.Categories.size(), 2U);
+ ASSERT_EQ(TestOption2Categories.size(), 2U);
- ASSERT_NE(TestOption2.Categories.end(),
- find_if(TestOption2.Categories,
+ ASSERT_NE(TestOption2Categories.end(),
+ find_if(TestOption2Categories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
<< "Failed to assign Option Category.";
- ASSERT_NE(TestOption2.Categories.end(),
- find_if(TestOption2.Categories,
+ ASSERT_NE(TestOption2Categories.end(),
+ find_if(TestOption2Categories,
[&](const llvm::cl::OptionCategory *Cat) {
- return Cat == &cl::GeneralCategory;
+ return Cat == &*cl::GeneralCategory;
}))
<< "Failed to assign General Category.";
cl::OptionCategory AnotherCategory("Additional test Options", "Description");
StackOption<int> TestOption("test-option", cl::cat(TestCategory),
cl::cat(AnotherCategory));
- ASSERT_EQ(TestOption.Categories.end(),
- find_if(TestOption.Categories,
+ auto TestOptionCategories = TestOption.getCategories();
+ ASSERT_EQ(TestOptionCategories.end(),
+ find_if(TestOptionCategories,
[&](const llvm::cl::OptionCategory *Cat) {
- return Cat == &cl::GeneralCategory;
+ return Cat == &*cl::GeneralCategory;
}))
<< "Failed to remove General Category.";
- ASSERT_NE(TestOption.Categories.end(),
- find_if(TestOption.Categories,
+ ASSERT_NE(TestOptionCategories.end(),
+ find_if(TestOptionCategories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
<< "Failed to assign Option Category.";
- ASSERT_NE(TestOption.Categories.end(),
- find_if(TestOption.Categories,
+ ASSERT_NE(TestOptionCategories.end(),
+ find_if(TestOptionCategories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &AnotherCategory;
}))
testAliasRequired(array_lengthof(opts2), opts2);
}
+TEST(CommandLineTest, AliasWithSubCommand) {
+ StackSubCommand SC1("sc1", "Subcommand 1");
+ StackOption<std::string> Option1("option", cl::value_desc("output file"),
+ cl::init("-"), cl::desc("Option"),
+ cl::sub(SC1));
+ StackOption<std::string, cl::alias> Alias1("o", llvm::cl::aliasopt(Option1),
+ cl::desc("Alias for --option"),
+ cl::sub(SC1));
+}
+
+TEST(CommandLineTest, AliasWithMultipleSubCommandsWithSameOption) {
+ StackSubCommand SC1("sc1", "Subcommand 1");
+ StackOption<std::string> Option1("option", cl::value_desc("output file"),
+ cl::init("-"), cl::desc("Option"),
+ cl::sub(SC1));
+ StackSubCommand SC2("sc2", "Subcommand 2");
+ StackOption<std::string> Option2("option", cl::value_desc("output file"),
+ cl::init("-"), cl::desc("Option"),
+ cl::sub(SC2));
+
+ StackOption<std::string, cl::alias> Alias1("o", llvm::cl::aliasopt(Option1),
+ cl::desc("Alias for --option"));
+}
+
TEST(CommandLineTest, HideUnrelatedOptions) {
StackOption<int> TestOption1("hide-option-1");
StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));