Repository: clap-rs/clap (rust)

Issue:
`ArgMathces::args_present` behaviour with `ArgAction::SetTrue`
### 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.85.0-nightly

### Clap Version

4.5.23

### Minimal reproducible code

```rust
fn main() {
    let cli = Command::new("myapp").arg(
        Arg::new("show")
            .short('s')
            .long("show")
            .action(ArgAction::SetTrue),
    );
    let matches = cli.get_matches_from(["myapp"]);
    assert!(!matches.args_present());
}
```

This code panic with message
```
assertion failed: !matches.args_present()
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

No args were provided, but `args_present()` returns true, which I think counterintuitive

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

cargo run

### Actual Behaviour

`matches.args_present()` returns true

### Expected Behaviour

`matches.args_present()` returns false if no arguments were provided despite `Arg::new("show").action(ArgAction::SetTrue)`

### Additional Context

_No response_

### Debug Output

_No response_

Pull request:
fix(parser): Dont report missing flags as present 
When we switched flags to `ArgAction::SetTrue`,
we overlooked updating `args_present`.
Because of the default value for `ArgAction::SetTrue`,
`args_present` will always report true when a flag is defined.

I went with the trivial implementation for now.  We could proactively
track this but was unsure about correctness vs overhead and so I thought
I'd have those using it pay for it.

Looking over uses on github, this will fix a couple bugs but should
otherwise be unnoticeable.

Fixes #5860

Patch:
diff --git a/clap_builder/src/parser/matches/arg_matches.rs b/clap_builder/src/parser/matches/arg_matches.rs
index 5e4d9dcd..78e6c758 100644
--- a/clap_builder/src/parser/matches/arg_matches.rs
+++ b/clap_builder/src/parser/matches/arg_matches.rs
@@ -554,7 +554,10 @@ impl ArgMatches {
         }
     }
 
-    /// Check if any args were present on the command line
+    /// Check if any [`Arg`][crate::Arg]s were present on the command line
+    ///
+    /// See [`ArgMatches::subcommand_name()`] or [`ArgMatches::subcommand()`] to check if a
+    /// subcommand was present on the command line.
     ///
     /// # Examples
     ///
@@ -575,7 +578,9 @@ impl ArgMatches {
     ///     .unwrap();
     /// assert!(! m.args_present());
     pub fn args_present(&self) -> bool {
-        !self.args.is_empty()
+        self.args
+            .values()
+            .any(|v| v.source().map(|s| s.is_explicit()).unwrap_or(false))
     }
 
     /// Report where argument value came from
diff --git a/clap_builder/src/util/flat_map.rs b/clap_builder/src/util/flat_map.rs
index d739b922..3dce096e 100644
--- a/clap_builder/src/util/flat_map.rs
+++ b/clap_builder/src/util/flat_map.rs
@@ -118,6 +118,10 @@ impl<K: PartialEq + Eq, V> FlatMap<K, V> {
         self.keys.iter()
     }
 
+    pub(crate) fn values(&self) -> std::slice::Iter<'_, V> {
+        self.values.iter()
+    }
+
     pub(crate) fn iter(&self) -> Iter<'_, K, V> {
         Iter {
             keys: self.keys.iter(),
diff --git a/src/lib.rs b/src/lib.rs
index b581a2c3..7dc4e463 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -67,7 +67,7 @@
 //! - [clio](https://crates.io/crates/clio) for reading/writing to files specified as arguments
 //! - [clap-verbosity-flag](https://crates.io/crates/clap-verbosity-flag)
 //! - [clap-cargo](https://crates.io/crates/clap-cargo)
-//! - [colorchoice-clap](https://crates.io/crates/colorchoice-clap)
+//! - [concolor-clap](https://crates.io/crates/concolor-clap)
 //!
 //! Testing
 //! - [`trycmd`](https://crates.io/crates/trycmd):  Bulk snapshot testing


Test patch:
diff --git a/tests/builder/arg_matches.rs b/tests/builder/arg_matches.rs
index c6d16df0..cfbb0731 100644
--- a/tests/builder/arg_matches.rs
+++ b/tests/builder/arg_matches.rs
@@ -1,5 +1,4 @@
 use clap::{arg, value_parser, Command};
-#[cfg(debug_assertions)]
 use clap::{Arg, ArgAction};
 
 #[test]
@@ -83,3 +82,36 @@ fn arg_matches_subcommand_matches_wrong_sub() {
     assert!(m.subcommand_matches("speed").is_some());
     m.subcommand_matches("seed");
 }
+
+#[test]
+fn args_present_positional() {
+    let c = Command::new("test").arg(Arg::new("positional"));
+
+    let m = c.clone().try_get_matches_from(["test"]).unwrap();
+    assert!(!m.args_present());
+
+    let m = c.clone().try_get_matches_from(["test", "value"]).unwrap();
+    assert!(m.args_present());
+}
+
+#[test]
+fn args_present_flag() {
+    let c = Command::new("test").arg(Arg::new("flag").long("flag").action(ArgAction::SetTrue));
+
+    let m = c.clone().try_get_matches_from(["test"]).unwrap();
+    assert!(!m.args_present());
+
+    let m = c.clone().try_get_matches_from(["test", "--flag"]).unwrap();
+    assert!(m.args_present());
+}
+
+#[test]
+fn args_present_subcommand() {
+    let c = Command::new("test").subcommand(Command::new("sub"));
+
+    let m = c.clone().try_get_matches_from(["test"]).unwrap();
+    assert!(!m.args_present());
+
+    let m = c.clone().try_get_matches_from(["test", "sub"]).unwrap();
+    assert!(!m.args_present());
+}