clap-rs / clap

A full featured, fast Command Line Argument Parser for Rust
docs.rs/clap
Apache License 2.0
14.29k stars 1.04k forks source link

`Vec<Option<_>>` with `num_args(0..)` Ignores flags passed with no value #5250

Open Maurdekye opened 11 months ago

Maurdekye commented 11 months ago

Please complete the following tasks

Rust Version

rustc 1.76.0-nightly (4cb3beec8 2023-11-18)

Clap Version

clap = { version = "4.4.11", features = ["derive", "debug"] }

Minimal reproducible code

use clap::Parser;

#[derive(Parser)]
struct Args {
    #[clap(short, num_args(0..))]
    s: Vec<Option<String>>
}

fn main() {
    let args = Args::parse();
    println!("{:#?}", args.s);
}

Steps to reproduce the bug with the above code

cargo run -- -s -s test -s

Actual Behaviour

[
    Some(
        "test",
    ),
]

Expected Behaviour

[
    None,
    Some(
        "test",
    ),
    None,
]

Additional Context

No response

Debug Output

[clap_builder::builder::command]Command::_do_parse
[clap_builder::builder::command]Command::_build: name="test_rs"
[clap_builder::builder::command]Command::_propagate:test_rs
[clap_builder::builder::command]Command::_check_help_and_version:test_rs expand_help_tree=false
[clap_builder::builder::command]Command::long_help_exists
[clap_builder::builder::command]Command::_check_help_and_version: Building default --help
[clap_builder::builder::command]Command::_propagate_global_args:test_rs
[clap_builder::builder::debug_asserts]Command::_debug_asserts
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:s
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:help
[clap_builder::builder::debug_asserts]Command::_verify_positionals
[clap_builder::parser::parser]Parser::get_matches_with
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"-s"'
[clap_builder::parser::parser]Parser::possible_subcommand: arg=Ok("-s")
[clap_builder::parser::parser]Parser::get_matches_with: sc=None
[clap_builder::parser::parser]Parser::parse_short_arg: short_arg=ShortFlags { inner: "s", utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['s']) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s: Found valid opt or flag
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s: val="", short_arg=ShortFlags { inner: "s", utf8_prefix: CharIndices { front_offset: 1, iter: Chars([]) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_opt_value; arg=s, val=None, has_eq=false
[clap_builder::parser::parser]Parser::parse_opt_value; arg.settings=ArgFlags(0)
[clap_builder::parser::parser]Parser::parse_opt_value; Checking for val...
[clap_builder::parser::parser]Parser::parse_opt_value: More arg vals required...
[clap_builder::parser::parser]Parser::get_matches_with: After parse_short_arg Opt("s")
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"-s"'
[clap_builder::parser::parser]Parser::parse_short_arg: short_arg=ShortFlags { inner: "s", utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['s']) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s: Found valid opt or flag
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s: val="", short_arg=ShortFlags { inner: "s", utf8_prefix: CharIndices { front_offset: 1, iter: Chars([]) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_opt_value; arg=s, val=None, has_eq=false
[clap_builder::parser::parser]Parser::parse_opt_value; arg.settings=ArgFlags(0)
[clap_builder::parser::parser]Parser::parse_opt_value; Checking for val...
[clap_builder::parser::parser]Parser::parse_opt_value: More arg vals required...
[clap_builder::parser::parser]Parser::resolve_pending: id="s"
[clap_builder::parser::parser]Parser::react action=Append, identifier=Some(Short), source=CommandLine
[clap_builder::parser::parser]Parser::react: cur_idx:=1
[clap_builder::parser::parser]Parser::remove_overrides: id="s"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="s", source=CommandLine
[clap_builder::builder::command]Command::groups_for_arg: id="s"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="Args", source=CommandLine
[clap_builder::parser::parser]Parser::push_arg_values: []
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=s, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=0..=18446744073709551615, actual=0
[clap_builder::parser::parser]Parser::react not enough values passed in, leaving it to the validator to complain
[clap_builder::parser::parser]Parser::get_matches_with: After parse_short_arg Opt("s")
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"test"'
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=s, pending=1
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=0..=18446744073709551615, actual=1
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"-s"'
[clap_builder::parser::parser]Parser::parse_short_arg: short_arg=ShortFlags { inner: "s", utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['s']) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s: Found valid opt or flag
[clap_builder::parser::parser]Parser::parse_short_arg:iter:s: val="", short_arg=ShortFlags { inner: "s", utf8_prefix: CharIndices { front_offset: 1, iter: Chars([]) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_opt_value; arg=s, val=None, has_eq=false
[clap_builder::parser::parser]Parser::parse_opt_value; arg.settings=ArgFlags(0)
[clap_builder::parser::parser]Parser::parse_opt_value; Checking for val...
[clap_builder::parser::parser]Parser::parse_opt_value: More arg vals required...
[clap_builder::parser::parser]Parser::resolve_pending: id="s"
[clap_builder::parser::parser]Parser::react action=Append, identifier=Some(Short), source=CommandLine
[clap_builder::parser::parser]Parser::react: cur_idx:=2
[clap_builder::parser::parser]Parser::remove_overrides: id="s"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="s", source=CommandLine
[clap_builder::builder::command]Command::groups_for_arg: id="s"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="Args", source=CommandLine
[clap_builder::parser::parser]Parser::push_arg_values: ["test"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=3
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=s, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=0..=18446744073709551615, actual=0
[clap_builder::parser::parser]Parser::react not enough values passed in, leaving it to the validator to complain
[clap_builder::parser::parser]Parser::get_matches_with: After parse_short_arg Opt("s")
[clap_builder::parser::parser]Parser::resolve_pending: id="s"
[clap_builder::parser::parser]Parser::react action=Append, identifier=Some(Short), source=CommandLine
[clap_builder::parser::parser]Parser::react: cur_idx:=4
[clap_builder::parser::parser]Parser::remove_overrides: id="s"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="s", source=CommandLine
[clap_builder::builder::command]Command::groups_for_arg: id="s"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="Args", source=CommandLine
[clap_builder::parser::parser]Parser::push_arg_values: []
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=s, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=0..=18446744073709551615, actual=0
[clap_builder::parser::parser]Parser::react not enough values passed in, leaving it to the validator to complain
[clap_builder::parser::parser]Parser::add_defaults
[clap_builder::parser::parser]Parser::add_defaults:iter:s:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:s: doesn't have default vals
[clap_builder::parser::parser]Parser::add_defaults:iter:help:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:help: doesn't have default vals
[clap_builder::parser::validator]Validator::validate
[clap_builder::builder::command]Command::groups_for_arg: id="s"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="s", conflicts=[]
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="Args", conflicts=[]
[clap_builder::parser::validator]Validator::validate: needs_val_of="s"
[clap_builder::parser::validator]Validator::validate_conflicts
[clap_builder::parser::validator]Validator::validate_exclusive
[clap_builder::parser::validator]Validator::validate_conflicts::iter: id="s"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="s"
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=[]
[clap_builder::parser::validator]Validator::validate_required: required=ChildGraph([])
[clap_builder::parser::validator]Validator::gather_requires
[clap_builder::parser::validator]Validator::gather_requires:iter:"s"
[clap_builder::parser::validator]Validator::gather_requires:iter:"Args"
[clap_builder::parser::validator]Validator::gather_requires:iter:"Args":group
[clap_builder::parser::validator]Validator::validate_required: is_exclusive_present=false
[clap_builder::parser::arg_matcher]ArgMatcher::get_global_values: global_arg_vec=[]
[
    Some(
        "test",
    ),
]
epage commented 11 months ago

To double check, do you mean num_args(0..))] or num_args(0..=1))]? num_args controls how many values per flag, not how many values total.

If its num_args(0..))], then you likely want to use Vec<Vec<String>> instead. It is an interesting idea though to support Vec<Option<String>> for num_args(0..=1))].

Maurdekye commented 11 months ago

i didn't know that's what the flags did; i was just trying to find a way to allow clap to accept a single option repeatedly, which may or may not accept values