You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Okay, this one is a bit of a headscratcher. I've been digging into this with Rust 1.48.0 and Clap 3.0.0-beta.2, although I bet 2.x.x suffers from the same problem given its definition of struct MatchedArg.
First, a test case:
use clap::*;fnmain(){let m = App::new("multiple_values").arg(Arg::new("pos").about("multiple positionals").number_of_values(3),).try_get_matches_from(vec!["myprog","val1","val2","val3","val4","val5","val6",]);assert!(m.is_err());// This panics, because `m.is_err() == false`.assert_eq!(m.unwrap_err().kind,ErrorKind::WrongNumberOfValues);}
This test case should obviously fail: specifying number_of_values(3) should not allow a successful parse if I pass 6 values. If you play around with this, you find a couple of other interesting symptoms. In general, if I call number_of_values(x) and pass N values:
When N == 0, the parse incorrectly succeeds. (This is because N % x == 0 - see below where I explain what's going on here).
When x > 1:
When 0 < N < x, the parse correctly fails.
When N == x, the parse correctly succeeds.
When N > x && N % x == 0, the parse incorrectly succeeds.
When x == 1 && N > 1, the parse correctly fails, but returns an UnknownArgument error instead of the WrongNumberOfValues error, which is interesting but actually I think correct (because the parser is trying to interpret the extra value as a new, unknown argument rather than an additional value to the previous argument).
This is also different from setting max_values(3) and min_values(3), which does what I want (only allow exactly three argument values), but also does so incorrectly (by ignoring multiple occurrences altogether!) and renders a different error message (max_values returns TooManyValues and min_values returns TooFewValues instead of WrongNumberOfValues).
What's going on?
The issue is that validation of argument counts for multiple values and multiple occurrences of arguments is fundamentally broken because Clap doesn't store which argument values were matched in which occurrences.
I have some failing test cases set up in my fork at master...liftM:bug/number-of-values-multioccurrence. I tried to put together a PR patch, but I'm not sure I understand all the implications for how multiple values and occurrences are handled.
For some reason, calling number_of_values sends us down the path to
and sets ArgSettings::MultipleValues and ArgSettings::MultipleOccurrences. When the number_of_values validator then runs, it goes down the code path at
which checks that ((ma.vals.len() as u64) % num) != 0.
I assume this is happening as a hack in order to support multiple values and multiple occurrences. Unfortunately, this doesn't work:
Documentation on the meaning of MultipleValues seems to be incorrect (see multiple_values() documentation wrong #1828), but I think the intention of this setting is to allow multiple values to be passed into a single occurrence of an argument.
The documentation for MultipleOccurrences says that this setting is intended to allow multiple occurrences of an argument.
Therefore, I think the correct, intended behavior for validating argument counts should be: (1) if MultipleOccurrences is set, allow any number of occurrences per argument, and (2) if MultipleValues is set, allow a validated number of values per argument occurrence.
Unfortunately, the current definition of MatchedArg at
makes this impossible, because we cannot determine which values were passed through which occurrence. I believe the correct fix here is to refactor MatchedArg so that we not only count occurrences, but we in fact store each vector of occurrences (so that we can validate the correct count of values per occurrence).
Alternatively, another option is to make the design call to drop support for either multiple values or multiple occurrences. I think #2031 makes a pretty compelling argument that supporting both of them is confusing for both developers and users.
The text was updated successfully, but these errors were encountered:
elldritch
changed the title
number_of_values incorrectly allows argument counts that are integer multiples handles combinations of _multiple values_ and _multiple occurrences_ of an argumentnumber_of_values(n) incorrectly allows argument counts that are integer multiples of nNov 28, 2020
Yeah, I still need to go through all the multiple related issues and do a proper design for v3. Just short of time. If someone wants to pick this up and do a proper design and implement it, I would really appreciate it.
Reproducing the bug
Okay, this one is a bit of a headscratcher. I've been digging into this with Rust
1.48.0
and Clap3.0.0-beta.2
, although I bet2.x.x
suffers from the same problem given its definition ofstruct MatchedArg
.First, a test case:
This test case should obviously fail: specifying
number_of_values(3)
should not allow a successful parse if I pass 6 values. If you play around with this, you find a couple of other interesting symptoms. In general, if I callnumber_of_values(x)
and passN
values:N == 0
, the parse incorrectly succeeds. (This is becauseN % x == 0
- see below where I explain what's going on here).x > 1
:0 < N < x
, the parse correctly fails.N == x
, the parse correctly succeeds.N > x && N % x == 0
, the parse incorrectly succeeds.x == 1 && N > 1
, the parse correctly fails, but returns anUnknownArgument
error instead of theWrongNumberOfValues
error, which is interesting but actually I think correct (because the parser is trying to interpret the extra value as a new, unknown argument rather than an additional value to the previous argument).This is also different from setting
max_values(3)
andmin_values(3)
, which does what I want (only allow exactly three argument values), but also does so incorrectly (by ignoring multiple occurrences altogether!) and renders a different error message (max_values
returnsTooManyValues
andmin_values
returnsTooFewValues
instead ofWrongNumberOfValues
).What's going on?
The issue is that validation of argument counts for multiple values and multiple occurrences of arguments is fundamentally broken because Clap doesn't store which argument values were matched in which occurrences.
I have some failing test cases set up in my fork at master...liftM:bug/number-of-values-multioccurrence. I tried to put together a PR patch, but I'm not sure I understand all the implications for how multiple values and occurrences are handled.
For some reason, calling
number_of_values
sends us down the path toclap/src/build/arg/mod.rs
Lines 4219 to 4239 in 08b2f4d
ArgSettings::MultipleValues
andArgSettings::MultipleOccurrences
. When thenumber_of_values
validator then runs, it goes down the code path atclap/src/parse/validator.rs
Lines 445 to 466 in 08b2f4d
((ma.vals.len() as u64) % num) != 0
.I assume this is happening as a hack in order to support multiple values and multiple occurrences. Unfortunately, this doesn't work:
MultipleValues
seems to be incorrect (see multiple_values() documentation wrong #1828), but I think the intention of this setting is to allow multiple values to be passed into a single occurrence of an argument.MultipleOccurrences
says that this setting is intended to allow multiple occurrences of an argument.MultipleOccurrences
is set, allow any number of occurrences per argument, and (2) ifMultipleValues
is set, allow a validated number of values per argument occurrence.Unfortunately, the current definition of
MatchedArg
atclap/src/parse/matches/matched_arg.rs
Lines 12 to 18 in 08b2f4d
MatchedArg
so that we not only count occurrences, but we in fact store each vector of occurrences (so that we can validate the correct count of values per occurrence).Alternatively, another option is to make the design call to drop support for either multiple values or multiple occurrences. I think #2031 makes a pretty compelling argument that supporting both of them is confusing for both developers and users.
The text was updated successfully, but these errors were encountered: