Repository: clap-rs/clap
Pull request #4881 (2023-05-04 01:21:13)

chore: Dedup clap derive get fields

nyurik: thx, fixed

Patch:
diff --git a/clap_builder/src/builder/possible_value.rs b/clap_builder/src/builder/possible_value.rs
index de7e543f..b0282e59 100644
--- a/clap_builder/src/builder/possible_value.rs
+++ b/clap_builder/src/builder/possible_value.rs
@@ -162,6 +162,18 @@ impl PossibleValue {
         self.help.as_ref()
     }
 
+    /// Get the help specified for this argument, if any and the argument
+    /// value is not hidden
+    #[inline]
+    #[cfg(feature = "help")]
+    pub(crate) fn get_visible_help(&self) -> Option<&StyledStr> {
+        if !self.hide {
+            self.get_help()
+        } else {
+            None
+        }
+    }
+
     /// Report if [`PossibleValue::hide`] is set
     #[inline]
     pub fn is_hide_set(&self) -> bool {
diff --git a/clap_builder/src/builder/value_hint.rs b/clap_builder/src/builder/value_hint.rs
index 38fa862c..ee286b19 100644
--- a/clap_builder/src/builder/value_hint.rs
+++ b/clap_builder/src/builder/value_hint.rs
@@ -24,12 +24,11 @@ use std::str::FromStr;
 ///
 /// [^1]: fish completions currently only support named arguments (e.g. -o or --opt), not
 ///       positional arguments.
-#[derive(Debug, Default, PartialEq, Eq, Hash, Copy, Clone)]
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
 #[non_exhaustive]
 pub enum ValueHint {
     /// Default value if hint is not specified. Follows shell default behavior, which is usually
     /// auto-completing filenames.
-    #[default]
     Unknown,
     /// None of the hints below apply. Disables shell completion for this argument.
     Other,
@@ -67,6 +66,12 @@ pub enum ValueHint {
     EmailAddress,
 }
 
+impl Default for ValueHint {
+    fn default() -> Self {
+        ValueHint::Unknown
+    }
+}
+
 impl FromStr for ValueHint {
     type Err = String;
     fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
diff --git a/clap_builder/src/error/format.rs b/clap_builder/src/error/format.rs
index 49e617d0..1d2a760f 100644
--- a/clap_builder/src/error/format.rs
+++ b/clap_builder/src/error/format.rs
@@ -38,7 +38,7 @@ impl ErrorFormatter for KindFormatter {
         if let Some(msg) = error.kind().as_str() {
             styled.push_str(msg);
         } else if let Some(source) = error.inner.source.as_ref() {
-            let _ = write!(styled, "{source}");
+            let _ = write!(styled, "{}", source);
         } else {
             styled.push_str("unknown cause");
         }
@@ -68,7 +68,7 @@ impl ErrorFormatter for RichFormatter {
             if let Some(msg) = error.kind().as_str() {
                 styled.push_str(msg);
             } else if let Some(source) = error.inner.source.as_ref() {
-                let _ = write!(styled, "{source}");
+                let _ = write!(styled, "{}", source);
             } else {
                 styled.push_str("unknown cause");
             }
@@ -357,7 +357,7 @@ fn write_dynamic_context(
                     literal.render_reset(),
                 );
                 if let Some(source) = error.inner.source.as_deref() {
-                    let _ = write!(styled, ": {source}");
+                    let _ = write!(styled, ": {}", source);
                 }
                 true
             } else {
diff --git a/clap_builder/src/output/help_template.rs b/clap_builder/src/output/help_template.rs
index 76009589..863e906b 100644
--- a/clap_builder/src/output/help_template.rs
+++ b/clap_builder/src/output/help_template.rs
@@ -654,10 +654,11 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
         self.writer.push_styled(&help);
         if let Some(arg) = arg {
             const DASH_SPACE: usize = "- ".len();
+            const COLON_SPACE: usize = ": ".len();
             let possible_vals = arg.get_possible_values();
-            if !possible_vals.is_empty()
+            if self.use_long
                 && !arg.is_hide_possible_values_set()
-                && self.use_long_pv(arg)
+                && possible_vals.iter().any(PossibleValue::should_show_help)
             {
                 debug!(
                     "HelpTemplate::help: Found possible vals...{:?}",
@@ -665,13 +666,26 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
                 );
                 let longest = possible_vals
                     .iter()
-                    .filter(|f| !f.is_hide_set())
-                    .map(|f| display_width(f.get_name()))
+                    .filter_map(|f| f.get_visible_quoted_name().map(|name| display_width(&name)))
                     .max()
                     .expect("Only called with possible value");
+                let help_longest = possible_vals
+                    .iter()
+                    .filter_map(|f| f.get_visible_help().map(|h| h.display_width()))
+                    .max()
+                    .expect("Only called with possible value with help");
+                // should new line
+                let taken = longest + spaces + DASH_SPACE;
+
+                let possible_value_new_line =
+                    self.term_w >= taken && self.term_w < taken + COLON_SPACE + help_longest;
 
                 let spaces = spaces + TAB_WIDTH - DASH_SPACE;
-                let trailing_indent = spaces + DASH_SPACE;
+                let trailing_indent = if possible_value_new_line {
+                    spaces + DASH_SPACE
+                } else {
+                    spaces + longest + DASH_SPACE + COLON_SPACE
+                };
                 let trailing_indent = self.get_spaces(trailing_indent);
 
                 if !help_is_empty {
@@ -690,9 +704,14 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
                     if let Some(help) = pv.get_help() {
                         debug!("HelpTemplate::help: Possible Value help");
 
-                        // To align help messages
-                        let padding = longest - display_width(pv.get_name());
-                        let _ = write!(self.writer, ": {:padding$}", "");
+                        if possible_value_new_line {
+                            let padding = trailing_indent.len();
+                            let _ = write!(self.writer, ":\n{:padding$}", "");
+                        } else {
+                            // To align help messages
+                            let padding = longest - display_width(pv.get_name());
+                            let _ = write!(self.writer, ": {:padding$}", "");
+                        }
 
                         let avail_chars = if self.term_w > trailing_indent.len() {
                             self.term_w - trailing_indent.len()
@@ -816,7 +835,10 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
         }
 
         let possible_vals = a.get_possible_values();
-        if !possible_vals.is_empty() && !a.is_hide_possible_values_set() && !self.use_long_pv(a) {
+        if !(a.is_hide_possible_values_set()
+            || possible_vals.is_empty()
+            || self.use_long && possible_vals.iter().any(PossibleValue::should_show_help))
+        {
             debug!(
                 "HelpTemplate::spec_vals: Found possible vals...{:?}",
                 possible_vals
@@ -842,14 +864,6 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
         use std::fmt::Write as _;
         let _ = write!(self.writer, "{:amount$}", "");
     }
-
-    fn use_long_pv(&self, arg: &Arg) -> bool {
-        self.use_long
-            && arg
-                .get_possible_values()
-                .iter()
-                .any(PossibleValue::should_show_help)
-    }
 }
 
 /// Subcommand handling
diff --git a/clap_builder/src/parser/matches/arg_matches.rs b/clap_builder/src/parser/matches/arg_matches.rs
index 2fa52071..11df68fe 100644
--- a/clap_builder/src/parser/matches/arg_matches.rs
+++ b/clap_builder/src/parser/matches/arg_matches.rs
@@ -143,7 +143,10 @@ impl ArgMatches {
     #[cfg_attr(debug_assertions, track_caller)]
     pub fn get_count(&self, id: &str) -> u8 {
         *self.get_one::<u8>(id).unwrap_or_else(|| {
-            panic!("arg `{id}`'s `ArgAction` should be `Count` which should provide a default")
+            panic!(
+                "arg `{}`'s `ArgAction` should be `Count` which should provide a default",
+                id
+            )
         })
     }
 
@@ -179,7 +182,8 @@ impl ArgMatches {
             .get_one::<bool>(id)
             .unwrap_or_else(|| {
                 panic!(
-                    "arg `{id}`'s `ArgAction` should be one of `SetTrue`, `SetFalse` which should provide a default"
+                    "arg `{}`'s `ArgAction` should be one of `SetTrue`, `SetFalse` which should provide a default",
+                    id
                 )
             })
     }
diff --git a/clap_complete/src/generator/mod.rs b/clap_complete/src/generator/mod.rs
index 0c52b85d..c766508e 100644
--- a/clap_complete/src/generator/mod.rs
+++ b/clap_complete/src/generator/mod.rs
@@ -28,10 +28,10 @@ pub trait Generator {
     /// pub struct Fish;
     ///
     /// impl Generator for Fish {
+    /// #   fn generate(&self, cmd: &Command, buf: &mut dyn Write) {}
     ///     fn file_name(&self, name: &str) -> String {
     ///         format!("{}.fish", name)
     ///     }
-    /// #   fn generate(&self, cmd: &Command, buf: &mut dyn Write) {}
     /// }
     /// ```
     fn file_name(&self, name: &str) -> String;
@@ -55,12 +55,12 @@ pub trait Generator {
     /// pub struct ClapDebug;
     ///
     /// impl Generator for ClapDebug {
-    /// #   fn file_name(&self, name: &str) -> String {
-    /// #       name.into()
-    /// #   }
     ///     fn generate(&self, cmd: &Command, buf: &mut dyn Write) {
     ///         write!(buf, "{}", cmd).unwrap();
     ///     }
+    /// #   fn file_name(&self, name: &str) -> String {
+    /// #    name.into()
+    /// #   }
     /// }
     /// ```
     fn generate(&self, cmd: &Command, buf: &mut dyn Write);
@@ -164,7 +164,7 @@ pub trait Generator {
 /// to see the name of the files generated.
 pub fn generate_to<G, S, T>(
     gen: G,
-    cmd: &mut Command,
+    cmd: &mut clap::Command,
     bin_name: S,
     out_dir: T,
 ) -> Result<PathBuf, Error>
@@ -181,7 +181,7 @@ where
     let path = out_dir.join(file_name);
     let mut file = File::create(&path)?;
 
-    _generate::<G>(gen, cmd, &mut file);
+    _generate::<G, S>(gen, cmd, &mut file);
     Ok(path)
 }
 
@@ -220,16 +220,21 @@ where
 /// ```console
 /// $ myapp generate-bash-completions > /usr/share/bash-completion/completions/myapp.bash
 /// ```
-pub fn generate<G, S>(gen: G, cmd: &mut Command, bin_name: S, buf: &mut dyn Write)
+pub fn generate<G, S>(gen: G, cmd: &mut clap::Command, bin_name: S, buf: &mut dyn Write)
 where
     G: Generator,
     S: Into<String>,
 {
     cmd.set_bin_name(bin_name);
-    _generate::<G>(gen, cmd, buf)
+    _generate::<G, S>(gen, cmd, buf)
 }
 
-fn _generate<G: Generator>(gen: G, cmd: &mut Command, buf: &mut dyn Write) {
+fn _generate<G, S>(gen: G, cmd: &mut clap::Command, buf: &mut dyn Write)
+where
+    G: Generator,
+    S: Into<String>,
+{
     cmd.build();
+
     gen.generate(cmd, buf)
 }
diff --git a/clap_complete/src/shells/powershell.rs b/clap_complete/src/shells/powershell.rs
index df31bfaf..e449e2e3 100644
--- a/clap_complete/src/shells/powershell.rs
+++ b/clap_complete/src/shells/powershell.rs
@@ -50,7 +50,9 @@ Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{
     $completions.Where{{ $_.CompletionText -like "$wordToComplete*" }} |
         Sort-Object -Property ListItemText
 }}
-"#
+"#,
+            bin_name = bin_name,
+            subcommands_cases = subcommands_cases
         );
 
         w!(buf, result.as_bytes());
diff --git a/clap_complete/src/shells/zsh.rs b/clap_complete/src/shells/zsh.rs
index 9c91af00..28c8ff85 100644
--- a/clap_complete/src/shells/zsh.rs
+++ b/clap_complete/src/shells/zsh.rs
@@ -475,7 +475,14 @@ fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String {
 
         if let Some(shorts) = o.get_short_and_visible_aliases() {
             for short in shorts {
-                let s = format!("'{conflicts}{multiple}-{short}+[{help}]{vc}' \\");
+                let s = format!(
+                    "'{conflicts}{multiple}-{arg}+[{help}]{value_completion}' \\",
+                    conflicts = conflicts,
+                    multiple = multiple,
+                    arg = short,
+                    value_completion = vc,
+                    help = help
+                );
 
                 debug!("write_opts_of:iter: Wrote...{}", &*s);
                 ret.push(s);
@@ -483,7 +490,14 @@ fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String {
         }
         if let Some(longs) = o.get_long_and_visible_aliases() {
             for long in longs {
-                let l = format!("'{conflicts}{multiple}--{long}=[{help}]{vc}' \\");
+                let l = format!(
+                    "'{conflicts}{multiple}--{arg}=[{help}]{value_completion}' \\",
+                    conflicts = conflicts,
+                    multiple = multiple,
+                    arg = long,
+                    value_completion = vc,
+                    help = help
+                );
 
                 debug!("write_opts_of:iter: Wrote...{}", &*l);
                 ret.push(l);
@@ -550,7 +564,13 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String {
         };
 
         if let Some(short) = f.get_short() {
-            let s = format!("'{conflicts}{multiple}-{short}[{help}]' \\");
+            let s = format!(
+                "'{conflicts}{multiple}-{arg}[{help}]' \\",
+                multiple = multiple,
+                conflicts = conflicts,
+                arg = short,
+                help = help
+            );
 
             debug!("write_flags_of:iter: Wrote...{}", &*s);
 
@@ -568,7 +588,13 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String {
         }
 
         if let Some(long) = f.get_long() {
-            let l = format!("'{conflicts}{multiple}--{long}[{help}]' \\");
+            let l = format!(
+                "'{conflicts}{multiple}--{arg}[{help}]' \\",
+                conflicts = conflicts,
+                multiple = multiple,
+                arg = long,
+                help = help
+            );
 
             debug!("write_flags_of:iter: Wrote...{}", &*l);
 
@@ -576,7 +602,13 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String {
 
             if let Some(aliases) = f.get_visible_aliases() {
                 for alias in aliases {
-                    let l = format!("'{conflicts}{multiple}--{alias}[{help}]' \\");
+                    let l = format!(
+                        "'{conflicts}{multiple}--{arg}[{help}]' \\",
+                        conflicts = conflicts,
+                        multiple = multiple,
+                        arg = alias,
+                        help = help
+                    );
 
                     debug!("write_flags_of:iter: Wrote...{}", &*l);
 
diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs
index e8611b6b..8a9272af 100644
--- a/clap_derive/src/derives/args.rs
+++ b/clap_derive/src/derives/args.rs
@@ -16,7 +16,7 @@ use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote, quote_spanned};
 use syn::{
     punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field,
-    Fields, Generics,
+    Fields, FieldsNamed, Generics,
 };
 
 use crate::item::{Item, Kind, Name};
@@ -32,14 +32,7 @@ pub fn derive_args(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
         }) => {
             let name = Name::Derived(ident.clone());
             let item = Item::from_args_struct(input, name)?;
-            let fields = fields
-                .named
-                .iter()
-                .map(|field| {
-                    let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
-                    Ok((field, item))
-                })
-                .collect::<Result<Vec<_>, syn::Error>>()?;
+            let fields = collect_args_fields(&item, fields)?;
             gen_for_struct(&item, ident, &input.generics, &fields)
         }
         Data::Struct(DataStruct {
@@ -740,3 +733,17 @@ pub fn raw_deprecated() -> TokenStream {
 
     }
 }
+
+pub fn collect_args_fields<'a>(
+    item: &'a Item,
+    fields: &'a FieldsNamed,
+) -> Result<Vec<(&'a Field, Item)>, syn::Error> {
+    fields
+        .named
+        .iter()
+        .map(|field| {
+            let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
+            Ok((field, item))
+        })
+        .collect()
+}
diff --git a/clap_derive/src/derives/parser.rs b/clap_derive/src/derives/parser.rs
index 39b0b8a7..926c50ce 100644
--- a/clap_derive/src/derives/parser.rs
+++ b/clap_derive/src/derives/parser.rs
@@ -21,6 +21,7 @@ use syn::{
     Generics,
 };
 
+use crate::derives::args::collect_args_fields;
 use crate::derives::{args, into_app, subcommand};
 use crate::item::Item;
 use crate::item::Name;
@@ -36,14 +37,7 @@ pub fn derive_parser(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
         }) => {
             let name = Name::Assigned(quote!(#pkg_name));
             let item = Item::from_args_struct(input, name)?;
-            let fields = fields
-                .named
-                .iter()
-                .map(|field| {
-                    let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
-                    Ok((field, item))
-                })
-                .collect::<Result<Vec<_>, syn::Error>>()?;
+            let fields = collect_args_fields(&item, fields)?;
             gen_for_struct(&item, ident, &input.generics, &fields)
         }
         Data::Struct(DataStruct {
diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs
index 403fe455..5c235096 100644
--- a/clap_derive/src/derives/subcommand.rs
+++ b/clap_derive/src/derives/subcommand.rs
@@ -17,6 +17,7 @@ use quote::{format_ident, quote, quote_spanned};
 use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
 
 use crate::derives::args;
+use crate::derives::args::collect_args_fields;
 use crate::item::{Item, Kind, Name};
 use crate::utils::{is_simple_ty, subty_if_name};
 
@@ -264,15 +265,7 @@ fn gen_augment(
                 let sub_augment = match variant.fields {
                     Named(ref fields) => {
                         // Defer to `gen_augment` for adding cmd methods
-                        let fields = fields
-                            .named
-                            .iter()
-                            .map(|field| {
-                                let item =
-                                    Item::from_args_field(field, item.casing(), item.env_casing())?;
-                                Ok((field, item))
-                            })
-                            .collect::<Result<Vec<_>, syn::Error>>()?;
+                        let fields = collect_args_fields(item, fields)?;
                         args::gen_augment(&fields, &subcommand_var, item, override_required)?
                     }
                     Unit => {
@@ -484,14 +477,7 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, sy
         let variant_name = &variant.ident;
         let constructor_block = match variant.fields {
             Named(ref fields) => {
-                let fields = fields
-                    .named
-                    .iter()
-                    .map(|field| {
-                        let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
-                        Ok((field, item))
-                    })
-                    .collect::<Result<Vec<_>, syn::Error>>()?;
+                let fields = collect_args_fields(item, fields)?;
                 args::gen_constructor(&fields)?
             },
             Unit => quote!(),
@@ -599,14 +585,7 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStr
                 let field_names = fields.named.iter().map(|field| {
                     field.ident.as_ref().unwrap()
                 }).collect::<Vec<_>>();
-                let fields = fields
-                    .named
-                    .iter()
-                    .map(|field| {
-                        let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
-                        Ok((field, item))
-                    })
-                    .collect::<Result<Vec<_>, syn::Error>>()?;
+                let fields = collect_args_fields(item, fields)?;
                 let update = args::gen_updater(&fields, false)?;
                 (quote!( { #( #field_names, )* }), quote!( { #update } ))
             }
diff --git a/clap_derive/src/item.rs b/clap_derive/src/item.rs
index 71a9e968..9b29ff9e 100644
--- a/clap_derive/src/item.rs
+++ b/clap_derive/src/item.rs
@@ -1247,8 +1247,8 @@ impl Method {
                     ident,
                     "cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n",
                     ident,
-                    note = format_args!("`{env_var}` environment variable is not set"),
-                    help = format_args!("use `{ident} = \"...\"` to set {ident} manually")
+                    note = format_args!("`{}` environment variable is not set", env_var),
+                    help = format_args!("use `{} = \"...\"` to set {} manually", ident, ident)
                 );
             }
         };


Test patch:
diff --git a/tests/builder/double_require.rs b/tests/builder/double_require.rs
index 7438b228..11e0e45c 100644
--- a/tests/builder/double_require.rs
+++ b/tests/builder/double_require.rs
@@ -51,7 +51,7 @@ fn help_text() {
     assert!(res.is_err());
     let err = res.unwrap_err();
     assert_eq!(err.kind(), ErrorKind::DisplayHelp);
-    println!("{err}");
+    println!("{}", err);
     assert_eq!(err.to_string(), HELP);
 }
 
diff --git a/tests/builder/help.rs b/tests/builder/help.rs
index 526bc0c6..ab060278 100644
--- a/tests/builder/help.rs
+++ b/tests/builder/help.rs
@@ -449,12 +449,15 @@ Usage: test [OPTIONS]
 Options:
       --possible-values <possible_values>
           Possible values:
-          - short_name: Long enough help message, barely warrant wrapping
-          - second:     Short help gets handled the same
+          - short_name:
+            Long enough help message, barely warrant wrapping
+          - second:
+            Short help gets handled the same
 
       --possible-values-with-new-line <possible_values_with_new_line>
           Possible values:
-          - long enough name to trigger new line: Really long enough help message to clearly warrant
+          - long enough name to trigger new line:
+            Really long enough help message to clearly warrant
             wrapping believe me
           - second
 
@@ -2647,7 +2650,8 @@ fn disable_help_flag_affects_help_subcommand() {
         .collect::<Vec<_>>();
     assert!(
         !args.contains(&"help"),
-        "`help` should not be present: {args:?}"
+        "`help` should not be present: {:?}",
+        args
     );
 }
 
diff --git a/tests/builder/ignore_errors.rs b/tests/builder/ignore_errors.rs
index 3103ad51..dfb35753 100644
--- a/tests/builder/ignore_errors.rs
+++ b/tests/builder/ignore_errors.rs
@@ -8,7 +8,7 @@ fn single_short_arg_without_value() {
 
     let r = cmd.try_get_matches_from(vec!["cmd", "-c" /* missing: , "config file" */]);
 
-    assert!(r.is_ok(), "unexpected error: {r:?}");
+    assert!(r.is_ok(), "unexpected error: {:?}", r);
     let m = r.unwrap();
     assert!(m.contains_id("config"));
 }
@@ -42,7 +42,7 @@ fn multiple_args_and_final_arg_without_value() {
         "cmd", "-c", "file", "-f", "-x", /* missing: , "some stuff" */
     ]);
 
-    assert!(r.is_ok(), "unexpected error: {r:?}");
+    assert!(r.is_ok(), "unexpected error: {:?}", r);
     let m = r.unwrap();
     assert_eq!(
         m.get_one::<String>("config").map(|v| v.as_str()),
@@ -69,7 +69,7 @@ fn multiple_args_and_intermittent_arg_without_value() {
         "-c", "file", "-f",
     ]);
 
-    assert!(r.is_ok(), "unexpected error: {r:?}");
+    assert!(r.is_ok(), "unexpected error: {:?}", r);
     let m = r.unwrap();
     assert_eq!(
         m.get_one::<String>("config").map(|v| v.as_str()),
diff --git a/tests/builder/opts.rs b/tests/builder/opts.rs
index 25a9d271..7e61b763 100644
--- a/tests/builder/opts.rs
+++ b/tests/builder/opts.rs
@@ -64,7 +64,7 @@ fn double_hyphen_as_value() {
                 .long("config"),
         )
         .try_get_matches_from(vec!["prog", "--config", "--"]);
-    assert!(res.is_ok(), "{res:?}");
+    assert!(res.is_ok(), "{:?}", res);
     assert_eq!(
         res.unwrap().get_one::<String>("cfg").map(|v| v.as_str()),
         Some("--")
@@ -428,7 +428,7 @@ fn leading_hyphen_with_only_pos_follows() {
         )
         .arg(arg!([arg] "some arg"))
         .try_get_matches_from(vec!["", "-o", "-2", "--", "val"]);
-    assert!(r.is_ok(), "{r:?}");
+    assert!(r.is_ok(), "{:?}", r);
     let m = r.unwrap();
     assert!(m.contains_id("o"));
     assert_eq!(
diff --git a/tests/builder/positionals.rs b/tests/builder/positionals.rs
index 49e17f06..f15b2edc 100644
--- a/tests/builder/positionals.rs
+++ b/tests/builder/positionals.rs
@@ -27,7 +27,7 @@ fn issue_946() {
                 .help("filters to apply to output"),
         )
         .try_get_matches_from(vec!["compiletest", "--exact"]);
-    assert!(r.is_ok(), "{r:#?}");
+    assert!(r.is_ok(), "{:#?}", r);
     let matches = r.unwrap();
 
     assert!(*matches.get_one::<bool>("exact").expect("defaulted by clap"));
@@ -45,7 +45,7 @@ fn positional() {
             Arg::new("positional").index(1),
         ])
         .try_get_matches_from(vec!["", "-f", "test"]);
-    assert!(r.is_ok(), "{r:#?}");
+    assert!(r.is_ok(), "{:#?}", r);
     let m = r.unwrap();
     assert!(m.contains_id("positional"));
     assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
@@ -124,7 +124,7 @@ fn positional_multiple() {
                 .num_args(1..),
         ])
         .try_get_matches_from(vec!["", "-f", "test1", "test2", "test3"]);
-    assert!(r.is_ok(), "{r:#?}");
+    assert!(r.is_ok(), "{:#?}", r);
     let m = r.unwrap();
     assert!(m.contains_id("positional"));
     assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
@@ -148,7 +148,7 @@ fn positional_multiple_3() {
                 .num_args(1..),
         ])
         .try_get_matches_from(vec!["", "test1", "test2", "test3", "--flag"]);
-    assert!(r.is_ok(), "{r:#?}");
+    assert!(r.is_ok(), "{:#?}", r);
     let m = r.unwrap();
     assert!(m.contains_id("positional"));
     assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
diff --git a/tests/builder/tests.rs b/tests/builder/tests.rs
index bd7f0182..a7148574 100644
--- a/tests/builder/tests.rs
+++ b/tests/builder/tests.rs
@@ -96,17 +96,17 @@ pub fn check_complex_output(args: &str, out: &str) {
             writeln!(w, "flag NOT present").unwrap();
         }
         n => {
-            writeln!(w, "flag present {n} times").unwrap();
+            writeln!(w, "flag present {} times", n).unwrap();
         }
     }
 
     if matches.contains_id("option") {
         if let Some(v) = matches.get_one::<String>("option").map(|v| v.as_str()) {
-            writeln!(w, "option present with value: {v}").unwrap();
+            writeln!(w, "option present with value: {}", v).unwrap();
         }
         if let Some(ov) = matches.get_many::<String>("option") {
             for o in ov {
-                writeln!(w, "An option: {o}").unwrap();
+                writeln!(w, "An option: {}", o).unwrap();
             }
         }
     } else {
@@ -114,7 +114,7 @@ pub fn check_complex_output(args: &str, out: &str) {
     }
 
     if let Some(p) = matches.get_one::<String>("positional").map(|v| v.as_str()) {
-        writeln!(w, "positional present with value: {p}").unwrap();
+        writeln!(w, "positional present with value: {}", p).unwrap();
     } else {
         writeln!(w, "positional NOT present").unwrap();
     }
@@ -183,11 +183,11 @@ pub fn check_complex_output(args: &str, out: &str) {
 
     if matches.contains_id("option") {
         if let Some(v) = matches.get_one::<String>("option").map(|v| v.as_str()) {
-            writeln!(w, "option present with value: {v}").unwrap();
+            writeln!(w, "option present with value: {}", v).unwrap();
         }
         if let Some(ov) = matches.get_many::<String>("option") {
             for o in ov {
-                writeln!(w, "An option: {o}").unwrap();
+                writeln!(w, "An option: {}", o).unwrap();
             }
         }
     } else {