From 42885681b9c31673a85de380ca810d17d30bf3e6 Mon Sep 17 00:00:00 2001 From: Don Hinton Date: Tue, 7 May 2019 18:57:01 +0000 Subject: [PATCH] [CommandLine] Allow Options to specify multiple OptionCategory's. Summary: It's not uncommon for separate components to share common Options, e.g., it's common for related Passes to share Options in addition to the Pass specific ones. With this change, components can use OptionCategory's to simply help output even if some of the options are shared. Reviewed By: MaskRay Tags: #llvm Differential Revision: https://reviews.llvm.org/D61574 llvm-svn: 360179 --- include/llvm/Support/CommandLine.h | 13 ++++-- lib/Support/CommandLine.cpp | 37 ++++++++++----- unittests/Support/CommandLineTest.cpp | 66 ++++++++++++++++++++++++--- 3 files changed, 92 insertions(+), 24 deletions(-) diff --git a/include/llvm/Support/CommandLine.h b/include/llvm/Support/CommandLine.h index 74f5de6621e..68cbebe5de8 100644 --- a/include/llvm/Support/CommandLine.h +++ b/include/llvm/Support/CommandLine.h @@ -281,7 +281,8 @@ public: 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 @@ public: 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 @@ struct cat { 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 @@ class alias : public Option { 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/lib/Support/CommandLine.cpp b/lib/Support/CommandLine.cpp index ff53fefefea..28516476d58 100644 --- a/lib/Support/CommandLine.cpp +++ b/lib/Support/CommandLine.cpp @@ -425,6 +425,17 @@ void Option::setArgStr(StringRef S) { 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 @@ protected: // 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 @@ cl::getRegisteredSubcommands() { 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/unittests/Support/CommandLineTest.cpp b/unittests/Support/CommandLineTest.cpp index e528f51cbf0..8869674372e 100644 --- a/unittests/Support/CommandLineTest.cpp +++ b/unittests/Support/CommandLineTest.cpp @@ -95,12 +95,20 @@ TEST(CommandLineTest, ModifyExisitingOption) { cl::Option *Retrieved = Map["test-option"]; ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option."; - ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) << - "Incorrect default 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->setCategory(TestCategory); - ASSERT_EQ(&TestCategory,Retrieved->Category) << - "Failed to modify option's 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, ParseEnvironmentToLocalVar) { 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,