mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-05-01 13:13:53 +00:00
conflicting option names (#1049)
Take the configurability of an option into account when determining ambiguous names and conflicts. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
08d840bfbe
commit
4ecbdd83e5
@ -87,7 +87,7 @@ repos:
|
|||||||
exclude: .pre-commit-config.yaml
|
exclude: .pre-commit-config.yaml
|
||||||
|
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.2.6
|
rev: v2.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
args: ["-L", "atleast,ans,doub,inout"]
|
args: ["-L", "atleast,ans,doub,inout,AtMost"]
|
||||||
|
@ -174,19 +174,19 @@ CLI11_INLINE Option *App::add_option(std::string option_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto *op = get_option_no_throw(test_name);
|
auto *op = get_option_no_throw(test_name);
|
||||||
if(op != nullptr) {
|
if(op != nullptr && op->get_configurable()) {
|
||||||
throw(OptionAlreadyAdded("added option positional name matches existing option: " + test_name));
|
throw(OptionAlreadyAdded("added option positional name matches existing option: " + test_name));
|
||||||
}
|
}
|
||||||
} else if(parent_ != nullptr) {
|
} else if(parent_ != nullptr) {
|
||||||
for(auto &ln : myopt.lnames_) {
|
for(auto &ln : myopt.lnames_) {
|
||||||
auto *op = parent_->get_option_no_throw(ln);
|
auto *op = parent_->get_option_no_throw(ln);
|
||||||
if(op != nullptr) {
|
if(op != nullptr && op->get_configurable()) {
|
||||||
throw(OptionAlreadyAdded("added option matches existing positional option: " + ln));
|
throw(OptionAlreadyAdded("added option matches existing positional option: " + ln));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(auto &sn : myopt.snames_) {
|
for(auto &sn : myopt.snames_) {
|
||||||
auto *op = parent_->get_option_no_throw(sn);
|
auto *op = parent_->get_option_no_throw(sn);
|
||||||
if(op != nullptr) {
|
if(op != nullptr && op->get_configurable()) {
|
||||||
throw(OptionAlreadyAdded("added option matches existing positional option: " + sn));
|
throw(OptionAlreadyAdded("added option matches existing positional option: " + sn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1490,6 +1490,24 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
|
|||||||
}
|
}
|
||||||
if(op == nullptr) {
|
if(op == nullptr) {
|
||||||
op = get_option_no_throw(item.name);
|
op = get_option_no_throw(item.name);
|
||||||
|
} else if(!op->get_configurable()) {
|
||||||
|
auto *testop = get_option_no_throw(item.name);
|
||||||
|
if(testop != nullptr && testop->get_configurable()) {
|
||||||
|
op = testop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(!op->get_configurable()) {
|
||||||
|
if(item.name.size() == 1) {
|
||||||
|
auto *testop = get_option_no_throw("-" + item.name);
|
||||||
|
if(testop != nullptr && testop->get_configurable()) {
|
||||||
|
op = testop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!op->get_configurable()) {
|
||||||
|
auto *testop = get_option_no_throw(item.name);
|
||||||
|
if(testop != nullptr && testop->get_configurable()) {
|
||||||
|
op = testop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,26 +311,27 @@ CLI11_INLINE void Option::run_callback() {
|
|||||||
|
|
||||||
CLI11_NODISCARD CLI11_INLINE const std::string &Option::matching_name(const Option &other) const {
|
CLI11_NODISCARD CLI11_INLINE const std::string &Option::matching_name(const Option &other) const {
|
||||||
static const std::string estring;
|
static const std::string estring;
|
||||||
|
bool bothConfigurable = configurable_ && other.configurable_;
|
||||||
for(const std::string &sname : snames_) {
|
for(const std::string &sname : snames_) {
|
||||||
if(other.check_sname(sname))
|
if(other.check_sname(sname))
|
||||||
return sname;
|
return sname;
|
||||||
if(other.check_lname(sname))
|
if(bothConfigurable && other.check_lname(sname))
|
||||||
return sname;
|
return sname;
|
||||||
}
|
}
|
||||||
for(const std::string &lname : lnames_) {
|
for(const std::string &lname : lnames_) {
|
||||||
if(other.check_lname(lname))
|
if(other.check_lname(lname))
|
||||||
return lname;
|
return lname;
|
||||||
if(lname.size() == 1) {
|
if(lname.size() == 1 && bothConfigurable) {
|
||||||
if(other.check_sname(lname)) {
|
if(other.check_sname(lname)) {
|
||||||
return lname;
|
return lname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(snames_.empty() && lnames_.empty() && !pname_.empty()) {
|
if(bothConfigurable && snames_.empty() && lnames_.empty() && !pname_.empty()) {
|
||||||
if(other.check_sname(pname_) || other.check_lname(pname_) || pname_ == other.pname_)
|
if(other.check_sname(pname_) || other.check_lname(pname_) || pname_ == other.pname_)
|
||||||
return pname_;
|
return pname_;
|
||||||
}
|
}
|
||||||
if(other.snames_.empty() && other.fnames_.empty() && !other.pname_.empty()) {
|
if(bothConfigurable && other.snames_.empty() && other.fnames_.empty() && !other.pname_.empty()) {
|
||||||
if(check_sname(other.pname_) || check_lname(other.pname_) || (pname_ == other.pname_))
|
if(check_sname(other.pname_) || check_lname(other.pname_) || (pname_ == other.pname_))
|
||||||
return other.pname_;
|
return other.pname_;
|
||||||
}
|
}
|
||||||
|
@ -555,6 +555,18 @@ TEST_CASE_METHOD(TApp, "NumberFlags", "[app]") {
|
|||||||
CHECK(7 == val);
|
CHECK(7 == val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "doubleDashH", "[app]") {
|
||||||
|
|
||||||
|
int val{0};
|
||||||
|
// test you can add a --h option and it doesn't conflict with the help
|
||||||
|
CHECK_NOTHROW(app.add_flag("--h", val));
|
||||||
|
|
||||||
|
auto *topt = app.add_flag("-t");
|
||||||
|
CHECK_THROWS_AS(app.add_flag("--t"), CLI::OptionAlreadyAdded);
|
||||||
|
topt->configurable(false);
|
||||||
|
CHECK_NOTHROW(app.add_flag("--t"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "DisableFlagOverrideTest", "[app]") {
|
TEST_CASE_METHOD(TApp, "DisableFlagOverrideTest", "[app]") {
|
||||||
|
|
||||||
int val{0};
|
int val{0};
|
||||||
@ -2180,7 +2192,7 @@ TEST_CASE_METHOD(TApp, "NeedsTrue", "[app]") {
|
|||||||
args = {"--string", "val_with_opt1", "--opt1"};
|
args = {"--string", "val_with_opt1", "--opt1"};
|
||||||
run();
|
run();
|
||||||
|
|
||||||
args = {"--opt1", "--string", "val_with_opt1"}; // order is not revelant
|
args = {"--opt1", "--string", "val_with_opt1"}; // order is not relevant
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,6 +1109,67 @@ TEST_CASE_METHOD(TApp, "IniOverwrite", "[config]") {
|
|||||||
CHECK(two == 99);
|
CHECK(two == 99);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "hInConfig", "[config]") {
|
||||||
|
|
||||||
|
TempFile tmpini{"TestIniTmp.ini"};
|
||||||
|
{
|
||||||
|
std::ofstream out{tmpini};
|
||||||
|
out << "[default]" << '\n';
|
||||||
|
out << "h=99" << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string next = "TestIniTmp.ini";
|
||||||
|
app.set_config("--conf", next);
|
||||||
|
int two{7};
|
||||||
|
app.add_option("--h", two);
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
CHECK(two == 99);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "notConfigurableOptionOverload", "[config]") {
|
||||||
|
|
||||||
|
TempFile tmpini{"TestIniTmp.ini"};
|
||||||
|
{
|
||||||
|
std::ofstream out{tmpini};
|
||||||
|
out << "[default]" << '\n';
|
||||||
|
out << "m=99" << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string next = "TestIniTmp.ini";
|
||||||
|
app.set_config("--conf", next);
|
||||||
|
int two{7};
|
||||||
|
int three{5};
|
||||||
|
app.add_option("--m", three)->configurable(false);
|
||||||
|
app.add_option("-m", two);
|
||||||
|
|
||||||
|
run();
|
||||||
|
CHECK(three == 5);
|
||||||
|
CHECK(two == 99);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "notConfigurableOptionOverload2", "[config]") {
|
||||||
|
|
||||||
|
TempFile tmpini{"TestIniTmp.ini"};
|
||||||
|
{
|
||||||
|
std::ofstream out{tmpini};
|
||||||
|
out << "[default]" << '\n';
|
||||||
|
out << "m=99" << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string next = "TestIniTmp.ini";
|
||||||
|
app.set_config("--conf", next);
|
||||||
|
int two{7};
|
||||||
|
int three{5};
|
||||||
|
app.add_option("-m", three)->configurable(false);
|
||||||
|
app.add_option("m", two);
|
||||||
|
|
||||||
|
run();
|
||||||
|
CHECK(three == 5);
|
||||||
|
CHECK(two == 99);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "IniRequired", "[config]") {
|
TEST_CASE_METHOD(TApp, "IniRequired", "[config]") {
|
||||||
|
|
||||||
TempFile tmpini{"TestIniTmp.ini"};
|
TempFile tmpini{"TestIniTmp.ini"};
|
||||||
@ -2818,6 +2879,13 @@ TEST_CASE_METHOD(TApp, "IniDisableFlagOverride", "[config]") {
|
|||||||
CHECK(tmpini3.c_str() == app.get_config_ptr()->as<std::string>());
|
CHECK(tmpini3.c_str() == app.get_config_ptr()->as<std::string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("fclear", "[config]") {
|
||||||
|
// mainly to clear up some warnings
|
||||||
|
(void)fclear1;
|
||||||
|
(void)fclear2;
|
||||||
|
(void)fclear3;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "TomlOutputSimple", "[config]") {
|
TEST_CASE_METHOD(TApp, "TomlOutputSimple", "[config]") {
|
||||||
|
|
||||||
int v{0};
|
int v{0};
|
||||||
|
@ -66,7 +66,7 @@ TEST_CASE("app_file_gen_fail") {
|
|||||||
CLI::FuzzApp fuzzdata;
|
CLI::FuzzApp fuzzdata;
|
||||||
auto app = fuzzdata.generateApp();
|
auto app = fuzzdata.generateApp();
|
||||||
|
|
||||||
int index = GENERATE(range(1, 40));
|
int index = GENERATE(range(1, 41));
|
||||||
std::string optionString, flagString;
|
std::string optionString, flagString;
|
||||||
auto parseData = loadFailureFile("fuzz_app_file_fail", index);
|
auto parseData = loadFailureFile("fuzz_app_file_fail", index);
|
||||||
if(parseData.size() > 25) {
|
if(parseData.size() > 25) {
|
||||||
|
1
tests/fuzzFail/fuzz_app_file_fail40
Normal file
1
tests/fuzzFail/fuzz_app_file_fail40
Normal file
@ -0,0 +1 @@
|
|||||||
|
config ' '
|
Loading…
x
Reference in New Issue
Block a user