diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -281,7 +281,8 @@ 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 - OptionCategory *Category; // The Category this option belongs to + SmallVector + Categories; // The Categories this option belongs to SmallPtrSet Subs; // The subcommands this option belongs to. bool FullyInitialized = false; // Has addArgument been called? @@ -333,14 +334,16 @@ void setFormattingFlag(enum FormattingFlags V) { Formatting = V; } void setMiscFlag(enum MiscFlags M) { Misc |= M; } void setPosition(unsigned pos) { Position = pos; } - void setCategory(OptionCategory &C) { Category = &C; } + void addCategory(OptionCategory &C); void addSubCommand(SubCommand &S) { Subs.insert(&S); } protected: explicit Option(enum NumOccurrencesFlag OccurrencesFlag, enum OptionHidden Hidden) : Occurrences(OccurrencesFlag), Value(0), HiddenFlag(Hidden), - Formatting(NormalFormatting), Misc(0), Category(&GeneralCategory) {} + Formatting(NormalFormatting), Misc(0) { + Categories.push_back(&GeneralCategory); + } inline void setNumAdditionalVals(unsigned n) { AdditionalVals = n; } @@ -451,7 +454,7 @@ cat(OptionCategory &c) : Category(c) {} - template void apply(Opt &O) const { O.setCategory(Category); } + template void apply(Opt &O) const { O.addCategory(Category); } }; // sub - Specify the subcommand that this option belongs to. @@ -1770,7 +1773,7 @@ if (!Subs.empty()) error("cl::alias must not have cl::sub(), aliased option's cl::sub() will be used!"); Subs = AliasFor->Subs; - Category = AliasFor->Category; + Categories = AliasFor->Categories; addArgument(); } diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -425,6 +425,17 @@ setMiscFlag(Grouping); } +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 + Categories.push_back(&C); +} + void Option::reset() { NumOccurrences = 0; setDefault(); @@ -2132,9 +2143,11 @@ // options within categories will also be alphabetically sorted. for (size_t I = 0, E = Opts.size(); I != E; ++I) { Option *Opt = Opts[I].second; - assert(CategorizedOptions.count(Opt->Category) > 0 && - "Option has an unregistered category"); - CategorizedOptions[Opt->Category].push_back(Opt); + for (auto &Cat : Opt->Categories) { + assert(CategorizedOptions.count(Cat) > 0 && + "Option has an unregistered category"); + CategorizedOptions[Cat].push_back(Opt); + } } // Now do printing. @@ -2391,21 +2404,21 @@ void cl::HideUnrelatedOptions(cl::OptionCategory &Category, SubCommand &Sub) { for (auto &I : Sub.OptionsMap) { - if (I.second->Category != &Category && - I.second->Category != &GenericCategory) - I.second->setHiddenFlag(cl::ReallyHidden); + for (auto &Cat : I.second->Categories) { + if (Cat != &Category && + Cat != &GenericCategory) + I.second->setHiddenFlag(cl::ReallyHidden); + } } } void cl::HideUnrelatedOptions(ArrayRef Categories, SubCommand &Sub) { - auto CategoriesBegin = Categories.begin(); - auto CategoriesEnd = Categories.end(); for (auto &I : Sub.OptionsMap) { - if (std::find(CategoriesBegin, CategoriesEnd, I.second->Category) == - CategoriesEnd && - I.second->Category != &GenericCategory) - I.second->setHiddenFlag(cl::ReallyHidden); + for (auto &Cat : I.second->Categories) { + if (find(Categories, Cat) == Categories.end() && Cat != &GenericCategory) + I.second->setHiddenFlag(cl::ReallyHidden); + } } } diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -95,12 +95,20 @@ cl::Option *Retrieved = Map["test-option"]; ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option."; - ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) << - "Incorrect default option category."; - - Retrieved->setCategory(TestCategory); - ASSERT_EQ(&TestCategory,Retrieved->Category) << - "Failed to modify option's option category."; + ASSERT_NE(Retrieved->Categories.end(), + find_if(Retrieved->Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &cl::GeneralCategory; + })) + << "Incorrect default option category."; + + Retrieved->addCategory(TestCategory); + ASSERT_NE(Retrieved->Categories.end(), + find_if(Retrieved->Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &TestCategory; + })) + << "Failed to modify option's option category."; Retrieved->setDescription(Description); ASSERT_STREQ(Retrieved->HelpStr.data(), Description) @@ -152,8 +160,52 @@ TEST(CommandLineTest, UseOptionCategory) { StackOption TestOption2("test-option", cl::cat(TestCategory)); - ASSERT_EQ(&TestCategory,TestOption2.Category) << "Failed to assign Option " - "Category."; + ASSERT_NE(TestOption2.Categories.end(), + find_if(TestOption2.Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &TestCategory; + })) + << "Failed to assign Option Category."; +} + +TEST(CommandLineTest, UseMultipleCategories) { + StackOption TestOption2("test-option2", cl::cat(TestCategory), + cl::cat(cl::GeneralCategory)); + + ASSERT_NE(TestOption2.Categories.end(), + find_if(TestOption2.Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &TestCategory; + })) + << "Failed to assign Option Category."; + ASSERT_NE(TestOption2.Categories.end(), + find_if(TestOption2.Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &cl::GeneralCategory; + })) + << "Failed to assign General Category."; + + cl::OptionCategory AnotherCategory("Additional test Options", "Description"); + StackOption TestOption("test-option", cl::cat(TestCategory), + cl::cat(AnotherCategory)); + ASSERT_EQ(TestOption.Categories.end(), + find_if(TestOption.Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &cl::GeneralCategory; + })) + << "Failed to remove General Category."; + ASSERT_NE(TestOption.Categories.end(), + find_if(TestOption.Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &TestCategory; + })) + << "Failed to assign Option Category."; + ASSERT_NE(TestOption.Categories.end(), + find_if(TestOption.Categories, + [&](const llvm::cl::OptionCategory *Cat) { + return Cat == &AnotherCategory; + })) + << "Failed to assign Another Category."; } typedef void ParserFunction(StringRef Source, StringSaver &Saver,