Skip to content

Commit f317c53

Browse files
committed
Make the IncorrectConstruction errors on bad subcommand name more comprehensible.
1 parent e2e3cb2 commit f317c53

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

include/CLI/App.hpp

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,20 @@ class App {
368368
/// Set an alias for the app
369369
App *alias(std::string app_name) {
370370
if(!detail::valid_name_string(app_name)) {
371-
throw(IncorrectConstruction("alias is not a valid name string"));
371+
if(app_name.empty()) {
372+
throw IncorrectConstruction("Empty aliases are not allowed");
373+
}
374+
if(!detail::valid_first_char(app_name[0])) {
375+
throw IncorrectConstruction(
376+
"Alias starts with invalid character, allowed characters are [a-zA-z0-9]+'_','?','@' ");
377+
}
378+
for(auto c : app_name) {
379+
if(!detail::valid_later_char(c)) {
380+
throw IncorrectConstruction(std::string("Alias contains invalid character ('") + c +
381+
"'), allowed characters are "
382+
"[a-zA-z0-9]+'_','?','@','.','-' ");
383+
}
384+
}
372385
}
373386

374387
if(parent_ != nullptr) {
@@ -1013,7 +1026,17 @@ class App {
10131026
/// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag
10141027
App *add_subcommand(std::string subcommand_name = "", std::string subcommand_description = "") {
10151028
if(!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {
1016-
throw IncorrectConstruction("subcommand name is not valid");
1029+
if(!detail::valid_first_char(subcommand_name[0])) {
1030+
throw IncorrectConstruction(
1031+
"Subcommand name starts with invalid character, allowed characters are [a-zA-z0-9]+'_','?','@' ");
1032+
}
1033+
for(auto c : subcommand_name) {
1034+
if(!detail::valid_later_char(c)) {
1035+
throw IncorrectConstruction(std::string("Subcommand name contains invalid character ('") + c +
1036+
"'), allowed characters are "
1037+
"[a-zA-z0-9]+'_','?','@','.','-' ");
1038+
}
1039+
}
10171040
}
10181041
CLI::App_p subcom = std::shared_ptr<App>(new App(std::move(subcommand_description), subcommand_name, this));
10191042
return add_subcommand(std::move(subcom));
@@ -3158,25 +3181,25 @@ struct AppFriend {
31583181
#ifdef CLI11_CPP14
31593182

31603183
/// Wrap _parse_short, perfectly forward arguments and return
3161-
template <typename... Args> static decltype(auto) parse_arg(App *app, Args &&... args) {
3184+
template <typename... Args> static decltype(auto) parse_arg(App *app, Args &&...args) {
31623185
return app->_parse_arg(std::forward<Args>(args)...);
31633186
}
31643187

31653188
/// Wrap _parse_subcommand, perfectly forward arguments and return
3166-
template <typename... Args> static decltype(auto) parse_subcommand(App *app, Args &&... args) {
3189+
template <typename... Args> static decltype(auto) parse_subcommand(App *app, Args &&...args) {
31673190
return app->_parse_subcommand(std::forward<Args>(args)...);
31683191
}
31693192
#else
31703193
/// Wrap _parse_short, perfectly forward arguments and return
31713194
template <typename... Args>
3172-
static auto parse_arg(App *app, Args &&... args) ->
3195+
static auto parse_arg(App *app, Args &&...args) ->
31733196
typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type {
31743197
return app->_parse_arg(std::forward<Args>(args)...);
31753198
}
31763199

31773200
/// Wrap _parse_subcommand, perfectly forward arguments and return
31783201
template <typename... Args>
3179-
static auto parse_subcommand(App *app, Args &&... args) ->
3202+
static auto parse_subcommand(App *app, Args &&...args) ->
31803203
typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type {
31813204
return app->_parse_subcommand(std::forward<Args>(args)...);
31823205
}

tests/SubcommandTest.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,18 @@ TEST_CASE_METHOD(TApp, "RequiredPosInSubcommand", "[subcom]") {
811811
CHECK_THROWS_AS(run(), CLI::RequiredError);
812812
}
813813

814+
TEST_CASE_METHOD(TApp, "invalidSubcommandName", "[subcom]") {
815+
816+
bool gotError{false};
817+
try {
818+
app.add_subcommand("foo/foo", "Foo a bar");
819+
} catch(const CLI::IncorrectConstruction &e) {
820+
gotError = true;
821+
CHECK_THAT(e.what(), Contains("/"));
822+
}
823+
CHECK(gotError);
824+
}
825+
814826
struct SubcommandProgram : public TApp {
815827

816828
CLI::App *start{nullptr};

0 commit comments

Comments
 (0)