Repository: clap-rs/clap
Pull request #3902 (2022-07-09 06:38:35)

Include groups in `get_arg_conflicts_with`
So that it doesn't panic if trying to get the conflicts for an Arg that
conflicts with a group.

Fixes: #3900

<!--
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).
-->


epage: @tmccombs could you run cargo fmt so CI passes?

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59c113ed..2370ea82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,18 +30,6 @@ _gated behind `unstable-v4`_
 <!-- next-header -->
 ## [Unreleased] - ReleaseDate
 
-## [3.2.11] - 2022-07-13
-
-### Features
-
-- Added `Arg::get_all_short_aliaes` and `Arg::get_all_aliases`
-
-## [3.2.10] - 2022-07-12
-
-### Fixes
-
-- Loosen lifetime on `Command::mut_subcommand`
-
 ## [3.2.8] - 2022-06-30
 
 ### Features
@@ -3513,9 +3501,7 @@ Minimum version of Rust is now v1.13.0 (Stable)
 * **arg**  allow lifetimes other than 'static in arguments ([9e8c1fb9](https://github.com/clap-rs/clap/commit/9e8c1fb9406f8448873ca58bab07fe905f1551e5))
 
 <!-- next-url -->
-[Unreleased]: https://github.com/clap-rs/clap/compare/v3.2.11...HEAD
-[3.2.11]: https://github.com/clap-rs/clap/compare/v3.2.10...v3.2.11
-[3.2.10]: https://github.com/clap-rs/clap/compare/v3.2.8...v3.2.10
+[Unreleased]: https://github.com/clap-rs/clap/compare/v3.2.8...HEAD
 [3.2.8]: https://github.com/clap-rs/clap/compare/v3.2.7...v3.2.8
 [3.2.7]: https://github.com/clap-rs/clap/compare/v3.2.6...v3.2.7
 [3.2.6]: https://github.com/clap-rs/clap/compare/v3.2.5...v3.2.6
