Repository: clap-rs/clap (rust)

Issue:
Remove `{bin}` help template variable
When we added the `{name}` variable, we forgot to remove the broken `{bin}` variable in 4.0 and people are still accidentally using it for new use cases (#4561).

When removing it, we should assert on `{bin}` not being in the help template to help people transition across the breaking change.

Pull request description:
removed {bin} variable from help_template
Closes #4565 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac532626..b9d2fb4c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,12 +13,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 <!-- next-header -->
 ## [Unreleased] - ReleaseDate
 
-## [4.0.30] - 2022-12-21
-
-### Fixes
-
-- *(error)* Improve error for `args_conflicts_with_subcommand`
-
 ## [4.0.29] - 2022-11-29
 
 ## [4.0.28] - 2022-11-29
@@ -4047,8 +4041,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/v4.0.30...HEAD
-[4.0.30]: https://github.com/clap-rs/clap/compare/v4.0.29...v4.0.30
+[Unreleased]: https://github.com/clap-rs/clap/compare/v4.0.29...HEAD
 [4.0.29]: https://github.com/clap-rs/clap/compare/v4.0.28...v4.0.29
 [4.0.28]: https://github.com/clap-rs/clap/compare/v4.0.27...v4.0.28
 [4.0.27]: https://github.com/clap-rs/clap/compare/v4.0.26...v4.0.27
diff --git a/Cargo.lock b/Cargo.lock
index 2dbfdc8a..7575bccf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -132,7 +132,7 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.0.30"
+version = "4.0.29"
 dependencies = [
  "backtrace",
  "bitflags",
@@ -159,7 +159,7 @@ dependencies = [
 name = "clap_bench"
 version = "0.0.0"
 dependencies = [
- "clap 4.0.30",
+ "clap 4.0.29",
  "criterion",
  "lazy_static",
 ]
@@ -168,7 +168,7 @@ dependencies = [
 name = "clap_complete"
 version = "4.0.6"
 dependencies = [
- "clap 4.0.30",
+ "clap 4.0.29",
  "clap_lex 0.3.0",
  "is_executable",
  "os_str_bytes",
@@ -183,7 +183,7 @@ dependencies = [
 name = "clap_complete_fig"
 version = "4.0.2"
 dependencies = [
- "clap 4.0.30",
+ "clap 4.0.29",
  "clap_complete",
  "snapbox",
 ]
@@ -219,7 +219,7 @@ dependencies = [
 name = "clap_mangen"
 version = "0.2.5"
 dependencies = [
- "clap 4.0.30",
+ "clap 4.0.29",
  "roff",
  "snapbox",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 2363864e..f0c4c5a0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,7 +10,7 @@ members = [
 
 [package]
 name = "clap"
-version = "4.0.30"
+version = "4.0.29"
 description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
 repository = "https://github.com/clap-rs/clap"
 categories = ["command-line-interface"]
diff --git a/clap_bench/benches/04_new_help.rs b/clap_bench/benches/04_new_help.rs
index 3398b58e..dfb2157e 100644
--- a/clap_bench/benches/04_new_help.rs
+++ b/clap_bench/benches/04_new_help.rs
@@ -199,7 +199,7 @@ pub fn example10(c: &mut Criterion) {
 }
 
 pub fn example4_template(c: &mut Criterion) {
-    let mut cmd = app_example4().help_template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n    {usage}\n\nOPTIONS:\n{options}\n\nARGS:\n{args}\n");
+    let mut cmd = app_example4().help_template("{name} {version}\n{author}\n{about}\n\nUSAGE:\n    {usage}\n\nOPTIONS:\n{options}\n\nARGS:\n{args}\n");
     c.bench_function("example4_template", |b| b.iter(|| build_help(&mut cmd)));
 }
 
diff --git a/clap_bench/benches/05_ripgrep.rs b/clap_bench/benches/05_ripgrep.rs
index efca09c8..00662ea5 100644
--- a/clap_bench/benches/05_ripgrep.rs
+++ b/clap_bench/benches/05_ripgrep.rs
@@ -256,7 +256,7 @@ const USAGE: &str = "
     rg [OPTIONS] --type-list";
 
 const TEMPLATE: &str = "\
-{bin} {version}
+{name} {version}
 {author}
 {about}
 
diff --git a/src/builder/command.rs b/src/builder/command.rs
index 203e677a..1a424993 100644
--- a/src/builder/command.rs
+++ b/src/builder/command.rs
@@ -1744,7 +1744,7 @@ impl Command {
     /// Valid tags are:
     ///
     ///   * `{name}`                - Display name for the (sub-)command.
-    ///   * `{bin}`                 - Binary name.
+    ///   * `{bin}`                 - Binary name.(deprecated)
     ///   * `{version}`             - Version number.
     ///   * `{author}`              - Author information.
     ///   * `{author-with-newline}` - Author followed by `\n`.
@@ -1772,7 +1772,7 @@ impl Command {
     /// # use clap::Command;
     /// Command::new("myprog")
     ///     .version("1.0")
-    ///     .help_template("{bin} ({version}) - {usage}")
+    ///     .help_template("{name} ({version}) - {usage}")
     /// # ;
     /// ```
     ///
diff --git a/src/builder/debug_asserts.rs b/src/builder/debug_asserts.rs
index dff70e30..bfebbb4c 100644
--- a/src/builder/debug_asserts.rs
+++ b/src/builder/debug_asserts.rs
@@ -359,6 +359,13 @@ pub(crate) fn assert_app(cmd: &Command) {
             cmd.get_name(),
             "`{unified}` template variable was removed in clap3, use `{options}` instead"
         );
+        #[cfg(feature = "unstable-v5")]
+        assert!(
+            !help_template.to_string().contains("{bin}"),
+            "Command {}: {}",
+            cmd.get_name(),
+            "`{bin}` template variable was removed in clap5, use `{name}` instead"
+        )
     }
 
     cmd._panic_on_missing_help(cmd.is_help_expected_set());
diff --git a/src/output/help_template.rs b/src/output/help_template.rs
index 3db80da7..6fe9db1b 100644
--- a/src/output/help_template.rs
+++ b/src/output/help_template.rs
@@ -137,6 +137,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
                     "name" => {
                         self.write_display_name();
                     }
+                    #[cfg(not(feature = "unstable-v5"))]
                     "bin" => {
                         self.write_bin_name();
                     }
diff --git a/src/parser/matches/arg_matches.rs b/src/parser/matches/arg_matches.rs
index 000ee77d..36d2ca93 100644
--- a/src/parser/matches/arg_matches.rs
+++ b/src/parser/matches/arg_matches.rs
@@ -216,44 +216,6 @@ impl ArgMatches {
         MatchesError::unwrap(id, self.try_get_many(id))
     }
 
-    /// Iterate over the values passed to each occurrence of an option.
-    ///
-    /// Each item is itself an iterator containing the arguments passed to a single occurrence
-    /// of the option.
-    ///
-    /// If the option doesn't support multiple occurrences, or there was only a single occurrence,
-    /// the iterator will only contain a single item.
-    ///
-    /// Returns `None` if the option wasn't present.
-    ///
-    /// # Panics
-    ///
-    /// If the argument definition and access mismatch. To handle this case programmatically, see
-    /// [`ArgMatches::try_get_occurrences`].
-    ///
-    /// # Examples
-    /// ```rust
-    /// # use clap::{Command,Arg, ArgAction, value_parser};
-    /// let m = Command::new("myprog")
-    ///     .arg(Arg::new("x")
-    ///         .short('x')
-    ///         .num_args(2)
-    ///         .action(ArgAction::Append)
-    ///         .value_parser(value_parser!(String)))
-    ///     .get_matches_from(vec![
-    ///         "myprog", "-x", "a", "b", "-x", "c", "d"]);
-    /// let vals: Vec<Vec<&String>> = m.get_occurrences("x").unwrap().map(Iterator::collect).collect();
-    /// assert_eq!(vals, [["a", "b"], ["c", "d"]]);
-    /// ```
-    #[cfg_attr(debug_assertions, track_caller)]
-    #[cfg(feature = "unstable-grouped")]
-    pub fn get_occurrences<T: Any + Clone + Send + Sync + 'static>(
-        &self,
-        id: &str,
-    ) -> Option<OccurrencesRef<T>> {
-        MatchesError::unwrap(id, self.try_get_occurrences(id))
-    }
-
     /// Iterate over the original argument values.
     ///
     /// An `OsStr` on Unix-like systems is any series of bytes, regardless of whether or not they
@@ -300,60 +262,6 @@ impl ArgMatches {
         MatchesError::unwrap(id, self.try_get_raw(id))
     }
 
-    /// Iterate over the original values for each occurrence of an option.
-    ///
-    /// Similar to [`ArgMatches::get_occurrences`] but returns raw values.
-    ///
-    /// An `OsStr` on Unix-like systems is any series of bytes, regardless of whether or not they
-    /// contain valid UTF-8. Since [`String`]s in Rust are guaranteed to be valid UTF-8, a valid
-    /// filename on a Unix system as an argument value may contain invalid UTF-8.
-    ///
-    /// Returns `None` if the option wasn't present.
-    ///
-    /// # Panic
-    ///
-    /// If the argument definition and access mismatch.  To handle this case programmatically, see
-    /// [`ArgMatches::try_get_raw_occurrences`].
-    ///
-    /// # Examples
-    ///
-    #[cfg_attr(not(unix), doc = " ```ignore")]
-    #[cfg_attr(unix, doc = " ```")]
-    /// # use clap::{Command, arg, value_parser, ArgAction, Arg};
-    /// # use std::ffi::{OsStr,OsString};
-    /// # use std::os::unix::ffi::{OsStrExt,OsStringExt};
-    /// use std::path::PathBuf;
-    ///
-    /// let m = Command::new("myprog")
-    ///     .arg(Arg::new("x")
-    ///         .short('x')
-    ///         .num_args(2)
-    ///         .action(ArgAction::Append)
-    ///         .value_parser(value_parser!(PathBuf)))
-    ///     .get_matches_from(vec![OsString::from("myprog"),
-    ///                             OsString::from("-x"),
-    ///                             OsString::from("a"), OsString::from("b"),
-    ///                             OsString::from("-x"),
-    ///                             OsString::from("c"),
-    ///                             // "{0xe9}!"
-    ///                             OsString::from_vec(vec![0xe9, b'!'])]);
-    /// let mut itr = m.get_raw_occurrences("x")
-    ///     .expect("`-x`is required")
-    ///     .map(Iterator::collect::<Vec<_>>);
-    /// assert_eq!(itr.next(), Some(vec![OsStr::new("a"), OsStr::new("b")]));
-    /// assert_eq!(itr.next(), Some(vec![OsStr::new("c"), OsStr::from_bytes(&[0xe9, b'!'])]));
-    /// assert_eq!(itr.next(), None);
-    /// ```
-    /// [`Iterator`]: std::iter::Iterator
-    /// [`OsStr`]: std::ffi::OsStr
-    /// [values]: OsValues
-    /// [`String`]: std::string::String
-    #[cfg(feature = "unstable-grouped")]
-    #[cfg_attr(debug_assertions, track_caller)]
-    pub fn get_raw_occurrences(&self, id: &str) -> Option<RawOccurrences<'_>> {
-        MatchesError::unwrap(id, self.try_get_raw_occurrences(id))
-    }
-
     /// Returns the value of a specific option or positional argument.
     ///
     /// i.e. an argument that [takes an additional value][crate::Arg::num_args] at runtime.
@@ -430,45 +338,6 @@ impl ArgMatches {
         MatchesError::unwrap(id, self.try_remove_many(id))
     }
 
-    /// Return values for each occurrence of an option.
-    ///
-    /// Each item is itself an iterator containing the arguments passed to a single occurrence of
-    /// the option.
-    ///
-    /// If the option doesn't support multiple occurrences, or there was only a single occurrence,
-    /// the iterator will only contain a single item.
-    ///
-    /// Returns `None` if the option wasn't present.
-    ///
-    /// # Panic
-    ///
-    /// If the argument definition and access mismatch.  To handle this case programmatically, see
-    /// [`ArgMatches::try_remove_occurrences`].
-    ///
-    /// # Examples
-    ///
-    /// ```rust
-    /// # use clap::{Command, Arg, value_parser, ArgAction};
-    /// let mut m = Command::new("myprog")
-    ///     .arg(Arg::new("x")
-    ///         .short('x')
-    ///         .num_args(2)
-    ///         .action(ArgAction::Append)
-    ///         .value_parser(value_parser!(String)))
-    ///     .get_matches_from(vec![
-    ///         "myprog", "-x", "a", "b", "-x", "c", "d"]);
-    /// let vals: Vec<Vec<String>> = m.remove_occurrences("x").unwrap().map(Iterator::collect).collect();
-    /// assert_eq!(vals, [["a", "b"], ["c", "d"]]);
-    /// ```
-    #[cfg(feature = "unstable-grouped")]
-    #[cfg_attr(debug_assertions, track_caller)]
-    pub fn remove_occurrences<T: Any + Clone + Send + Sync + 'static>(
-        &mut self,
-        id: &str,
-    ) -> Option<Occurrences<T>> {
-        MatchesError::unwrap(id, self.try_remove_occurrences(id))
-    }
-
     /// Check if values are present for the argument or group id
     ///
     /// *NOTE:* This will always return `true` if [`default_value`] has been set.
@@ -584,11 +453,6 @@ impl ArgMatches {
     /// [`Iterator`]: std::iter::Iterator
     #[cfg(feature = "unstable-grouped")]
     #[cfg_attr(debug_assertions, track_caller)]
-    #[deprecated(
-        since = "4.1.0",
-        note = "Use get_occurrences or remove_occurrences instead"
-    )]
-    #[allow(deprecated)]
     pub fn grouped_values_of(&self, id: &str) -> Option<GroupedValues> {
         let arg = some!(self.get_arg(id));
         let v = GroupedValues {
@@ -1103,30 +967,12 @@ impl ArgMatches {
         let values = arg.vals_flatten();
         let values = ValuesRef {
             // enforced by `try_get_arg_t`
-            iter: values.map(unwrap_downcast_ref),
+            iter: values.map(|v| v.downcast_ref::<T>().expect(INTERNAL_ERROR_MSG)),
             len,
         };
         Ok(Some(values))
     }
 
-    /// Non-panicking version of [`ArgMatches::get_occurrences`]
-    #[cfg(feature = "unstable-grouped")]
-    pub fn try_get_occurrences<T: Any + Clone + Send + Sync + 'static>(
-        &self,
-        id: &str,
-    ) -> Result<Option<OccurrencesRef<T>>, MatchesError> {
-        let arg = match ok!(self.try_get_arg_t::<T>(id)) {
-            Some(arg) => arg,
-            None => return Ok(None),
-        };
-        let values = arg.vals();
-        Ok(Some(OccurrencesRef {
-            iter: values.map(|g| OccurrenceValuesRef {
-                iter: g.iter().map(unwrap_downcast_ref),
-            }),
-        }))
-    }
-
     /// Non-panicking version of [`ArgMatches::get_raw`]
     pub fn try_get_raw(&self, id: &str) -> Result<Option<RawValues<'_>>, MatchesError> {
         let arg = match ok!(self.try_get_arg(id)) {
@@ -1142,25 +988,6 @@ impl ArgMatches {
         Ok(Some(values))
     }
 
-    /// Non-panicking version of [`ArgMatches::get_raw_occurrences`]
-    #[cfg(feature = "unstable-grouped")]
-    pub fn try_get_raw_occurrences(
-        &self,
-        id: &str,
-    ) -> Result<Option<RawOccurrences<'_>>, MatchesError> {
-        let arg = match ok!(self.try_get_arg(id)) {
-            Some(arg) => arg,
-            None => return Ok(None),
-        };
-        let values = arg.raw_vals();
-        let occurrences = RawOccurrences {
-            iter: values.map(|g| RawOccurrenceValues {
-                iter: g.iter().map(OsString::as_os_str),
-            }),
-        };
-        Ok(Some(occurrences))
-    }
-
     /// Non-panicking version of [`ArgMatches::remove_one`]
     pub fn try_remove_one<T: Any + Clone + Send + Sync + 'static>(
         &mut self,
@@ -1170,7 +997,7 @@ impl ArgMatches {
             Some(values) => Ok(values
                 .into_vals_flatten()
                 // enforced by `try_get_arg_t`
-                .map(unwrap_downcast_into)
+                .map(|v| v.downcast_into::<T>().expect(INTERNAL_ERROR_MSG))
                 .next()),
             None => Ok(None),
         }
@@ -1189,31 +1016,12 @@ impl ArgMatches {
         let values = arg.into_vals_flatten();
         let values = Values {
             // enforced by `try_get_arg_t`
-            iter: values.map(unwrap_downcast_into),
+            iter: values.map(|v| v.downcast_into::<T>().expect(INTERNAL_ERROR_MSG)),
             len,
         };
         Ok(Some(values))
     }
 
-    /// Non-panicking version of [`ArgMatches::remove_occurrences`]
-    #[cfg(feature = "unstable-grouped")]
-    pub fn try_remove_occurrences<T: Any + Clone + Send + Sync + 'static>(
-        &mut self,
-        id: &str,
-    ) -> Result<Option<Occurrences<T>>, MatchesError> {
-        let arg = match ok!(self.try_remove_arg_t::<T>(id)) {
-            Some(arg) => arg,
-            None => return Ok(None),
-        };
-        let values = arg.into_vals();
-        let occurrences = Occurrences {
-            iter: values.into_iter().map(|g| OccurrenceValues {
-                iter: g.into_iter().map(unwrap_downcast_into),
-            }),
-        };
-        Ok(Some(occurrences))
-    }
-
     /// Non-panicking version of [`ArgMatches::contains_id`]
     pub fn try_contains_id(&self, id: &str) -> Result<bool, MatchesError> {
         ok!(self.verify_arg(id));
@@ -1569,15 +1377,14 @@ impl Default for RawValues<'_> {
 // commit: be5e1fa3c26e351761b33010ddbdaf5f05dbcc33
 // license: MIT - Copyright (c) 2015 The Rust Project Developers
 
-#[derive(Clone, Debug)]
-#[deprecated(since = "4.1.0", note = "Use Occurrences instead")]
+#[derive(Clone)]
+#[allow(missing_debug_implementations)]
 pub struct GroupedValues<'a> {
     #[allow(clippy::type_complexity)]
     iter: Map<Iter<'a, Vec<AnyValue>>, fn(&Vec<AnyValue>) -> Vec<&str>>,
     len: usize,
 }
 
-#[allow(deprecated)]
 impl<'a> Iterator for GroupedValues<'a> {
     type Item = Vec<&'a str>;
 
@@ -1589,18 +1396,15 @@ impl<'a> Iterator for GroupedValues<'a> {
     }
 }
 
-#[allow(deprecated)]
 impl<'a> DoubleEndedIterator for GroupedValues<'a> {
     fn next_back(&mut self) -> Option<Self::Item> {
         self.iter.next_back()
     }
 }
 
-#[allow(deprecated)]
 impl<'a> ExactSizeIterator for GroupedValues<'a> {}
 
 /// Creates an empty iterator. Used for `unwrap_or_default()`.
-#[allow(deprecated)]
 impl<'a> Default for GroupedValues<'a> {
     fn default() -> Self {
         static EMPTY: [Vec<AnyValue>; 0] = [];
@@ -1611,206 +1415,6 @@ impl<'a> Default for GroupedValues<'a> {
     }
 }
 
-#[derive(Clone, Debug)]
-pub struct Occurrences<T> {
-    #[allow(clippy::type_complexity)]
-    iter: Map<std::vec::IntoIter<Vec<AnyValue>>, fn(Vec<AnyValue>) -> OccurrenceValues<T>>,
-}
-
-impl<T> Iterator for Occurrences<T> {
-    type Item = OccurrenceValues<T>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl<T> DoubleEndedIterator for Occurrences<T> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.iter.next_back()
-    }
-}
-
-impl<T> ExactSizeIterator for Occurrences<T> {}
-
-impl<T> Default for Occurrences<T> {
-    fn default() -> Self {
-        let empty: Vec<Vec<AnyValue>> = Default::default();
-        Occurrences {
-            iter: empty.into_iter().map(|_| unreachable!()),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct OccurrenceValues<T> {
-    #[allow(clippy::type_complexity)]
-    iter: Map<std::vec::IntoIter<AnyValue>, fn(AnyValue) -> T>,
-}
-
-impl<T> Iterator for OccurrenceValues<T> {
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl<T> DoubleEndedIterator for OccurrenceValues<T> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.iter.next_back()
-    }
-}
-
-impl<T> ExactSizeIterator for OccurrenceValues<T> {}
-
-#[derive(Clone, Debug)]
-pub struct OccurrencesRef<'a, T> {
-    #[allow(clippy::type_complexity)]
-    iter: Map<Iter<'a, Vec<AnyValue>>, fn(&Vec<AnyValue>) -> OccurrenceValuesRef<'_, T>>,
-}
-
-impl<'a, T> Iterator for OccurrencesRef<'a, T>
-where
-    Self: 'a,
-{
-    type Item = OccurrenceValuesRef<'a, T>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl<'a, T> DoubleEndedIterator for OccurrencesRef<'a, T>
-where
-    Self: 'a,
-{
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.iter.next_back()
-    }
-}
-
-impl<'a, T> ExactSizeIterator for OccurrencesRef<'a, T> where Self: 'a {}
-impl<'a, T> Default for OccurrencesRef<'a, T> {
-    fn default() -> Self {
-        static EMPTY: [Vec<AnyValue>; 0] = [];
-        OccurrencesRef {
-            iter: EMPTY[..].iter().map(|_| unreachable!()),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct OccurrenceValuesRef<'a, T> {
-    #[allow(clippy::type_complexity)]
-    iter: Map<Iter<'a, AnyValue>, fn(&AnyValue) -> &T>,
-}
-
-impl<'a, T> Iterator for OccurrenceValuesRef<'a, T>
-where
-    Self: 'a,
-{
-    type Item = &'a T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl<'a, T> DoubleEndedIterator for OccurrenceValuesRef<'a, T>
-where
-    Self: 'a,
-{
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.iter.next_back()
-    }
-}
-
-impl<'a, T> ExactSizeIterator for OccurrenceValuesRef<'a, T> where Self: 'a {}
-
-#[derive(Clone, Debug)]
-pub struct RawOccurrences<'a> {
-    #[allow(clippy::type_complexity)]
-    iter: Map<Iter<'a, Vec<OsString>>, fn(&Vec<OsString>) -> RawOccurrenceValues<'_>>,
-}
-
-impl<'a> Iterator for RawOccurrences<'a> {
-    type Item = RawOccurrenceValues<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl<'a> DoubleEndedIterator for RawOccurrences<'a> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.iter.next_back()
-    }
-}
-
-impl<'a> ExactSizeIterator for RawOccurrences<'a> {}
-
-impl<'a> Default for RawOccurrences<'a> {
-    fn default() -> Self {
-        static EMPTY: [Vec<OsString>; 0] = [];
-        RawOccurrences {
-            iter: EMPTY[..].iter().map(|_| unreachable!()),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct RawOccurrenceValues<'a> {
-    #[allow(clippy::type_complexity)]
-    iter: Map<Iter<'a, OsString>, fn(&OsString) -> &OsStr>,
-}
-
-impl<'a> Iterator for RawOccurrenceValues<'a>
-where
-    Self: 'a,
-{
-    type Item = &'a OsStr;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-}
-
-impl<'a> DoubleEndedIterator for RawOccurrenceValues<'a>
-where
-    Self: 'a,
-{
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.iter.next_back()
-    }
-}
-
-impl<'a> ExactSizeIterator for RawOccurrenceValues<'a> {}
-
 /// Iterate over indices for where an argument appeared when parsing, via [`ArgMatches::indices_of`]
 ///
 /// # Examples
@@ -1880,16 +1484,6 @@ fn unwrap_string(value: &AnyValue) -> &str {
     }
 }
 
-#[track_caller]
-fn unwrap_downcast_ref<T: Any + Clone + Send + Sync + 'static>(value: &AnyValue) -> &T {
-    value.downcast_ref().expect(INTERNAL_ERROR_MSG)
-}
-
-#[track_caller]
-fn unwrap_downcast_into<T: Any + Clone + Send + Sync + 'static>(value: AnyValue) -> T {
-    value.downcast_into().expect(INTERNAL_ERROR_MSG)
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/src/parser/matches/matched_arg.rs b/src/parser/matches/matched_arg.rs
index 188f82e6..6a80625f 100644
--- a/src/parser/matches/matched_arg.rs
+++ b/src/parser/matches/matched_arg.rs
@@ -80,11 +80,6 @@ impl MatchedArg {
         self.vals.iter()
     }
 
-    #[cfg(feature = "unstable-grouped")]
-    pub(crate) fn into_vals(self) -> Vec<Vec<AnyValue>> {
-        self.vals
-    }
-
     pub(crate) fn vals_flatten(&self) -> Flatten<Iter<Vec<AnyValue>>> {
         self.vals.iter().flatten()
     }
@@ -93,11 +88,6 @@ impl MatchedArg {
         self.vals.into_iter().flatten()
     }
 
-    #[cfg(feature = "unstable-grouped")]
-    pub(crate) fn raw_vals(&self) -> Iter<Vec<OsString>> {
-        self.raw_vals.iter()
-    }
-
     pub(crate) fn raw_vals_flatten(&self) -> Flatten<Iter<Vec<OsString>>> {
         self.raw_vals.iter().flatten()
     }
diff --git a/src/parser/parser.rs b/src/parser/parser.rs
index dc99e32e..9b7f9a9f 100644
--- a/src/parser/parser.rs
+++ b/src/parser/parser.rs
@@ -502,35 +502,33 @@ impl<'cmd> Parser<'cmd> {
             }
         }
 
-        if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) {
-            let candidates = suggestions::did_you_mean(
-                &arg_os.display().to_string(),
-                self.cmd.all_subcommand_names(),
+        let candidates = suggestions::did_you_mean(
+            &arg_os.display().to_string(),
+            self.cmd.all_subcommand_names(),
+        );
+        // If the argument looks like a subcommand.
+        if !candidates.is_empty() {
+            return ClapError::invalid_subcommand(
+                self.cmd,
+                arg_os.display().to_string(),
+                candidates,
+                self.cmd
+                    .get_bin_name()
+                    .unwrap_or_else(|| self.cmd.get_name())
+                    .to_owned(),
+                Usage::new(self.cmd).create_usage_with_title(&[]),
             );
-            // If the argument looks like a subcommand.
-            if !candidates.is_empty() {
-                return ClapError::invalid_subcommand(
-                    self.cmd,
-                    arg_os.display().to_string(),
-                    candidates,
-                    self.cmd
-                        .get_bin_name()
-                        .unwrap_or_else(|| self.cmd.get_name())
-                        .to_owned(),
-                    Usage::new(self.cmd).create_usage_with_title(&[]),
-                );
-            }
+        }
 
-            // If the argument must be a subcommand.
-            if self.cmd.has_subcommands()
-                && (!self.cmd.has_positionals() || self.cmd.is_infer_subcommands_set())
-            {
-                return ClapError::unrecognized_subcommand(
-                    self.cmd,
-                    arg_os.display().to_string(),
-                    Usage::new(self.cmd).create_usage_with_title(&[]),
-                );
-            }
+        // If the argument must be a subcommand.
+        if self.cmd.has_subcommands()
+            && (!self.cmd.has_positionals() || self.cmd.is_infer_subcommands_set())
+        {
+            return ClapError::unrecognized_subcommand(
+                self.cmd,
+                arg_os.display().to_string(),
+                Usage::new(self.cmd).create_usage_with_title(&[]),
+            );
         }
 
         let suggested_trailing_arg = !trailing_values


Test patch:
diff --git a/tests/builder/app_settings.rs b/tests/builder/app_settings.rs
index c2fc61c4..7cfe3443 100644
--- a/tests/builder/app_settings.rs
+++ b/tests/builder/app_settings.rs
@@ -654,6 +654,52 @@ Options:
     utils::assert_output(cmd, "clap-test --help", REQUIRE_EQUALS, false);
 }
 
+#[test]
+fn args_negate_subcommands_one_level() {
+    let res = Command::new("disablehelp")
+        .args_conflicts_with_subcommands(true)
+        .subcommand_negates_reqs(true)
+        .arg(arg!(<arg1> "some arg"))
+        .arg(arg!(<arg2> "some arg"))
+        .subcommand(
+            Command::new("sub1").subcommand(Command::new("sub2").subcommand(Command::new("sub3"))),
+        )
+        .try_get_matches_from(vec!["", "pickles", "sub1"]);
+    assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind());
+    let m = res.unwrap();
+    assert_eq!(
+        m.get_one::<String>("arg2").map(|v| v.as_str()),
+        Some("sub1")
+    );
+}
+
+#[test]
+fn args_negate_subcommands_two_levels() {
+    let res = Command::new("disablehelp")
+        .args_conflicts_with_subcommands(true)
+        .subcommand_negates_reqs(true)
+        .arg(arg!(<arg1> "some arg"))
+        .arg(arg!(<arg2> "some arg"))
+        .subcommand(
+            Command::new("sub1")
+                .args_conflicts_with_subcommands(true)
+                .subcommand_negates_reqs(true)
+                .arg(arg!(<arg> "some"))
+                .arg(arg!(<arg2> "some"))
+                .subcommand(Command::new("sub2").subcommand(Command::new("sub3"))),
+        )
+        .try_get_matches_from(vec!["", "sub1", "arg", "sub2"]);
+    assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind());
+    let m = res.unwrap();
+    assert_eq!(
+        m.subcommand_matches("sub1")
+            .unwrap()
+            .get_one::<String>("arg2")
+            .map(|v| v.as_str()),
+        Some("sub2")
+    );
+}
+
 #[test]
 fn propagate_vals_down() {
     let m = Command::new("myprog")
diff --git a/tests/builder/conflicts.rs b/tests/builder/conflicts.rs
index 1644b86f..32a3342a 100644
--- a/tests/builder/conflicts.rs
+++ b/tests/builder/conflicts.rs
@@ -655,74 +655,6 @@ fn exclusive_with_required() {
     cmd.clone().try_get_matches_from(["bug", "--test"]).unwrap();
 }
 
-#[test]
-fn args_negate_subcommands_one_level() {
-    let res = Command::new("disablehelp")
-        .args_conflicts_with_subcommands(true)
-        .subcommand_negates_reqs(true)
-        .arg(arg!(<arg1> "some arg"))
-        .arg(arg!(<arg2> "some arg"))
-        .subcommand(
-            Command::new("sub1").subcommand(Command::new("sub2").subcommand(Command::new("sub3"))),
-        )
-        .try_get_matches_from(vec!["", "pickles", "sub1"]);
-    assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind());
-    let m = res.unwrap();
-    assert_eq!(
-        m.get_one::<String>("arg2").map(|v| v.as_str()),
-        Some("sub1")
-    );
-}
-
-#[test]
-fn args_negate_subcommands_two_levels() {
-    let res = Command::new("disablehelp")
-        .args_conflicts_with_subcommands(true)
-        .subcommand_negates_reqs(true)
-        .arg(arg!(<arg1> "some arg"))
-        .arg(arg!(<arg2> "some arg"))
-        .subcommand(
-            Command::new("sub1")
-                .args_conflicts_with_subcommands(true)
-                .subcommand_negates_reqs(true)
-                .arg(arg!(<arg> "some"))
-                .arg(arg!(<arg2> "some"))
-                .subcommand(Command::new("sub2").subcommand(Command::new("sub3"))),
-        )
-        .try_get_matches_from(vec!["", "sub1", "arg", "sub2"]);
-    assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind());
-    let m = res.unwrap();
-    assert_eq!(
-        m.subcommand_matches("sub1")
-            .unwrap()
-            .get_one::<String>("arg2")
-            .map(|v| v.as_str()),
-        Some("sub2")
-    );
-}
-
-#[test]
-#[cfg(feature = "error-context")]
-fn subcommand_conflict_error_message() {
-    static CONFLICT_ERR: &str = "\
-error: Found argument 'sub1' which wasn't expected, or isn't valid in this context
-
-Usage: test [OPTIONS]
-       test <COMMAND>
-
-For more information try '--help'
-";
-
-    let cmd = Command::new("test")
-        .args_conflicts_with_subcommands(true)
-        .arg(arg!(-p --place <"place id"> "Place ID to open"))
-        .subcommand(
-            Command::new("sub1").subcommand(Command::new("sub2").subcommand(Command::new("sub3"))),
-        );
-
-    utils::assert_output(cmd, "test --place id sub1", CONFLICT_ERR, true);
-}
-
 #[test]
 fn subcommand_conflict_negates_required() {
     let cmd = Command::new("test")
diff --git a/tests/builder/occurrences.rs b/tests/builder/grouped_values.rs
similarity index 88%
rename from tests/builder/occurrences.rs
rename to tests/builder/grouped_values.rs
index 56716cb7..726eac92 100644
--- a/tests/builder/occurrences.rs
+++ b/tests/builder/grouped_values.rs
@@ -1,13 +1,6 @@
 #![cfg(feature = "unstable-grouped")]
 
-use clap::{Arg, ArgAction, ArgMatches, Command};
-
-fn occurrences_as_vec_vec<'a>(m: &'a ArgMatches, name: &str) -> Vec<Vec<&'a String>> {
-    m.get_occurrences(name)
-        .unwrap()
-        .map(Iterator::collect)
-        .collect()
-}
+use clap::{Arg, ArgAction, Command};
 
 #[test]
 fn grouped_value_works() {
@@ -29,7 +22,7 @@ fn grouped_value_works() {
             "en_US:my option 2",
         ])
         .unwrap();
-    let grouped_vals = occurrences_as_vec_vec(&m, "option");
+    let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect();
     assert_eq!(
         grouped_vals,
         vec![
@@ -57,7 +50,7 @@ fn issue_1026() {
             "target3", "file8",
         ])
         .unwrap();
-    let grouped_vals = occurrences_as_vec_vec(&m, "target");
+    let grouped_vals: Vec<_> = m.grouped_values_of("target").unwrap().collect();
     assert_eq!(
         grouped_vals,
         vec![
@@ -87,7 +80,7 @@ fn grouped_value_long_flag_delimiter() {
             "alice,bob",
         ])
         .unwrap();
-    let grouped_vals = occurrences_as_vec_vec(&m, "option");
+    let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect();
     assert_eq!(
         grouped_vals,
         vec![
@@ -111,7 +104,7 @@ fn grouped_value_short_flag_delimiter() {
         )
         .try_get_matches_from(vec!["myapp", "-o=foo", "-o=val1,val2,val3", "-o=bar"])
         .unwrap();
-    let grouped_vals = occurrences_as_vec_vec(&m, "option");
+    let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect();
     assert_eq!(
         grouped_vals,
         vec![vec!["foo"], vec!["val1", "val2", "val3"], vec!["bar"]]
@@ -131,7 +124,7 @@ fn grouped_value_positional_arg() {
             "myprog", "val1", "val2", "val3", "val4", "val5", "val6",
         ])
         .unwrap();
-    let grouped_vals = occurrences_as_vec_vec(&m, "pos");
+    let grouped_vals: Vec<_> = m.grouped_values_of("pos").unwrap().collect();
     assert_eq!(
         grouped_vals,
         vec![vec!["val1", "val2", "val3", "val4", "val5", "val6"]]
@@ -152,7 +145,7 @@ fn grouped_value_multiple_positional_arg() {
             "myprog", "val1", "val2", "val3", "val4", "val5", "val6",
         ])
         .unwrap();
-    let grouped_vals = occurrences_as_vec_vec(&m, "pos2");
+    let grouped_vals: Vec<_> = m.grouped_values_of("pos2").unwrap().collect();
     assert_eq!(
         grouped_vals,
         vec![vec!["val2", "val3", "val4", "val5", "val6"]]
@@ -174,7 +167,7 @@ fn grouped_value_multiple_positional_arg_last_multiple() {
             "myprog", "val1", "--", "val2", "val3", "val4", "val5", "val6",
         ])
         .unwrap();
-    let grouped_vals = occurrences_as_vec_vec(&m, "pos2");
+    let grouped_vals: Vec<_> = m.grouped_values_of("pos2").unwrap().collect();
     assert_eq!(
         grouped_vals,
         vec![vec!["val2", "val3", "val4", "val5", "val6"]]
@@ -197,10 +190,10 @@ fn grouped_interleaved_positional_values() {
         .try_get_matches_from(["foo", "1", "2", "-f", "a", "3", "-f", "b", "4"])
         .unwrap();
 
-    let pos = occurrences_as_vec_vec(&m, "pos");
+    let pos: Vec<_> = m.grouped_values_of("pos").unwrap().collect();
     assert_eq!(pos, vec![vec!["1", "2"], vec!["3"], vec!["4"]]);
 
-    let flag = occurrences_as_vec_vec(&m, "flag");
+    let flag: Vec<_> = m.grouped_values_of("flag").unwrap().collect();
     assert_eq!(flag, vec![vec!["a"], vec!["b"]]);
 }
 
@@ -220,10 +213,10 @@ fn grouped_interleaved_positional_occurrences() {
         .try_get_matches_from(["foo", "1", "2", "-f", "a", "3", "-f", "b", "4"])
         .unwrap();
 
-    let pos = occurrences_as_vec_vec(&m, "pos");
+    let pos: Vec<_> = m.grouped_values_of("pos").unwrap().collect();
     assert_eq!(pos, vec![vec!["1", "2"], vec!["3"], vec!["4"]]);
 
-    let flag = occurrences_as_vec_vec(&m, "flag");
+    let flag: Vec<_> = m.grouped_values_of("flag").unwrap().collect();
     assert_eq!(flag, vec![vec!["a"], vec!["b"]]);
 }
 
diff --git a/tests/builder/help.rs b/tests/builder/help.rs
index c448f711..dbd8084a 100644
--- a/tests/builder/help.rs
+++ b/tests/builder/help.rs
@@ -1042,6 +1042,7 @@ Options:
   -V, --version  Print version information
 ";
 
+    #[cfg(not(feature = "unstable-v5"))]
     let cmd = Command::new("ripgrep")
         .version("0.5")
         .override_usage(
@@ -1057,6 +1058,26 @@ Options:
 
 Usage: {usage}
 
+Options:
+{options}",
+        );
+
+    #[cfg(feature = "unstable-v5")]
+    let cmd = Command::new("ripgrep")
+        .version("0.5")
+        .override_usage(
+            "\
+       rg [OPTIONS] <pattern> [<path> ...]
+       rg [OPTIONS] [-e PATTERN | -f FILE ]... [<path> ...]
+       rg [OPTIONS] --files [<path> ...]
+       rg [OPTIONS] --type-list",
+        )
+        .help_template(
+            "\
+{name} {version}
+
+Usage: {usage}
+
 Options:
 {options}",
         );
diff --git a/tests/builder/main.rs b/tests/builder/main.rs
index 22fdd5f0..858f2875 100644
--- a/tests/builder/main.rs
+++ b/tests/builder/main.rs
@@ -24,6 +24,7 @@ mod error;
 mod flag_subcommands;
 mod flags;
 mod global_args;
+mod grouped_values;
 mod groups;
 mod help;
 mod help_env;
@@ -32,7 +33,6 @@ mod ignore_errors;
 mod indices;
 mod multiple_occurrences;
 mod multiple_values;
-mod occurrences;
 mod opts;
 mod positionals;
 mod posix_compatible;
diff --git a/tests/builder/template_help.rs b/tests/builder/template_help.rs
index 3df5ab46..4d305a24 100644
--- a/tests/builder/template_help.rs
+++ b/tests/builder/template_help.rs
@@ -2,6 +2,7 @@ use super::utils;
 
 use clap::{arg, Command};
 
+#[cfg(not(feature = "unstable-v5"))]
 static EXAMPLE1_TMPL_S: &str = "{bin} {version}
 {author}
 {about}
@@ -10,6 +11,16 @@ Usage: {usage}
 
 {all-args}";
 
+#[cfg(feature = "unstable-v5")]
+static EXAMPLE1_TMPL_S: &str = "{name} {version}
+{author}
+{about}
+
+Usage: {usage}
+
+{all-args}";
+
+#[cfg(not(feature = "unstable-v5"))]
 static EXAMPLE1_TMPS_F: &str = "{bin} {version}
 {author}
 {about}
@@ -23,6 +34,20 @@ Arguments:
 Commands:
 {subcommands}";
 
+#[cfg(feature = "unstable-v5")]
+static EXAMPLE1_TMPS_F: &str = "{name} {version}
+{author}
+{about}
+
+Usage: {usage}
+
+Options:
+{options}
+Arguments:
+{positionals}
+Commands:
+{subcommands}";
+
 static CUSTOM_TEMPL_HELP: &str = "MyApp 1.0
 Kevin K. <kbknapp@gmail.com>
 Does awesome things
@@ -105,11 +130,20 @@ fn template_unknowntag() {
 
 #[test]
 fn template_author_version() {
+    #[cfg(not(feature = "unstable-v5"))]
     let cmd = Command::new("MyApp")
         .version("1.0")
         .author("Kevin K. <kbknapp@gmail.com>")
         .about("Does awesome things")
         .help_template("{author}\n{version}\n{about}\n{bin}");
+
+    #[cfg(feature = "unstable-v5")]
+    let cmd = Command::new("MyApp")
+        .version("1.0")
+        .author("Kevin K. <kbknapp@gmail.com>")
+        .about("Does awesome things")
+        .help_template("{author}\n{version}\n{about}\n{name}");
+
     utils::assert_output(
         cmd,
         "MyApp --help",