Repository: clap-rs/clap (rust)

Issue:
`exclusive(true)` worked in `v4.0.6` and broke in `v4.0.7`
### Please complete the following tasks

- [X] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [X] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues

### Rust Version

rustc 1.62.1 (gentoo)

### Clap Version

4.0.15

### Minimal reproducible code

```rust
use clap::Parser;

#[derive(Debug, Parser)]
struct Opts {
    /// Foo!
    #[clap(exclusive(true), long)]
    foo: bool,
}

fn main() {
    Opts::parse();
}
```

### Steps to reproduce the bug with the above code

```bash
cargo run -- --foo
```

### Actual Behaviour

> error: The argument '--foo' cannot be used with one or more of the other specified arguments

### Expected Behaviour

No errors

### Additional Context

_No response_

### Debug Output

```
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/clap-mre --foo`
[      clap::builder::command] 	Command::_do_parse
[      clap::builder::command] 	Command::_build: name="clap-mre"
[      clap::builder::command] 	Command::_propagate:clap-mre
[      clap::builder::command] 	Command::_check_help_and_version:clap-mre expand_help_tree=false
[      clap::builder::command] 	Command::long_help_exists
[      clap::builder::command] 	Command::_check_help_and_version: Building default --help
[      clap::builder::command] 	Command::_propagate_global_args:clap-mre
[clap::builder::debug_asserts] 	Command::_debug_asserts
[clap::builder::debug_asserts] 	Arg::_debug_asserts:foo
[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("--foo")' ([45, 45, 102, 111, 111])
[        clap::parser::parser] 	Parser::possible_subcommand: arg=Ok("--foo")
[        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 '--foo'
[        clap::parser::parser] 	Parser::parse_long_arg("foo"): Presence validated
[        clap::parser::parser] 	Parser::react action=SetTrue, identifier=Some(Long), source=CommandLine
[        clap::parser::parser] 	Parser::react: has default_missing_vals
[        clap::parser::parser] 	Parser::remove_overrides: id="foo"
[   clap::parser::arg_matcher] 	ArgMatcher::start_custom_arg: id="foo", source=CommandLine
[      clap::builder::command] 	Command::groups_for_arg: id="foo"
[   clap::parser::arg_matcher] 	ArgMatcher::start_custom_arg: id="Opts", source=CommandLine
[        clap::parser::parser] 	Parser::push_arg_values: ["true"]
[        clap::parser::parser] 	Parser::add_single_val_to_arg: cur_idx:=1
[      clap::builder::command] 	Command::groups_for_arg: id="foo"
[        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:foo:
[        clap::parser::parser] 	Parser::add_default_value: doesn't have conditional defaults
[        clap::parser::parser] 	Parser::add_default_value:iter:foo: has default vals
[        clap::parser::parser] 	Parser::add_default_value:iter:foo: was used
[        clap::parser::parser] 	Parser::add_defaults:iter:help:
[        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:"foo"
[         clap::output::usage] 	Usage::create_usage_with_title
[         clap::output::usage] 	Usage::create_usage_no_title
[         clap::output::usage] 	Usage::create_help_usage; incl_reqs=true
[         clap::output::usage] 	Usage::needs_options_tag
[         clap::output::usage] 	Usage::needs_options_tag:iter: f=foo
[      clap::builder::command] 	Command::groups_for_arg: id="foo"
[         clap::output::usage] 	Usage::needs_options_tag:iter:iter: grp_s="Opts"
[         clap::output::usage] 	Usage::needs_options_tag:iter: [OPTIONS] required
[         clap::output::usage] 	Usage::get_args: incls=[]
[         clap::output::usage] 	Usage::get_args: unrolled_reqs=[]
[         clap::output::usage] 	Usage::get_args: ret_val=[]
[         clap::output::usage] 	Usage::create_help_usage: usage=clap-mre [OPTIONS]
[      clap::builder::command] 	Command::color: Color setting...
[      clap::builder::command] 	Auto
[      clap::builder::command] 	Command::color: Color setting...
[      clap::builder::command] 	Auto
error: The argument '--foo' cannot be used with one or more of the other specified arguments

Usage: clap-mre [OPTIONS]

For more information try '--help'
```

Pull request:
fix(parser): Don't make Args `exclusive` with their ArgGroup
This is most obvious with the derive API as it creates `ArgGroup`s all over the place now.

Fixes #4396

<!--
Thanks for helping out!

Please link the appropriate issue from your PR.

If you don't have an issue, we'd recommend starting with one first so the PR can focus on the
implementation (unless its an obvious bug or documentation fix that will have
little conversation).
-->


Patch:
diff --git a/src/parser/validator.rs b/src/parser/validator.rs
index 8b6e46c6..970804bc 100644
--- a/src/parser/validator.rs
+++ b/src/parser/validator.rs
@@ -112,6 +112,9 @@ impl<'cmd> Validator<'cmd> {
             .arg_ids()
             .filter(|arg_id| {
                 matcher.check_explicit(arg_id, &crate::builder::ArgPredicate::IsPresent)
+                    // Avoid including our own groups by checking none of them.  If a group is present, the
+                    // args for the group will be.
+                    && self.cmd.find(arg_id).is_some()
             })
             .count();
         if args_count <= 1 {


Test patch:
diff --git a/tests/builder/conflicts.rs b/tests/builder/conflicts.rs
index dbee06b1..32a3342a 100644
--- a/tests/builder/conflicts.rs
+++ b/tests/builder/conflicts.rs
@@ -38,10 +38,13 @@ fn flag_conflict_with_all() {
 
 #[test]
 fn exclusive_flag() {
-    let result = Command::new("flag_conflict")
+    let cmd = Command::new("flag_conflict")
         .arg(arg!(-f --flag "some flag").exclusive(true))
-        .arg(arg!(-o --other "some flag"))
-        .try_get_matches_from(vec!["myprog", "-o", "-f"]);
+        .arg(arg!(-o --other "some flag"));
+    let result = cmd.clone().try_get_matches_from(vec!["myprog", "-f"]);
+    assert!(result.is_ok(), "{}", result.unwrap_err());
+
+    let result = cmd.clone().try_get_matches_from(vec!["myprog", "-o", "-f"]);
     assert!(result.is_err());
     let err = result.err().unwrap();
     assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
@@ -71,6 +74,20 @@ fn not_exclusive_with_defaults() {
     assert!(result.is_ok(), "{}", result.unwrap_err());
 }
 
+#[test]
+fn not_exclusive_with_group() {
+    let cmd = Command::new("test")
+        .group(clap::ArgGroup::new("test").arg("foo"))
+        .arg(
+            clap::Arg::new("foo")
+                .long("foo")
+                .exclusive(true)
+                .action(clap::ArgAction::SetTrue),
+        );
+    let result = cmd.try_get_matches_from(vec!["test", "--foo"]);
+    assert!(result.is_ok(), "{}", result.unwrap_err());
+}
+
 #[test]
 fn default_doesnt_activate_exclusive() {
     let result = Command::new("flag_conflict")