diff --git a/Cargo.toml b/Cargo.toml
index 882deeaa..26d753f8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,7 +10,7 @@ members = [
 
 [package]
 name = "clap"
-version = "3.2.11"
+version = "3.2.8"
 description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
 repository = "https://github.com/clap-rs/clap"
 categories = ["command-line-interface"]
@@ -52,6 +52,7 @@ pre-release-replacements = [
   {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## [Unreleased] - ReleaseDate\n", exactly=1},
   {file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD", exactly=1},
   {file="README.md", search="github.com/clap-rs/clap/blob/[^/]+/", replace="github.com/clap-rs/clap/blob/{{tag_name}}/", exactly=13, prerelease = true},
+  {file="README.md", search="version = \"[a-z0-9\\.-]+\"", replace="version = \"{{version}}\"", exactly=1, prerelease = true},
   {file="src/derive.rs", search="github.com/clap-rs/clap/blob/[^/]+/", replace="github.com/clap-rs/clap/blob/{{tag_name}}/", exactly=4, prerelease = true},
 ]
 
diff --git a/README.md b/README.md
index 2d6a11e0..0ea13503 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,8 @@
 
 [![Crates.io](https://img.shields.io/crates/v/clap?style=flat-square)](https://crates.io/crates/clap)
 [![Crates.io](https://img.shields.io/crates/d/clap?style=flat-square)](https://crates.io/crates/clap)
-[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.2.11/LICENSE-APACHE)
-[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.2.11/LICENSE-MIT)
+[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.2.8/LICENSE-APACHE)
+[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.2.8/LICENSE-MIT)
 [![Build Status](https://img.shields.io/github/workflow/status/clap-rs/clap/CI/staging?style=flat-square)](https://github.com/clap-rs/clap/actions/workflows/ci.yml?query=branch%3Astaging)
 [![Coverage Status](https://img.shields.io/coveralls/github/clap-rs/clap/master?style=flat-square)](https://coveralls.io/github/clap-rs/clap?branch=master)
 [![Contributors](https://img.shields.io/github/contributors/clap-rs/clap?style=flat-square)](https://github.com/clap-rs/clap/graphs/contributors)
@@ -14,15 +14,15 @@
 Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).
 
 1. [About](#about)
-2. Tutorial: [Builder API](https://github.com/clap-rs/clap/blob/v3.2.11/examples/tutorial_builder/README.md),  [Derive API](https://github.com/clap-rs/clap/blob/v3.2.11/examples/tutorial_derive/README.md)
-3. [Examples](https://github.com/clap-rs/clap/blob/v3.2.11/examples/README.md)
+2. Tutorial: [Builder API](https://github.com/clap-rs/clap/blob/v3.2.8/examples/tutorial_builder/README.md),  [Derive API](https://github.com/clap-rs/clap/blob/v3.2.8/examples/tutorial_derive/README.md)
+3. [Examples](https://github.com/clap-rs/clap/blob/v3.2.8/examples/README.md)
 4. [API Reference](https://docs.rs/clap)
-    - [Derive Reference](https://github.com/clap-rs/clap/blob/v3.2.11/examples/derive_ref/README.md)
+    - [Derive Reference](https://github.com/clap-rs/clap/blob/v3.2.8/examples/derive_ref/README.md)
     - [Feature Flags](#feature-flags)
-5. [CHANGELOG](https://github.com/clap-rs/clap/blob/v3.2.11/CHANGELOG.md)
-6. [FAQ](https://github.com/clap-rs/clap/blob/v3.2.11/docs/FAQ.md)
+5. [CHANGELOG](https://github.com/clap-rs/clap/blob/v3.2.8/CHANGELOG.md)
+6. [FAQ](https://github.com/clap-rs/clap/blob/v3.2.8/docs/FAQ.md)
 7. [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
-8. [Contributing](https://github.com/clap-rs/clap/blob/v3.2.11/CONTRIBUTING.md)
+8. [Contributing](https://github.com/clap-rs/clap/blob/v3.2.8/CONTRIBUTING.md)
 8. [Sponsors](#sponsors)
 
 ## About
@@ -31,14 +31,9 @@ Create your command-line parser, with all of the bells and whistles, declarative
 
 ### Example
 
-Add `clap` as a dependency with the `derive` feature enabled:
-```console
-cargo add clap -F derive
-```
-
-This allows using the
-[Derive API](https://github.com/clap-rs/clap/blob/v3.2.11/examples/tutorial_derive/README.md)
-which provides access to the [Builder API](https://github.com/clap-rs/clap/blob/v3.2.11/examples/tutorial_builder/README.md) as attributes on a `struct`:
+This uses our
+[Derive API](https://github.com/clap-rs/clap/blob/v3.2.8/examples/tutorial_derive/README.md)
+which provides access to the [Builder API](https://github.com/clap-rs/clap/blob/v3.2.8/examples/tutorial_builder/README.md) as attributes on a `struct`:
 
 <!-- Copied from examples/demo.{rs,md} -->
 ```rust,no_run
@@ -65,7 +60,11 @@ fn main() {
     }
 }
 ```
-
+Add this to `Cargo.toml`:
+```toml
+[dependencies]
+clap = { version = "3.2.8", features = ["derive"] }
+```
 ```bash
 $ demo --help
 clap [..]
@@ -104,12 +103,12 @@ CLI parsers optimized for other use cases.
 
 ### Selecting an API
 
-Why use the declarative [Derive API](https://github.com/clap-rs/clap/blob/v3.2.11/examples/tutorial_derive/README.md):
+Why use the declarative [Derive API](https://github.com/clap-rs/clap/blob/v3.2.8/examples/tutorial_derive/README.md):
 - Easier to read, write, and modify
 - Easier to keep the argument declaration and reading of argument in sync
 - Easier to reuse, e.g. [clap-verbosity-flag](https://crates.io/crates/clap-verbosity-flag)
 
-Why use the procedural [Builder API](https://github.com/clap-rs/clap/blob/v3.2.11/examples/tutorial_builder/README.md):
+Why use the procedural [Builder API](https://github.com/clap-rs/clap/blob/v3.2.8/examples/tutorial_builder/README.md):
 - Faster compile times if you aren't already using other procedural macros
 - More flexible, e.g. you can look up how many times an argument showed up,
   what its values were, and what were the indexes of those values.  The Derive
diff --git a/clap_complete/src/lib.rs b/clap_complete/src/lib.rs
index de00eafb..3c2f1def 100644
--- a/clap_complete/src/lib.rs
+++ b/clap_complete/src/lib.rs
@@ -22,7 +22,7 @@
 //! ## Example
 //!
 //! ```rust,no_run
-//! use clap::{Command, Arg, ValueHint, value_parser};
+//! use clap::{Command, AppSettings, Arg, ValueHint, value_parser};
 //! use clap_complete::{generate, Generator, Shell};
 //! use std::io;
 //!
@@ -35,7 +35,6 @@
 //!        .arg(
 //!            Arg::new("generator")
 //!                .long("generate")
-//!                .takes_value(true)
 //!                .value_parser(value_parser!(Shell)),
 //!        )
 //! }
@@ -47,7 +46,7 @@
 //! fn main() {
 //!     let matches = build_cli().get_matches();
 //!
-//!     if let Some(generator) = matches.get_one::<Shell>("generator").copied() {
+//!     if let Ok(generator) = matches.value_of_t::<Shell>("generator") {
 //!         let mut cmd = build_cli();
 //!         eprintln!("Generating completion file for {}...", generator);
 //!         print_completions(generator, &mut cmd);
diff --git a/examples/derive_ref/README.md b/examples/derive_ref/README.md
index db5113d8..660b15fb 100644
--- a/examples/derive_ref/README.md
+++ b/examples/derive_ref/README.md
@@ -152,12 +152,12 @@ when defining subcommands.
 - `next_display_order`: `clap::Command::next_display_order`
 - `next_help_heading`: `clap::Command::next_help_heading`
   - When `flatten`ing `Args`, this is scoped to just the args in this struct and any struct `flatten`ed into it
-- `rename_all = <string_literal>`: Override default field / variant name case conversion for `Command::name` / `Arg::name`
-  - When not present: `"kebab-case"`
-  - Available values: `"camelCase"`, `"kebab-case"`, `"PascalCase"`, `"SCREAMING_SNAKE_CASE"`, `"snake_case"`, `"lower"`, `"UPPER"`, `"verbatim"`
-- `rename_all_env = <string_literal>`: Override default field name case conversion for env variables for  `clap::Arg::env`
-  - When not present: `"SCREAMING_SNAKE_CASE"`
-  - Available values: `"camelCase"`, `"kebab-case"`, `"PascalCase"`, `"SCREAMING_SNAKE_CASE"`, `"snake_case"`, `"lower"`, `"UPPER"`, `"verbatim"`
+- `rename_all = <expr>`: Override default field / variant name case conversion for `Command::name` / `Arg::name`
+  - When not present: `kebab-case`
+  - Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim`
+- `rename_all_env = <expr>`: Override default field name case conversion for env variables for  `clap::Arg::env`
+  - When not present: `SCREAMING_SNAKE_CASE`
+  - Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim`
 
 And for `Subcommand` variants:
 - `skip`: Ignore this variant
@@ -228,9 +228,9 @@ These correspond to a `clap::Arg`.
 
 ### Arg Enum Attributes
 
-- `rename_all = <string_literal>`: Override default field / variant name case conversion for `PossibleValue::new`
-  - When not present: `"kebab-case"`
-  - Available values: `"camelCase"`, `"kebab-case"`, `"PascalCase"`, `"SCREAMING_SNAKE_CASE"`, `"snake_case"`, `"lower"`, `"UPPER"`, `"verbatim"`
+- `rename_all = <expr>`: Override default field / variant name case conversion for `PossibleValue::new`
+  - When not present: `kebab-case`
+  - Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim`
 
 ### Possible Value Attributes
 
diff --git a/examples/tutorial_builder/README.md b/examples/tutorial_builder/README.md
index 98f86caf..945b01e7 100644
--- a/examples/tutorial_builder/README.md
+++ b/examples/tutorial_builder/README.md
@@ -63,7 +63,7 @@ Not printing testing lists...
 
 ## Configuring the Parser
 
-You use the `Command` to start building a parser.
+You use the `Command` the start building a parser.
 
 [Example:](02_apps.rs)
 ```console
diff --git a/examples/tutorial_derive/README.md b/examples/tutorial_derive/README.md
index 8f47b2fc..6127b6cd 100644
--- a/examples/tutorial_derive/README.md
+++ b/examples/tutorial_derive/README.md
@@ -66,7 +66,7 @@ In addition to this tutorial, see the [derive reference](../derive_ref/README.md
 
 ## Configuring the Parser
 
-You use derive `Parser` to start building a parser.
+You use derive `Parser` the start building a parser.
 
 [Example:](02_apps.rs)
 ```console
diff --git a/src/builder/arg.rs b/src/builder/arg.rs
index e9403d0b..63e88e80 100644
--- a/src/builder/arg.rs
+++ b/src/builder/arg.rs
@@ -4316,16 +4316,6 @@ impl<'help> Arg<'help> {
         }
     }
 
-    /// Get *all* short aliases for this argument, if any, both visible and hidden.
-    #[inline]
-    pub fn get_all_short_aliases(&self) -> Option<Vec<char>> {
-        if self.short_aliases.is_empty() {
-            None
-        } else {
-            Some(self.short_aliases.iter().map(|(s, _)| s).copied().collect())
-        }
-    }
-
     /// Get the short option name and its visible aliases, if any
     #[inline]
     pub fn get_short_and_visible_aliases(&self) -> Option<Vec<char>> {
@@ -4361,16 +4351,6 @@ impl<'help> Arg<'help> {
         }
     }
 
-    /// Get *all* aliases for this argument, if any, both visible and hidden.
-    #[inline]
-    pub fn get_all_aliases(&self) -> Option<Vec<&'help str>> {
-        if self.aliases.is_empty() {
-            None
-        } else {
-            Some(self.aliases.iter().map(|(s, _)| s).copied().collect())
-        }
-    }
-
     /// Get the long option name and its visible aliases, if any
     #[inline]
     pub fn get_long_and_visible_aliases(&self) -> Option<Vec<&'help str>> {
diff --git a/src/builder/command.rs b/src/builder/command.rs
index 9ef4947c..7373c6eb 100644
--- a/src/builder/command.rs
+++ b/src/builder/command.rs
@@ -298,10 +298,10 @@ impl<'help> App<'help> {
     /// assert!(res.is_ok());
     /// ```
     #[must_use]
-    pub fn mut_subcommand<'a, T, F>(mut self, subcmd_id: T, f: F) -> Self
+    pub fn mut_subcommand<T, F>(mut self, subcmd_id: T, f: F) -> Self
     where
         F: FnOnce(App<'help>) -> App<'help>,
-        T: Into<&'a str>,
+        T: Into<&'help str>,
     {
         let subcmd_id: &str = subcmd_id.into();
         let id = Id::from(subcmd_id);
@@ -3556,15 +3556,21 @@ impl<'help> App<'help> {
         if arg.is_global_set() {
             self.get_global_arg_conflicts_with(arg)
         } else {
-            arg.blacklist
-                .iter()
-                .map(|id| {
-                    self.args.args().find(|arg| arg.id == *id).expect(
-                        "Command::get_arg_conflicts_with: \
-                    The passed arg conflicts with an arg unknown to the cmd",
-                    )
-                })
-                .collect()
+            let mut result = Vec::new();
+            for id in arg.blacklist.iter() {
+                if let Some(arg) = self.find(id) {
+                    result.push(arg);
+                } else if let Some(group) = self.find_group(id) {
+                    result.extend(
+                        self.unroll_args_in_group(&group.id)
+                            .iter()
+                            .map(|id| self.find(id).expect(INTERNAL_ERROR_MSG)),
+                    );
+                } else {
+                    panic!("Command::get_arg_conflicts_with: The passed arg conflicts with an arg unknown to the cmd");
+                }
+            }
+            result
         }
     }
 
diff --git a/src/derive.rs b/src/derive.rs
index 29e1b86f..7c32a272 100644
--- a/src/derive.rs
+++ b/src/derive.rs
@@ -20,7 +20,7 @@ use std::ffi::OsString;
 /// See also [`Subcommand`] and [`Args`].
 ///
 /// See the
-/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.11/examples/derive_ref/README.md)
+/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.8/examples/derive_ref/README.md)
 /// for attributes and best practices.
 ///
 /// **NOTE:** Deriving requires the `derive` feature flag
@@ -373,7 +373,7 @@ pub trait FromArgMatches: Sized {
 /// - `Variant(ChildArgs)`: No attribute is used with enum variants that impl `Args`.
 ///
 /// See the
-/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.11/examples/derive_ref/README.md)
+/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.8/examples/derive_ref/README.md)
 /// for attributes and best practices.
 ///
 /// **NOTE:** Deriving requires the `derive` feature flag
@@ -417,7 +417,7 @@ pub trait Args: FromArgMatches + Sized {
 ///   `Subcommand`.
 ///
 /// See the
-/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.11/examples/derive_ref/README.md)
+/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.8/examples/derive_ref/README.md)
 /// for attributes and best practices.
 ///
 /// **NOTE:** Deriving requires the `derive` feature flag
@@ -461,7 +461,7 @@ pub trait Subcommand: FromArgMatches + Sized {
 /// - Allowing using the `#[clap(default_value_t)]` attribute without implementing `Display`.
 ///
 /// See the
-/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.11/examples/derive_ref/README.md)
+/// [derive reference](https://github.com/clap-rs/clap/blob/v3.2.8/examples/derive_ref/README.md)
 /// for attributes and best practices.
 ///
 /// **NOTE:** Deriving requires the `derive` feature flag


Test patch:
diff --git a/tests/builder/arg_aliases.rs b/tests/builder/arg_aliases.rs
index 3cc8dbf5..7b216dbc 100644
--- a/tests/builder/arg_aliases.rs
+++ b/tests/builder/arg_aliases.rs
@@ -111,37 +111,6 @@ fn multiple_aliases_of_option() {
     );
 }
 
-#[test]
-fn get_aliases() {
-    let a = Arg::new("aliases")
-        .long("aliases")
-        .takes_value(true)
-        .help("multiple aliases")
-        .aliases(&["alias1", "alias2", "alias3"])
-        .short_aliases(&['a', 'b', 'c'])
-        .visible_aliases(&["alias4", "alias5", "alias6"])
-        .visible_short_aliases(&['d', 'e', 'f']);
-
-    assert!(a.get_short_and_visible_aliases().is_none());
-    assert_eq!(
-        a.get_long_and_visible_aliases().unwrap(),
-        &["aliases", "alias4", "alias5", "alias6"]
-    );
-    assert_eq!(
-        a.get_visible_aliases().unwrap(),
-        &["alias4", "alias5", "alias6"]
-    );
-    assert_eq!(
-        a.get_all_aliases().unwrap(),
-        &["alias1", "alias2", "alias3", "alias4", "alias5", "alias6"]
-    );
-    assert_eq!(a.get_visible_short_aliases().unwrap(), vec!['d', 'e', 'f']);
-    assert_eq!(
-        a.get_all_short_aliases().unwrap(),
-        vec!['a', 'b', 'c', 'd', 'e', 'f']
-    );
-}
-
 #[test]
 fn single_alias_of_flag() {
     let a = Command::new("test")
diff --git a/tests/builder/conflicts.rs b/tests/builder/conflicts.rs
index 3c0ef817..cb5e7da7 100644
--- a/tests/builder/conflicts.rs
+++ b/tests/builder/conflicts.rs
@@ -296,6 +296,24 @@ fn required_group_conflicts_with_arg() {
     }
 }
 
+#[test]
+fn get_arg_conflicts_with_group() {
+    let flag = arg!(--flag).conflicts_with("gr");
+    let mut cmd = Command::new("group_conflict")
+        .arg(&flag)
+        .group(ArgGroup::new("gr").arg("some").arg("other"))
+        .arg(arg!(--some))
+        .arg(arg!(--other));
+
+    cmd.build();
+
+    let result = cmd.get_arg_conflicts_with(&flag);
+
+    assert_eq!(result.len(), 2);
+    assert_eq!(result[0].get_id(), "some");
+    assert_eq!(result[1].get_id(), "other");
+}
+
 #[test]
 fn conflict_output() {
     utils::assert_output(
diff --git a/tests/builder/tests.rs b/tests/builder/tests.rs
index 8a1544c1..196884e0 100644
--- a/tests/builder/tests.rs
+++ b/tests/builder/tests.rs
@@ -445,25 +445,6 @@ fn mut_subcommand_all() {
     );
 }
 
-#[test]
-fn mut_subcommand_with_alias_resolve() {
-    let mut cmd =
-        Command::new("foo").subcommand(Command::new("bar").alias("baz").about("test subcmd"));
-    assert_eq!(
-        cmd.find_subcommand("baz").unwrap().get_about().unwrap(),
-        "test subcmd"
-    );
-
-    let true_name = cmd.find_subcommand("baz").unwrap().get_name().to_string();
-    assert_eq!(true_name, "bar");
-
-    cmd = cmd.mut_subcommand(&*true_name, |subcmd| subcmd.about("modified about"));
-    assert_eq!(
-        cmd.find_subcommand("baz").unwrap().get_about().unwrap(),
-        "modified about"
-    );
-}
-
 #[test]
 fn issue_3669_command_build_recurses() {
     let mut cmd = Command::new("ctest").subcommand(