Skip to content

"allow_hyphen_values" absorbs short options (-f), but not long options (--flag) #3880

Closed
@david0u0

Description

@david0u0

Please complete the following tasks

Rust Version

rustc 1.60.0-nightly (88fb06a1f 2022-02-05)

Clap Version

3.1.8

Minimal reproducible code

use clap::Parser;

#[derive(Parser, Debug)]
#[clap(disable_help_subcommand = true, args_override_self = true)]
pub enum Root {
    #[clap(allow_hyphen_values = true)]
    Run {
        #[clap(long, short)]
        flag: bool,
        #[clap(default_value = "-")]
        query: String,
        #[clap(allow_hyphen_values = true)]
        args: Vec<String>,
    },
}

fn handle_args(s: &str) -> Root {
    Root::from_iter(s.split(' '))
}

fn extract(r: Root) -> Vec<String> {
    match r {
        Root::Run {args, ..} => args
    }
}

fn main() {
    let root = handle_args("test run query -f");
    println!("{:?}", root);
    assert_eq!(extract(root), vec!["-f"]);

    let root = handle_args("test run query --flag");
    println!("{:?}", root);
    assert_eq!(extract(root), vec!["--flag"]); // <- fail!!
}

Steps to reproduce the bug with the above code

cargo run

The second assert will fail

Actual Behaviour

With argument "test run query --flag", the "--flag" will be treated as the option, instead of the positional argument

This seems be not consistent, because the short option "-f" will be treated as the positional argument

Expected Behaviour

Treat the long option as positional argument

Or treat the short option as option?

Additional Context

No response

Debug Output

first

[ clap::builder::command] Command::_do_parse
[ clap::builder::command] Command::_build: name="clap-hyphen"
[ clap::builder::command] Command::_propagate:clap-hyphen
[ clap::builder::command] Command::_check_help_and_version: clap-hyphen
[ clap::builder::command] Command::_check_help_and_version: Removing generated version
[ clap::builder::command] Command::_propagate_global_args:clap-hyphen
[ clap::builder::command] Command::_propagate removing run's help
[ clap::builder::command] Command::_propagate pushing help to run
[ clap::builder::command] Command::_derive_display_order:clap-hyphen
[ clap::builder::command] Command::_derive_display_order:run
[clap::builder::debug_asserts] Command::_debug_asserts
[clap::builder::debug_asserts] Arg::_debug_asserts:help
[clap::builder::debug_asserts] Command::_verify_positionals
[ clap::parser::parser] Parser::get_matches_with
[ clap::parser::parser] Parser::get_matches_with: Begin parsing 'RawOsStr("run")' ([114, 117, 110])
[ clap::parser::parser] Parser::get_matches_with: Positional counter...1
[ clap::parser::parser] Parser::get_matches_with: Low index multiples...false
[ clap::parser::parser] Parser::possible_subcommand: arg=Ok("run")
[ clap::parser::parser] Parser::get_matches_with: sc=Some("run")
[ clap::parser::parser] Parser::parse_subcommand
[ clap::output::usage] Usage::get_required_usage_from: incls=[], matcher=false, incl_last=true
[ clap::output::usage] Usage::get_required_usage_from: unrolled_reqs={}
[ clap::output::usage] Usage::get_required_usage_from: ret_val={}
[ clap::builder::command] Command::_build_subcommand Setting bin_name of run to "test run"
[ clap::builder::command] Command::_build_subcommand Setting display_name of run to "clap-hyphen-run"
[ clap::builder::command] Command::_build: name="run"
[ clap::builder::command] Command::_propagate:run
[ clap::builder::command] Command::_check_help_and_version: run
[ clap::builder::command] Command::_check_help_and_version: Removing generated version
[ clap::builder::command] Command::_propagate_global_args:run
[ clap::builder::command] Command::_derive_display_order:run
[clap::builder::debug_asserts] Command::_debug_asserts
[clap::builder::debug_asserts] Arg::_debug_asserts:flag
[clap::builder::debug_asserts] Arg::_debug_asserts:query
[clap::builder::debug_asserts] Arg::_debug_asserts:args
[clap::builder::debug_asserts] Arg::_debug_asserts:help
[clap::builder::debug_asserts] Command::_verify_positionals
[ clap::parser::parser] Parser::parse_subcommand: About to parse sc=run
[ clap::parser::parser] Parser::get_matches_with
[ clap::parser::parser] Parser::get_matches_with: Begin parsing 'RawOsStr("query")' ([113, 117, 101, 114, 121])
[ clap::parser::parser] Parser::get_matches_with: Positional counter...1
[ clap::parser::parser] Parser::get_matches_with: Low index multiples...false
[ clap::parser::parser] Parser::possible_subcommand: arg=Ok("query")
[ clap::parser::parser] Parser::get_matches_with: sc=None
[ clap::parser::parser] Parser::split_arg_values; arg=query, val=RawOsStr("query")
[ clap::parser::parser] Parser::split_arg_values; trailing_values=false, DontDelimTrailingVals=false
[ clap::parser::parser] Parser::get_matches_with: Begin parsing 'RawOsStr("-f")' ([45, 102])
[ clap::parser::parser] Parser::get_matches_with: Positional counter...2
[ clap::parser::parser] Parser::get_matches_with: Low index multiples...false
[ clap::parser::parser] Parser::possible_subcommand: arg=Ok("-f")
[ clap::parser::parser] Parser::get_matches_with: sc=None
[ clap::parser::parser] Parser::parse_short_arg: short_arg=ShortFlags { inner: RawOsStr("f"), utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['f']) }, invalid_suffix: None }
[ clap::parser::parser] Parser::parse_short_args: positional at 2 allows hyphens
[ clap::parser::parser] Parser::get_matches_with: After parse_short_arg MaybeHyphenValue
[ clap::parser::parser] Parser::resolve_pending: id=query
[ clap::parser::parser] Parser::react action=StoreValue, identifier=Some(Index), source=CommandLine
[ clap::parser::parser] Parser::remove_overrides: id=query
[ clap::parser::arg_matcher] ArgMatcher::start_occurrence_of_arg: id=query
[ clap::builder::command] Command::groups_for_arg: id=query
[ clap::parser::parser] Parser::push_arg_values: ["query"]
[ clap::parser::parser] Parser::add_single_val_to_arg: cur_idx:=1
[ clap::builder::command] Command::groups_for_arg: id=query
[ clap::parser::arg_matcher] ArgMatcher::needs_more_vals: o=query, resolved=1, pending=0
[ clap::parser::parser] Parser::split_arg_values; arg=args, val=RawOsStr("-f")
[ clap::parser::parser] Parser::split_arg_values; trailing_values=true, DontDelimTrailingVals=false
[ clap::parser::parser] Parser::resolve_pending: id=args
[ clap::parser::parser] Parser::react action=StoreValue, identifier=Some(Index), source=CommandLine
[ clap::parser::parser] Parser::remove_overrides: id=args
[ clap::parser::arg_matcher] ArgMatcher::start_occurrence_of_arg: id=args
[ clap::builder::command] Command::groups_for_arg: id=args
[ clap::parser::parser] Parser::push_arg_values: ["-f"]
[ clap::parser::parser] Parser::add_single_val_to_arg: cur_idx:=2
[ clap::builder::command] Command::groups_for_arg: id=args
[ clap::parser::arg_matcher] ArgMatcher::needs_more_vals: o=args, resolved=1, pending=0
[ clap::parser::parser] Parser::add_defaults
[ clap::parser::parser] Parser::add_defaults:iter:flag:
[ clap::parser::parser] Parser::add_default_value:iter:flag: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:flag: doesn't have default vals
[ clap::parser::parser] Parser::add_defaults:iter:query:
[ clap::parser::parser] Parser::add_default_value:iter:query: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:query: has default vals
[ clap::parser::parser] Parser::add_default_value:iter:query: was used
[ clap::parser::parser] Parser::add_defaults:iter:args:
[ clap::parser::parser] Parser::add_default_value:iter:args: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:args: doesn't have default vals
[ clap::parser::parser] Parser::add_defaults:iter:help:
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default vals
[ clap::parser::validator] Validator::validate
[ clap::parser::validator] Validator::validate_conflicts
[ clap::parser::validator] Validator::validate_exclusive
[ clap::parser::validator] Validator::validate_exclusive:iter:query
[ clap::parser::validator] Validator::validate_exclusive:iter:args
[ clap::parser::validator] Validator::validate_conflicts::iter: id=query
[ clap::parser::validator] Conflicts::gather_conflicts: arg=query
[ clap::builder::command] Command::groups_for_arg: id=query
[ clap::parser::validator] Conflicts::gather_direct_conflicts id=query, conflicts=[]
[ clap::builder::command] Command::groups_for_arg: id=args
[ clap::parser::validator] Conflicts::gather_direct_conflicts id=args, conflicts=[]
[ clap::parser::validator] Conflicts::gather_conflicts: conflicts=[]
[ clap::parser::validator] Validator::validate_conflicts::iter: id=args
[ clap::parser::validator] Conflicts::gather_conflicts: arg=args
[ clap::parser::validator] Conflicts::gather_conflicts: conflicts=[]
[ clap::parser::validator] Validator::validate_required: required=ChildGraph([])
[ clap::parser::validator] Validator::gather_requires
[ clap::parser::validator] Validator::gather_requires:iter:query
[ clap::parser::validator] Validator::gather_requires:iter:args
[ clap::parser::validator] Validator::validate_required: is_exclusive_present=false
[ clap::parser::validator] Validator::validate_required_unless
[ clap::parser::validator] Validator::validate_matched_args
[ clap::parser::validator] Validator::validate_matched_args:iter:query: vals=Flatten {
inner: FlattenCompat {
iter: Fuse {
iter: Some(
Iter(
[
[
AnyValue {
inner: alloc::string::String,
},
],
],
),
),
},
frontiter: None,
backiter: None,
},
}
[ clap::parser::validator] Validator::validate_arg_num_vals
[ clap::parser::validator] Validator::validate_arg_values: arg="query"
[ clap::parser::validator] Validator::validate_arg_values: checking validator...
[ clap::parser::validator] good
[ clap::parser::validator] Validator::validate_arg_num_occurs: "query"=1
[ clap::parser::validator] Validator::validate_matched_args:iter:args: vals=Flatten {
inner: FlattenCompat {
iter: Fuse {
iter: Some(
Iter(
[
[
AnyValue {
inner: alloc::string::String,
},
],
],
),
),
},
frontiter: None,
backiter: None,
},
}
[ clap::parser::validator] Validator::validate_arg_num_vals
[ clap::parser::validator] Validator::validate_arg_values: arg="args"
[ clap::parser::validator] Validator::validate_arg_values: checking validator...
[ clap::parser::validator] good
[ clap::parser::validator] Validator::validate_arg_num_occurs: "args"=1
[ clap::parser::parser] Parser::add_defaults
[ clap::parser::parser] Parser::add_defaults:iter:help:
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default vals
[ clap::parser::validator] Validator::validate
[ clap::parser::validator] Validator::validate_conflicts
[ clap::parser::validator] Validator::validate_exclusive
[ clap::parser::validator] Validator::validate_required: required=ChildGraph([])
[ clap::parser::validator] Validator::gather_requires
[ clap::parser::validator] Validator::validate_required: is_exclusive_present=false
[ clap::parser::validator] Validator::validate_required_unless
[ clap::parser::validator] Validator::validate_matched_args
[ clap::parser::arg_matcher] ArgMatcher::get_global_values: global_arg_vec=[help, help]

second

[ clap::builder::command] Command::_do_parse
[ clap::builder::command] Command::_build: name="clap-hyphen"
[ clap::builder::command] Command::_propagate:clap-hyphen
[ clap::builder::command] Command::_check_help_and_version: clap-hyphen
[ clap::builder::command] Command::_check_help_and_version: Removing generated version
[ clap::builder::command] Command::_propagate_global_args:clap-hyphen
[ clap::builder::command] Command::_propagate removing run's help
[ clap::builder::command] Command::_propagate pushing help to run
[ clap::builder::command] Command::_derive_display_order:clap-hyphen
[ clap::builder::command] Command::_derive_display_order:run
[clap::builder::debug_asserts] Command::_debug_asserts
[clap::builder::debug_asserts] Arg::_debug_asserts:help
[clap::builder::debug_asserts] Command::_verify_positionals
[ clap::parser::parser] Parser::get_matches_with
[ clap::parser::parser] Parser::get_matches_with: Begin parsing 'RawOsStr("run")' ([114, 117, 110])
[ clap::parser::parser] Parser::get_matches_with: Positional counter...1
[ clap::parser::parser] Parser::get_matches_with: Low index multiples...false
[ clap::parser::parser] Parser::possible_subcommand: arg=Ok("run")
[ clap::parser::parser] Parser::get_matches_with: sc=Some("run")
[ clap::parser::parser] Parser::parse_subcommand
[ clap::output::usage] Usage::get_required_usage_from: incls=[], matcher=false, incl_last=true
[ clap::output::usage] Usage::get_required_usage_from: unrolled_reqs={}
[ clap::output::usage] Usage::get_required_usage_from: ret_val={}
[ clap::builder::command] Command::_build_subcommand Setting bin_name of run to "test run"
[ clap::builder::command] Command::_build_subcommand Setting display_name of run to "clap-hyphen-run"
[ clap::builder::command] Command::_build: name="run"
[ clap::builder::command] Command::_propagate:run
[ clap::builder::command] Command::_check_help_and_version: run
[ clap::builder::command] Command::_check_help_and_version: Removing generated version
[ clap::builder::command] Command::_propagate_global_args:run
[ clap::builder::command] Command::_derive_display_order:run
[clap::builder::debug_asserts] Command::_debug_asserts
[clap::builder::debug_asserts] Arg::_debug_asserts:flag
[clap::builder::debug_asserts] Arg::_debug_asserts:query
[clap::builder::debug_asserts] Arg::_debug_asserts:args
[clap::builder::debug_asserts] Arg::_debug_asserts:help
[clap::builder::debug_asserts] Command::_verify_positionals
[ clap::parser::parser] Parser::parse_subcommand: About to parse sc=run
[ clap::parser::parser] Parser::get_matches_with
[ clap::parser::parser] Parser::get_matches_with: Begin parsing 'RawOsStr("query")' ([113, 117, 101, 114, 121])
[ clap::parser::parser] Parser::get_matches_with: Positional counter...1
[ clap::parser::parser] Parser::get_matches_with: Low index multiples...false
[ clap::parser::parser] Parser::possible_subcommand: arg=Ok("query")
[ clap::parser::parser] Parser::get_matches_with: sc=None
[ clap::parser::parser] Parser::split_arg_values; arg=query, val=RawOsStr("query")
[ clap::parser::parser] Parser::split_arg_values; trailing_values=false, DontDelimTrailingVals=false
[ clap::parser::parser] Parser::get_matches_with: Begin parsing 'RawOsStr("--flag")' ([45, 45, 102, 108, 97, 103])
[ clap::parser::parser] Parser::get_matches_with: Positional counter...2
[ clap::parser::parser] Parser::get_matches_with: Low index multiples...false
[ clap::parser::parser] Parser::possible_subcommand: arg=Ok("--flag")
[ clap::parser::parser] Parser::get_matches_with: sc=None
[ clap::parser::parser] Parser::parse_long_arg
[ clap::parser::parser] Parser::parse_long_arg: Does it contain '='...
[ clap::parser::parser] Parser::parse_long_arg: Found valid arg or flag '--flag'
[ clap::parser::parser] Parser::parse_long_arg("flag"): Presence validated
[ clap::parser::parser] Parser::resolve_pending: id=query
[ clap::parser::parser] Parser::react action=StoreValue, identifier=Some(Index), source=CommandLine
[ clap::parser::parser] Parser::remove_overrides: id=query
[ clap::parser::arg_matcher] ArgMatcher::start_occurrence_of_arg: id=query
[ clap::builder::command] Command::groups_for_arg: id=query
[ clap::parser::parser] Parser::push_arg_values: ["query"]
[ clap::parser::parser] Parser::add_single_val_to_arg: cur_idx:=1
[ clap::builder::command] Command::groups_for_arg: id=query
[ clap::parser::arg_matcher] ArgMatcher::needs_more_vals: o=query, resolved=1, pending=0
[ clap::parser::parser] Parser::react action=IncOccurrence, identifier=Some(Long), source=CommandLine
[ clap::parser::parser] Parser::react: cur_idx:=2
[ clap::parser::parser] Parser::remove_overrides: id=flag
[ clap::parser::parser] Parser::remove_overrides:iter:flag: removing
[ clap::parser::arg_matcher] ArgMatcher::start_occurrence_of_arg: id=flag
[ clap::builder::command] Command::groups_for_arg: id=flag
[ clap::parser::parser] Parser::get_matches_with: After parse_long_arg ValuesDone
[ clap::parser::parser] Parser::add_defaults
[ clap::parser::parser] Parser::add_defaults:iter:flag:
[ clap::parser::parser] Parser::add_default_value:iter:flag: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:flag: doesn't have default vals
[ clap::parser::parser] Parser::add_defaults:iter:query:
[ clap::parser::parser] Parser::add_default_value:iter:query: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:query: has default vals
[ clap::parser::parser] Parser::add_default_value:iter:query: was used
[ clap::parser::parser] Parser::add_defaults:iter:args:
[ clap::parser::parser] Parser::add_default_value:iter:args: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:args: doesn't have default vals
[ clap::parser::parser] Parser::add_defaults:iter:help:
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default vals
[ clap::parser::validator] Validator::validate
[ clap::parser::validator] Validator::validate_conflicts
[ clap::parser::validator] Validator::validate_exclusive
[ clap::parser::validator] Validator::validate_exclusive:iter:query
[ clap::parser::validator] Validator::validate_exclusive:iter:flag
[ clap::parser::validator] Validator::validate_conflicts::iter: id=query
[ clap::parser::validator] Conflicts::gather_conflicts: arg=query
[ clap::builder::command] Command::groups_for_arg: id=query
[ clap::parser::validator] Conflicts::gather_direct_conflicts id=query, conflicts=[]
[ clap::builder::command] Command::groups_for_arg: id=flag
[ clap::parser::validator] Conflicts::gather_direct_conflicts id=flag, conflicts=[flag]
[ clap::parser::validator] Conflicts::gather_conflicts: conflicts=[]
[ clap::parser::validator] Validator::validate_conflicts::iter: id=flag
[ clap::parser::validator] Conflicts::gather_conflicts: arg=flag
[ clap::parser::validator] Conflicts::gather_conflicts: conflicts=[]
[ clap::parser::validator] Validator::validate_required: required=ChildGraph([])
[ clap::parser::validator] Validator::gather_requires
[ clap::parser::validator] Validator::gather_requires:iter:query
[ clap::parser::validator] Validator::gather_requires:iter:flag
[ clap::parser::validator] Validator::validate_required: is_exclusive_present=false
[ clap::parser::validator] Validator::validate_required_unless
[ clap::parser::validator] Validator::validate_matched_args
[ clap::parser::validator] Validator::validate_matched_args:iter:query: vals=Flatten {
inner: FlattenCompat {
iter: Fuse {
iter: Some(
Iter(
[
[
AnyValue {
inner: alloc::string::String,
},
],
],
),
),
},
frontiter: None,
backiter: None,
},
}
[ clap::parser::validator] Validator::validate_arg_num_vals
[ clap::parser::validator] Validator::validate_arg_values: arg="query"
[ clap::parser::validator] Validator::validate_arg_values: checking validator...
[ clap::parser::validator] good
[ clap::parser::validator] Validator::validate_arg_num_occurs: "query"=1
[ clap::parser::validator] Validator::validate_matched_args:iter:flag: vals=Flatten {
inner: FlattenCompat {
iter: Fuse {
iter: Some(
Iter(
[
[],
],
),
),
},
frontiter: None,
backiter: None,
},
}
[ clap::parser::validator] Validator::validate_arg_num_vals
[ clap::parser::validator] Validator::validate_arg_values: arg="flag"
[ clap::parser::validator] Validator::validate_arg_num_occurs: "flag"=1
[ clap::parser::parser] Parser::add_defaults
[ clap::parser::parser] Parser::add_defaults:iter:help:
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default missing vals
[ clap::parser::parser] Parser::add_default_value: doesn't have conditional defaults
[ clap::parser::parser] Parser::add_default_value:iter:help: doesn't have default vals
[ clap::parser::validator] Validator::validate
[ clap::parser::validator] Validator::validate_conflicts
[ clap::parser::validator] Validator::validate_exclusive
[ clap::parser::validator] Validator::validate_required: required=ChildGraph([])
[ clap::parser::validator] Validator::gather_requires
[ clap::parser::validator] Validator::validate_required: is_exclusive_present=false
[ clap::parser::validator] Validator::validate_required_unless
[ clap::parser::validator] Validator::validate_matched_args
[ clap::parser::arg_matcher] ArgMatcher::get_global_values: global_arg_vec=[help, help]

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-parsingArea: Parser's logic and needs it changed somehow.C-bugCategory: bugM-breaking-changeMeta: Implementing or merging this will introduce a breaking change.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions