diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 7131f25..6796046 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -198,7 +198,7 @@ public: /// /// For example, /// - /// std::string filename + /// std::string filename; /// program.add_option("filename", filename, "description of filename"); /// Option* add_option( @@ -474,6 +474,7 @@ public: /// The real work is done here. Expects a reversed vector. /// Changes the vector to the remaining options. std::vector& parse(std::vector &args) { + _validate(); _parse(args); run_callback(); return args; @@ -678,6 +679,18 @@ public: protected: + /// Check the options to make sure there are no conficts. + /// + /// Currenly checks to see if mutiple positionals exist with -1 args + void _validate() const { + auto count = std::count_if(std::begin(options_), std::end(options_), + [](const Option_p& opt){return opt->get_expected() == -1 && opt->get_positional();}); + if(count > 1) + throw InvalidError(name_ + ": Too many positional arguments with unlimited expected args"); + for(const App_p& app : subcommands_) + app->_validate(); + } + /// Return missing from the master missing_t* missing() { diff --git a/include/CLI/Error.hpp b/include/CLI/Error.hpp index 196a5bd..55b0304 100644 --- a/include/CLI/Error.hpp +++ b/include/CLI/Error.hpp @@ -100,6 +100,11 @@ struct ExtrasError : public ParseError { ExtrasError(std::string name) : ParseError("ExtrasError", name, 6) {} }; +/// Thrown when validation fails before parsing +struct InvalidError : public ParseError { + InvalidError(std::string name) : ParseError("InvalidError", name, 15) {} +}; + /// This is just a safety check to verify selection and parsing match struct HorribleError : public ParseError { HorribleError(std::string name) : ParseError("HorribleError", "(You should never see this error) " + name, 7) {} diff --git a/tests/CreationTest.cpp b/tests/CreationTest.cpp index 36de203..2df43f6 100644 --- a/tests/CreationTest.cpp +++ b/tests/CreationTest.cpp @@ -35,6 +35,24 @@ TEST_F(TApp, AddingExistingWithCaseAfter2) { EXPECT_THROW(cat->ignore_case(), CLI::OptionAlreadyAdded); } +TEST_F(TApp, AddingMultipleInfPositionals) { + std::vector one, two; + app.add_option("one", one); + app.add_option("two", two); + + EXPECT_THROW(run(), CLI::InvalidError); +} + + +TEST_F(TApp, AddingMultipleInfPositionalsSubcom) { + std::vector one, two; + CLI::App* below = app.add_subcommand("below"); + below->add_option("one", one); + below->add_option("two", two); + + EXPECT_THROW(run(), CLI::InvalidError); +} + TEST_F(TApp, MultipleSubcomMatching) { app.add_subcommand("first"); app.add_subcommand("second");