Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subcommand flags can't conflict with arguments #5297

Closed
2 tasks done
nix-enthusiast opened this issue Jan 10, 2024 · 2 comments · Fixed by #5298
Closed
2 tasks done

Subcommand flags can't conflict with arguments #5297

nix-enthusiast opened this issue Jan 10, 2024 · 2 comments · Fixed by #5298
Labels
A-parsing Area: Parser's logic and needs it changed somehow. C-bug Category: Updating dependencies E-medium Call for participation: Experience needed to fix: Medium / intermediate

Comments

@nix-enthusiast
Copy link

Please complete the following tasks

Rust Version

1.75.0

Clap Version

4.4.14

Minimal reproducible code

use clap::{Arg, ArgAction, Command};
use std::process::exit;

fn main() {
    let matches = Command::new("cmd")
        .args_conflicts_with_subcommands(true)
        .arg(
            Arg::new("foo")
                .short('f')
                .long("foo")
                .action(ArgAction::SetTrue)
                .exclusive(true),
        )
        .subcommand(
            Command::new("bar").short_flag('B').long_flag("bar").arg(
                Arg::new("baz")
                    .short('b')
                    .long("baz")
                    .action(ArgAction::Append)
                    .num_args(1..),
            ),
        )
        .get_matches();

    if matches.get_flag("foo") {
        println!("-f is used")
    };
    println!(
        "{:?}",
        matches
            .subcommand_matches("bar")
            .unwrap_or_else(|| {
                println!("Bar subcommand is not used");
                exit(0)
            })
            .get_many::<String>("baz").unwrap().map(|x|x.to_string()).collect::<Vec<String>>()
    );
}

Steps to reproduce the bug with the above code

cargo run -- -f -Bb 1 2

Actual Behaviour

-f is used
["1", "2"]

Expected Behaviour

error: -f can not be used with -B subcommand 

example:
     -f <foo>

Additional Context

Using it with/without.subcommand_precedence_over_arg(true) won't make any difference

Debug Output

[clap_builder::builder::command]Command::_do_parse
[clap_builder::builder::command]Command::_build: name="cmd"
[clap_builder::builder::command]Command::_propagate:cmd
[clap_builder::builder::command]Command::_check_help_and_version:cmd 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::_check_help_and_version: Building help subcommand
[clap_builder::builder::command]Command::_propagate_global_args:cmd
[clap_builder::builder::debug_asserts]Command::_debug_asserts
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:foo
[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 '"-f"'
[clap_builder::parser::parser]Parser::possible_subcommand: arg=Ok("-f")
[clap_builder::parser::parser]Parser::get_matches_with: sc=None
[clap_builder::parser::parser]Parser::parse_short_arg: short_arg=ShortFlags { inner: "f", utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['f']) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_short_arg:iter:f
[clap_builder::parser::parser]Parser::parse_short_arg:iter:f: Found valid opt or flag
[clap_builder::parser::parser]Parser::react action=SetTrue, identifier=Some(Short), source=CommandLine
[clap_builder::parser::parser]Parser::react: has default_missing_vals
[clap_builder::parser::parser]Parser::remove_overrides: id="foo"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="foo", source=CommandLine
[clap_builder::builder::command]Command::groups_for_arg: id="foo"
[clap_builder::parser::parser]Parser::push_arg_values: ["true"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=1
[clap_builder::parser::parser]Parser::get_matches_with: After parse_short_arg ValuesDone
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"-Bb"'
[clap_builder::parser::parser]Parser::possible_subcommand: arg=Ok("-Bb")
[clap_builder::parser::parser]Parser::get_matches_with: sc=None
[clap_builder::parser::parser]Parser::parse_short_arg: short_arg=ShortFlags { inner: "Bb", utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['B', 'b']) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_short_arg:iter:B
[clap_builder::parser::parser]Parser::parse_short_arg:iter:B: subcommand=bar
[clap_builder::parser::parser]Parser::parse_short_arg: cur_idx:=2
[clap_builder::parser::parser]Parser::get_matches_with: After parse_short_arg FlagSubCommand("bar")
[clap_builder::parser::parser]Parser::get_matches_with:FlagSubCommandShort: subcmd_name=bar, keep_state=true, flag_subcmd_skip=1
[clap_builder::parser::parser]Parser::parse_subcommand
[clap_builder::builder::command]Command::_build_subcommand Setting bin_name of bar to "args bar"
[clap_builder::builder::command]Command::_build_subcommand Setting display_name of bar to "cmd-bar"
[clap_builder::builder::command]Command::_build: name="bar"
[clap_builder::builder::command]Command::_propagate:bar
[clap_builder::builder::command]Command::_check_help_and_version:bar 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:bar
[clap_builder::builder::debug_asserts]Command::_debug_asserts
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:baz
[clap_builder::builder::debug_asserts]Arg::_debug_asserts:help
[clap_builder::builder::debug_asserts]Command::_verify_positionals
[clap_builder::parser::parser]Parser::parse_subcommand: About to parse sc=bar
[clap_builder::parser::parser]Parser::get_matches_with
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"-Bb"'
[clap_builder::parser::parser]Parser::possible_subcommand: arg=Ok("-Bb")
[clap_builder::parser::parser]Parser::get_matches_with: sc=None
[clap_builder::parser::parser]Parser::parse_short_arg: short_arg=ShortFlags { inner: "Bb", utf8_prefix: CharIndices { front_offset: 0, iter: Chars(['B', 'b']) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_short_arg:iter:b
[clap_builder::parser::parser]Parser::parse_short_arg:iter:b: Found valid opt or flag
[clap_builder::parser::parser]Parser::parse_short_arg:iter:b: val="", short_arg=ShortFlags { inner: "Bb", utf8_prefix: CharIndices { front_offset: 2, iter: Chars([]) }, invalid_suffix: None }
[clap_builder::parser::parser]Parser::parse_opt_value; arg=baz, 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("baz")
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"1"'
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=baz, pending=1
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1..=18446744073709551615, actual=1
[clap_builder::parser::parser]Parser::get_matches_with: Begin parsing '"2"'
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=baz, pending=2
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1..=18446744073709551615, actual=2
[clap_builder::parser::parser]Parser::resolve_pending: id="baz"
[clap_builder::parser::parser]Parser::react action=Append, identifier=Some(Short), source=CommandLine
[clap_builder::parser::parser]Parser::react: cur_idx:=3
[clap_builder::parser::parser]Parser::remove_overrides: id="baz"
[clap_builder::parser::arg_matcher]ArgMatcher::start_custom_arg: id="baz", source=CommandLine
[clap_builder::builder::command]Command::groups_for_arg: id="baz"
[clap_builder::parser::parser]Parser::push_arg_values: ["1", "2"]
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=4
[clap_builder::parser::parser]Parser::add_single_val_to_arg: cur_idx:=5
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: o=baz, pending=0
[clap_builder::parser::arg_matcher]ArgMatcher::needs_more_vals: expected=1..=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:baz:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:baz: 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="baz"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="baz", conflicts=[]
[clap_builder::parser::validator]Validator::validate: needs_val_of="baz"
[clap_builder::parser::validator]Validator::validate_conflicts
[clap_builder::parser::validator]Validator::validate_exclusive
[clap_builder::parser::validator]Validator::validate_conflicts::iter: id="baz"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="baz"
[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:"baz"
[clap_builder::parser::validator]Validator::validate_required: is_exclusive_present=false
[clap_builder::parser::parser]Parser::add_defaults
[clap_builder::parser::parser]Parser::add_defaults:iter:foo:
[clap_builder::parser::parser]Parser::add_default_value: doesn't have conditional defaults
[clap_builder::parser::parser]Parser::add_default_value:iter:foo: has default vals
[clap_builder::parser::parser]Parser::add_default_value:iter:foo: was used
[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="foo"
[clap_builder::parser::validator]Conflicts::gather_direct_conflicts id="foo", conflicts=[]
[clap_builder::parser::validator]Validator::validate_conflicts
[clap_builder::parser::validator]Validator::validate_exclusive
[clap_builder::parser::validator]Validator::validate_conflicts::iter: id="foo"
[clap_builder::parser::validator]Conflicts::gather_conflicts: arg="foo"
[clap_builder::parser::validator]Conflicts::gather_conflicts: conflicts=[]
[clap_builder::parser::arg_matcher]ArgMatcher::get_global_values: global_arg_vec=[]
@nix-enthusiast nix-enthusiast added the C-bug Category: Updating dependencies label Jan 10, 2024
@epage
Copy link
Member

epage commented Jan 10, 2024

Started as #5294

@epage epage changed the title Argument doesn't conflict with subcommand even if it's set Subcommand flags can't conflict with arguments Jan 10, 2024
@epage epage added A-parsing Area: Parser's logic and needs it changed somehow. E-medium Call for participation: Experience needed to fix: Medium / intermediate labels Jan 10, 2024
@epage
Copy link
Member

epage commented Jan 10, 2024

As noted in #5294, there is likely a similar bug with subcommand_precedence_over_arg because we only check for conflicts if the argument doesn't exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-parsing Area: Parser's logic and needs it changed somehow. C-bug Category: Updating dependencies E-medium Call for participation: Experience needed to fix: Medium / intermediate
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants