Repository: clap-rs/clap
Pull request #3746 (2022-05-24 19:56:01)

fix(parser): Increase compatibility between old/new approach
As I've been deprecating the pre-#3746 way of doing things and switching tests over, they've identified these problems.


Patch:
diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs
index 03e87b2b..da7537ec 100644
--- a/clap_derive/src/attrs.rs
+++ b/clap_derive/src/attrs.rs
@@ -805,7 +805,12 @@ impl ValueParser {
             Self::Explicit(method) => method,
             Self::Implicit(ident) => {
                 let func = Ident::new("value_parser", ident.span());
-                Method::new(func, quote!(clap::value_parser!(#inner_type)))
+                Method::new(
+                    func,
+                    quote_spanned! { ident.span()=>
+                        clap::value_parser!(#inner_type)
+                    },
+                )
             }
         }
     }
@@ -955,14 +960,23 @@ impl Parser {
     fn value_parser(&self) -> Method {
         let func = Ident::new("value_parser", self.kind.span());
         match *self.kind {
-            ParserKind::FromStr | ParserKind::TryFromStr => {
-                Method::new(func, quote!(clap::builder::ValueParser::string()))
-            }
-            ParserKind::FromOsStr | ParserKind::TryFromOsStr => {
-                Method::new(func, quote!(clap::builder::ValueParser::os_string()))
-            }
-            ParserKind::FromOccurrences => Method::new(func, quote!(clap::value_parser!(usize))),
-            ParserKind::FromFlag => Method::new(func, quote!(clap::ValueParser::bool())),
+            ParserKind::FromStr | ParserKind::TryFromStr => Method::new(
+                func,
+                quote_spanned! { self.kind.span()=>
+                clap::builder::ValueParser::string()},
+            ),
+            ParserKind::FromOsStr | ParserKind::TryFromOsStr => Method::new(
+                func,
+                quote_spanned! { self.kind.span()=> clap::builder::ValueParser::os_string()},
+            ),
+            ParserKind::FromOccurrences => Method::new(
+                func,
+                quote_spanned! { self.kind.span()=> clap::value_parser!(usize)},
+            ),
+            ParserKind::FromFlag => Method::new(
+                func,
+                quote_spanned! { self.kind.span()=> clap::ValueParser::bool()},
+            ),
         }
     }
 }
diff --git a/examples/cargo-example.md b/examples/cargo-example.md
index 9279cc49..bf7de9ea 100644
--- a/examples/cargo-example.md
+++ b/examples/cargo-example.md
@@ -37,9 +37,9 @@ OPTIONS:
 Then to directly invoke the command, run:
 ```console
 $ cargo-example example
-None
+Ok(None)
 
 $ cargo-example example --manifest-path Cargo.toml
-Some("Cargo.toml")
+Ok(Some("Cargo.toml"))
 
 ```
diff --git a/examples/cargo-example.rs b/examples/cargo-example.rs
index 149bf599..45ae67c8 100644
--- a/examples/cargo-example.rs
+++ b/examples/cargo-example.rs
@@ -8,7 +8,7 @@ fn main() {
             clap::command!("example").arg(
                 clap::arg!(--"manifest-path" <PATH>)
                     .required(false)
-                    .allow_invalid_utf8(true),
+                    .value_parser(clap::value_parser!(std::path::PathBuf)),
             ),
         );
     let matches = cmd.get_matches();
@@ -16,8 +16,6 @@ fn main() {
         Some(("example", matches)) => matches,
         _ => unreachable!("clap should ensure we don't get here"),
     };
-    let manifest_path = matches
-        .value_of_os("manifest-path")
-        .map(std::path::PathBuf::from);
+    let manifest_path = matches.get_one::<std::path::PathBuf>("manifest-path");
     println!("{:?}", manifest_path);
 }
diff --git a/examples/derive_ref/README.md b/examples/derive_ref/README.md
index 2cde95ff..b3e16690 100644
--- a/examples/derive_ref/README.md
+++ b/examples/derive_ref/README.md
@@ -274,16 +274,16 @@ Notes:
   - Implies `arg.takes_value(true).allow_invalid_utf8(true)`
 - `from_occurrences`:
   - Implies `arg.takes_value(false).multiple_occurrences(true)`
-  - Reads from `clap::ArgMatches::occurrences_of` rather than a `value_of` function
+  - Reads from `clap::ArgMatches::occurrences_of` rather than a `get_one` function
     - Note: operations on values, like `default_value`, are unlikely to do what you want
 - `from_flag`
   - Implies `arg.takes_value(false)`
-  - Reads from `clap::ArgMatches::is_present` rather than a `value_of` function
+  - Reads from `clap::ArgMatches::is_present` rather than a `get_one` function
     - Note: operations on values, like `default_value`, are unlikely to do what you want
 
 **Warning:**
-- To support non-UTF8 paths, you must use `parse(from_os_str)`, otherwise
-  `clap` will use `clap::ArgMatches::value_of` with `PathBuf::FromStr`.
+- To support non-UTF8 paths, you should use `#[clap(value_parser)]` otherwise
+  `clap` will parse it as a `String` which will fail on some paths.
 
 ## Doc Comments
 
diff --git a/examples/derive_ref/flatten_hand_args.rs b/examples/derive_ref/flatten_hand_args.rs
index 2969457e..260d0964 100644
--- a/examples/derive_ref/flatten_hand_args.rs
+++ b/examples/derive_ref/flatten_hand_args.rs
@@ -10,17 +10,31 @@ struct CliArgs {
 
 impl FromArgMatches for CliArgs {
     fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> {
+        let mut matches = matches.clone();
+        Self::from_arg_matches_mut(&mut matches)
+    }
+    fn from_arg_matches_mut(matches: &mut ArgMatches) -> Result<Self, Error> {
         Ok(Self {
             foo: matches.is_present("foo"),
             bar: matches.is_present("bar"),
-            quuz: matches.value_of("quuz").map(|quuz| quuz.to_owned()),
+            quuz: matches
+                .remove_one::<String>("quuz")
+                .expect("matches definition")
+                .map(|quuz| std::sync::Arc::try_unwrap(quuz).unwrap_or_else(|arc| (*arc).clone())),
         })
     }
     fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> {
+        let mut matches = matches.clone();
+        self.update_from_arg_matches_mut(&mut matches)
+    }
+    fn update_from_arg_matches_mut(&mut self, matches: &mut ArgMatches) -> Result<(), Error> {
         self.foo |= matches.is_present("foo");
         self.bar |= matches.is_present("bar");
-        if let Some(quuz) = matches.value_of("quuz") {
-            self.quuz = Some(quuz.to_owned());
+        if let Some(quuz) = matches
+            .remove_one::<String>("quuz")
+            .expect("matches definition")
+        {
+            self.quuz = Some(std::sync::Arc::try_unwrap(quuz).unwrap_or_else(|arc| (*arc).clone()));
         }
         Ok(())
     }
diff --git a/examples/escaped-positional.rs b/examples/escaped-positional.rs
index f6d61bd9..dc0a013d 100644
--- a/examples/escaped-positional.rs
+++ b/examples/escaped-positional.rs
@@ -1,26 +1,44 @@
 // Note: this requires the `cargo` feature
 
-use clap::{arg, command};
+use clap::{arg, command, value_parser};
 
 fn main() {
     let matches = command!()
         .arg(arg!(eff: -f))
-        .arg(arg!(pea: -p <PEAR>).required(false))
         .arg(
-            arg!(slop: [SLOP]).multiple_occurrences(true).last(true), // Indicates that `slop` is only accessible after `--`.
+            arg!(pea: -p <PEAR>)
+                .required(false)
+                .value_parser(value_parser!(String)),
+        )
+        .arg(
+            // Indicates that `slop` is only accessible after `--`.
+            arg!(slop: [SLOP])
+                .multiple_occurrences(true)
+                .last(true)
+                .value_parser(value_parser!(String)),
         )
         .get_matches();
 
     // This is what will happen with `myprog -f -p=bob -- sloppy slop slop`...
-    println!("-f used: {:?}", matches.is_present("eff")); // -f used: true
-    println!("-p's value: {:?}", matches.value_of("pea")); // -p's value: Some("bob")
+
+    // -f used: true
+    println!("-f used: {:?}", matches.is_present("eff"));
+    // -p's value: Some("bob")
+    println!(
+        "-p's value: {:?}",
+        matches
+            .get_one::<String>("pea")
+            .expect("matches definition")
+    );
+    // 'slops' values: Some(["sloppy", "slop", "slop"])
     println!(
         "'slops' values: {:?}",
         matches
-            .values_of("slop")
+            .get_many::<String>("slop")
+            .expect("matches definition")
             .map(|vals| vals.collect::<Vec<_>>())
             .unwrap_or_default()
-    ); // 'slops' values: Some(["sloppy", "slop", "slop"])
+    );
 
     // Continued program logic goes here...
 }
diff --git a/examples/git.rs b/examples/git.rs
index 1ced54a9..d410ee43 100644
--- a/examples/git.rs
+++ b/examples/git.rs
@@ -1,5 +1,6 @@
 // Note: this requires the `cargo` feature
 
+use std::ffi::OsString;
 use std::path::PathBuf;
 
 use clap::{arg, Command};
@@ -27,7 +28,7 @@ fn cli() -> Command<'static> {
             Command::new("add")
                 .about("adds things")
                 .arg_required_else_help(true)
-                .arg(arg!(<PATH> ... "Stuff to add").allow_invalid_utf8(true)),
+                .arg(arg!(<PATH> ... "Stuff to add").value_parser(clap::value_parser!(PathBuf))),
         )
         .subcommand(
             Command::new("stash")
@@ -50,20 +51,27 @@ fn main() {
         Some(("clone", sub_matches)) => {
             println!(
                 "Cloning {}",
-                sub_matches.value_of("REMOTE").expect("required")
+                sub_matches
+                    .get_one::<String>("REMOTE")
+                    .expect("matches definition")
+                    .expect("required")
             );
         }
         Some(("push", sub_matches)) => {
             println!(
                 "Pushing to {}",
-                sub_matches.value_of("REMOTE").expect("required")
+                sub_matches
+                    .get_one::<String>("REMOTE")
+                    .expect("matches definition")
+                    .expect("required")
             );
         }
         Some(("add", sub_matches)) => {
             let paths = sub_matches
-                .values_of_os("PATH")
-                .unwrap_or_default()
-                .map(PathBuf::from)
+                .get_many::<PathBuf>("PATH")
+                .expect("matches definition")
+                .into_iter()
+                .flatten()
                 .collect::<Vec<_>>();
             println!("Adding {:?}", paths);
         }
@@ -71,15 +79,21 @@ fn main() {
             let stash_command = sub_matches.subcommand().unwrap_or(("push", sub_matches));
             match stash_command {
                 ("apply", sub_matches) => {
-                    let stash = sub_matches.value_of("STASH");
+                    let stash = sub_matches
+                        .get_one::<String>("STASH")
+                        .expect("matches definition");
                     println!("Applying {:?}", stash);
                 }
                 ("pop", sub_matches) => {
-                    let stash = sub_matches.value_of("STASH");
+                    let stash = sub_matches
+                        .get_one::<String>("STASH")
+                        .expect("matches definition");
                     println!("Popping {:?}", stash);
                 }
                 ("push", sub_matches) => {
-                    let message = sub_matches.value_of("message");
+                    let message = sub_matches
+                        .get_one::<String>("message")
+                        .expect("matches definition");
                     println!("Pushing {:?}", message);
                 }
                 (name, _) => {
@@ -89,8 +103,10 @@ fn main() {
         }
         Some((ext, sub_matches)) => {
             let args = sub_matches
-                .values_of_os("")
-                .unwrap_or_default()
+                .get_many::<OsString>("")
+                .expect("matches definition")
+                .into_iter()
+                .flatten()
                 .collect::<Vec<_>>();
             println!("Calling out to {:?} with {:?}", ext, args);
         }
diff --git a/examples/multicall-busybox.rs b/examples/multicall-busybox.rs
index f9d67867..ec0d7600 100644
--- a/examples/multicall-busybox.rs
+++ b/examples/multicall-busybox.rs
@@ -1,6 +1,7 @@
+use std::path::PathBuf;
 use std::process::exit;
 
-use clap::{Arg, Command};
+use clap::{value_parser, Arg, Command};
 
 fn applet_commands() -> [Command<'static>; 2] {
     [
@@ -24,6 +25,7 @@ fn main() {
                         .exclusive(true)
                         .takes_value(true)
                         .default_missing_value("/usr/local/bin")
+                        .value_parser(value_parser!(PathBuf))
                         .use_value_delimiter(false),
                 )
                 .subcommands(applet_commands()),
diff --git a/examples/pacman.rs b/examples/pacman.rs
index c088a8fe..974f1cb1 100644
--- a/examples/pacman.rs
+++ b/examples/pacman.rs
@@ -71,13 +71,23 @@ fn main() {
     match matches.subcommand() {
         Some(("sync", sync_matches)) => {
             if sync_matches.is_present("search") {
-                let packages: Vec<_> = sync_matches.values_of("search").unwrap().collect();
+                let packages: Vec<_> = sync_matches
+                    .get_many::<String>("search")
+                    .expect("matches definition")
+                    .expect("is present")
+                    .map(|s| s.as_str())
+                    .collect();
                 let values = packages.join(", ");
                 println!("Searching for {}...", values);
                 return;
             }
 
-            let packages: Vec<_> = sync_matches.values_of("package").unwrap().collect();
+            let packages: Vec<_> = sync_matches
+                .get_many::<String>("package")
+                .expect("matches definition")
+                .expect("is present")
+                .map(|s| s.as_str())
+                .collect();
             let values = packages.join(", ");
 
             if sync_matches.is_present("info") {
@@ -87,11 +97,17 @@ fn main() {
             }
         }
         Some(("query", query_matches)) => {
-            if let Some(packages) = query_matches.values_of("info") {
-                let comma_sep = packages.collect::<Vec<_>>().join(", ");
+            if let Some(packages) = query_matches
+                .get_many::<String>("info")
+                .expect("matches definition")
+            {
+                let comma_sep = packages.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
                 println!("Retrieving info for {}...", comma_sep);
-            } else if let Some(queries) = query_matches.values_of("search") {
-                let comma_sep = queries.collect::<Vec<_>>().join(", ");
+            } else if let Some(queries) = query_matches
+                .get_many::<String>("search")
+                .expect("matches definition")
+            {
+                let comma_sep = queries.map(|s| s.as_str()).collect::<Vec<_>>().join(", ");
                 println!("Searching Locally for {}...", comma_sep);
             } else {
                 println!("Displaying all locally installed packages...");
diff --git a/examples/tutorial_builder/01_quick.rs b/examples/tutorial_builder/01_quick.rs
index 7ab5081d..45ae9ec2 100644
--- a/examples/tutorial_builder/01_quick.rs
+++ b/examples/tutorial_builder/01_quick.rs
@@ -1,7 +1,8 @@
 // Note: this requires the `cargo` feature
 
-use clap::{arg, command, Command};
-use std::path::Path;
+use std::path::PathBuf;
+
+use clap::{arg, command, value_parser, Command};
 
 fn main() {
     let matches = command!()
@@ -12,8 +13,7 @@ fn main() {
             )
             // We don't have syntax yet for optional options, so manually calling `required`
             .required(false)
-            // Support non-UTF8 paths
-            .allow_invalid_utf8(true),
+            .value_parser(value_parser!(PathBuf)),
         )
         .arg(arg!(
             -d --debug ... "Turn debugging information on"
@@ -26,12 +26,17 @@ fn main() {
         .get_matches();
 
     // You can check the value provided by positional arguments, or option arguments
-    if let Some(name) = matches.value_of("name") {
+    if let Some(name) = matches
+        .get_one::<String>("name")
+        .expect("matches definition")
+    {
         println!("Value for name: {}", name);
     }
 
-    if let Some(raw_config) = matches.value_of_os("config") {
-        let config_path = Path::new(raw_config);
+    if let Some(config_path) = matches
+        .get_one::<PathBuf>("config")
+        .expect("matches definition")
+    {
         println!("Value for config: {}", config_path.display());
     }
 
diff --git a/examples/tutorial_builder/02_app_settings.rs b/examples/tutorial_builder/02_app_settings.rs
index 3485f588..41cbeb74 100644
--- a/examples/tutorial_builder/02_app_settings.rs
+++ b/examples/tutorial_builder/02_app_settings.rs
@@ -11,6 +11,18 @@ fn main() {
         .arg(arg!(--one <VALUE>))
         .get_matches();
 
-    println!("two: {:?}", matches.value_of("two").expect("required"));
-    println!("one: {:?}", matches.value_of("one").expect("required"));
+    println!(
+        "two: {:?}",
+        matches
+            .get_one::<String>("two")
+            .expect("matches definition")
+            .expect("required")
+    );
+    println!(
+        "one: {:?}",
+        matches
+            .get_one::<String>("one")
+            .expect("matches definition")
+            .expect("required")
+    );
 }
diff --git a/examples/tutorial_builder/02_apps.rs b/examples/tutorial_builder/02_apps.rs
index c9422d64..da9baf58 100644
--- a/examples/tutorial_builder/02_apps.rs
+++ b/examples/tutorial_builder/02_apps.rs
@@ -9,6 +9,18 @@ fn main() {
         .arg(arg!(--one <VALUE>))
         .get_matches();
 
-    println!("two: {:?}", matches.value_of("two").expect("required"));
-    println!("one: {:?}", matches.value_of("one").expect("required"));
+    println!(
+        "two: {:?}",
+        matches
+            .get_one::<String>("two")
+            .expect("matches definition")
+            .expect("required")
+    );
+    println!(
+        "one: {:?}",
+        matches
+            .get_one::<String>("one")
+            .expect("matches definition")
+            .expect("required")
+    );
 }
diff --git a/examples/tutorial_builder/02_crate.rs b/examples/tutorial_builder/02_crate.rs
index df214f3f..6b8ecaae 100644
--- a/examples/tutorial_builder/02_crate.rs
+++ b/examples/tutorial_builder/02_crate.rs
@@ -8,6 +8,18 @@ fn main() {
         .arg(arg!(--one <VALUE>))
         .get_matches();
 
-    println!("two: {:?}", matches.value_of("two").expect("required"));
-    println!("one: {:?}", matches.value_of("one").expect("required"));
+    println!(
+        "two: {:?}",
+        matches
+            .get_one::<String>("two")
+            .expect("matches definition")
+            .expect("required")
+    );
+    println!(
+        "one: {:?}",
+        matches
+            .get_one::<String>("one")
+            .expect("matches definition")
+            .expect("required")
+    );
 }
diff --git a/examples/tutorial_builder/03_02_option.rs b/examples/tutorial_builder/03_02_option.rs
index 29ad60a9..bd8f351a 100644
--- a/examples/tutorial_builder/03_02_option.rs
+++ b/examples/tutorial_builder/03_02_option.rs
@@ -7,5 +7,10 @@ fn main() {
         .arg(arg!(-n --name <NAME>).required(false))
         .get_matches();
 
-    println!("name: {:?}", matches.value_of("name"));
+    println!(
+        "name: {:?}",
+        matches
+            .get_one::<String>("name")
+            .expect("matches definition")
+    );
 }
diff --git a/examples/tutorial_builder/03_03_positional.rs b/examples/tutorial_builder/03_03_positional.rs
index 03bfab8e..5024319e 100644
--- a/examples/tutorial_builder/03_03_positional.rs
+++ b/examples/tutorial_builder/03_03_positional.rs
@@ -5,5 +5,10 @@ use clap::{arg, command};
 fn main() {
     let matches = command!().arg(arg!([NAME])).get_matches();
 
-    println!("NAME: {:?}", matches.value_of("NAME"));
+    println!(
+        "NAME: {:?}",
+        matches
+            .get_one::<String>("NAME")
+            .expect("matches definition")
+    );
 }
diff --git a/examples/tutorial_builder/03_04_subcommands.rs b/examples/tutorial_builder/03_04_subcommands.rs
index 1ab79e71..95a51212 100644
--- a/examples/tutorial_builder/03_04_subcommands.rs
+++ b/examples/tutorial_builder/03_04_subcommands.rs
@@ -17,7 +17,9 @@ fn main() {
     match matches.subcommand() {
         Some(("add", sub_matches)) => println!(
             "'myapp add' was used, name is: {:?}",
-            sub_matches.value_of("NAME")
+            sub_matches
+                .get_one::<String>("NAME")
+                .expect("matches definition")
         ),
         _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"),
     }
diff --git a/examples/tutorial_builder/03_05_default_values.rs b/examples/tutorial_builder/03_05_default_values.rs
index f88e3953..a49b01d6 100644
--- a/examples/tutorial_builder/03_05_default_values.rs
+++ b/examples/tutorial_builder/03_05_default_values.rs
@@ -10,7 +10,8 @@ fn main() {
     println!(
         "NAME: {:?}",
         matches
-            .value_of("NAME")
+            .get_one::<String>("NAME")
+            .expect("matches definition")
             .expect("default ensures there is always a value")
     );
 }
diff --git a/examples/tutorial_builder/04_01_enum.rs b/examples/tutorial_builder/04_01_enum.rs
index 128b3cdc..a788e326 100644
--- a/examples/tutorial_builder/04_01_enum.rs
+++ b/examples/tutorial_builder/04_01_enum.rs
@@ -1,6 +1,6 @@
 // Note: this requires the `cargo` feature
 
-use clap::{arg, command, ArgEnum, PossibleValue};
+use clap::{arg, command, value_parser, ArgEnum};
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)]
 enum Mode {
@@ -8,48 +8,19 @@ enum Mode {
     Slow,
 }
 
-impl Mode {
-    pub fn possible_values() -> impl Iterator<Item = PossibleValue<'static>> {
-        Mode::value_variants()
-            .iter()
-            .filter_map(ArgEnum::to_possible_value)
-    }
-}
-
-impl std::fmt::Display for Mode {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.to_possible_value()
-            .expect("no values are skipped")
-            .get_name()
-            .fmt(f)
-    }
-}
-
-impl std::str::FromStr for Mode {
-    type Err = String;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        for variant in Self::value_variants() {
-            if variant.to_possible_value().unwrap().matches(s, false) {
-                return Ok(*variant);
-            }
-        }
-        Err(format!("Invalid variant: {}", s))
-    }
-}
-
 fn main() {
     let matches = command!()
         .arg(
             arg!(<MODE>)
                 .help("What mode to run the program in")
-                .possible_values(Mode::possible_values()),
+                .value_parser(value_parser!(Mode)),
         )
         .get_matches();
 
     // Note, it's safe to call unwrap() because the arg is required
     match matches
-        .value_of_t("MODE")
+        .get_one::<Mode>("MODE")
+        .expect("matches definition")
         .expect("'MODE' is required and parsing will fail if its missing")
     {
         Mode::Fast => {
diff --git a/examples/tutorial_builder/04_01_possible.rs b/examples/tutorial_builder/04_01_possible.rs
index 77160392..889bc78e 100644
--- a/examples/tutorial_builder/04_01_possible.rs
+++ b/examples/tutorial_builder/04_01_possible.rs
@@ -7,14 +7,16 @@ fn main() {
         .arg(
             arg!(<MODE>)
                 .help("What mode to run the program in")
-                .possible_values(["fast", "slow"]),
+                .value_parser(["fast", "slow"]),
         )
         .get_matches();
 
     // Note, it's safe to call unwrap() because the arg is required
     match matches
-        .value_of("MODE")
+        .get_one::<String>("MODE")
+        .expect("matches definition")
         .expect("'MODE' is required and parsing will fail if its missing")
+        .as_str()
     {
         "fast" => {
             println!("Hare");
diff --git a/examples/tutorial_builder/04_02_parse.rs b/examples/tutorial_builder/04_02_parse.rs
index e354af3a..3cca7924 100644
--- a/examples/tutorial_builder/04_02_parse.rs
+++ b/examples/tutorial_builder/04_02_parse.rs
@@ -1,19 +1,20 @@
 // Note: this requires the `cargo` feature
 
-use clap::{arg, command};
+use clap::{arg, command, value_parser};
 
 fn main() {
     let matches = command!()
         .arg(
             arg!(<PORT>)
                 .help("Network port to use")
-                .validator(|s| s.parse::<usize>()),
+                .value_parser(value_parser!(u16).range(1..)),
         )
         .get_matches();
 
     // Note, it's safe to call unwrap() because the arg is required
-    let port: usize = matches
-        .value_of_t("PORT")
+    let port: u16 = *matches
+        .get_one::<u16>("PORT")
+        .expect("matches definition")
         .expect("'PORT' is required and parsing will fail if its missing");
     println!("PORT = {}", port);
 }
diff --git a/examples/tutorial_builder/04_02_validate.rs b/examples/tutorial_builder/04_02_validate.rs
index a49f869e..df53f285 100644
--- a/examples/tutorial_builder/04_02_validate.rs
+++ b/examples/tutorial_builder/04_02_validate.rs
@@ -9,25 +9,26 @@ fn main() {
         .arg(
             arg!(<PORT>)
                 .help("Network port to use")
-                .validator(port_in_range),
+                .value_parser(port_in_range),
         )
         .get_matches();
 
     // Note, it's safe to call unwrap() because the arg is required
-    let port: usize = matches
-        .value_of_t("PORT")
+    let port: u16 = *matches
+        .get_one::<u16>("PORT")
+        .expect("matches definition")
         .expect("'PORT' is required and parsing will fail if its missing");
     println!("PORT = {}", port);
 }
 
 const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
 
-fn port_in_range(s: &str) -> Result<(), String> {
+fn port_in_range(s: &str) -> Result<u16, String> {
     let port: usize = s
         .parse()
         .map_err(|_| format!("`{}` isn't a port number", s))?;
     if PORT_RANGE.contains(&port) {
-        Ok(())
+        Ok(port as u16)
     } else {
         Err(format!(
             "Port not in range {}-{}",
diff --git a/examples/tutorial_builder/04_03_relations.rs b/examples/tutorial_builder/04_03_relations.rs
index 495bf365..2f9108eb 100644
--- a/examples/tutorial_builder/04_03_relations.rs
+++ b/examples/tutorial_builder/04_03_relations.rs
@@ -1,6 +1,8 @@
 // Note: this requires the `cargo` feature
 
-use clap::{arg, command, ArgGroup};
+use std::path::PathBuf;
+
+use clap::{arg, command, value_parser, ArgGroup};
 
 fn main() {
     // Create application like normal
@@ -18,15 +20,25 @@ fn main() {
         )
         // Arguments can also be added to a group individually, these two arguments
         // are part of the "input" group which is not required
-        .arg(arg!([INPUT_FILE] "some regular input").group("input"))
+        .arg(
+            arg!([INPUT_FILE] "some regular input")
+                .value_parser(value_parser!(PathBuf))
+                .group("input"),
+        )
         .arg(
             arg!(--"spec-in" <SPEC_IN> "some special input argument")
                 .required(false)
+                .value_parser(value_parser!(PathBuf))
                 .group("input"),
         )
         // Now let's assume we have a -c [config] argument which requires one of
         // (but **not** both) the "input" arguments
-        .arg(arg!(config: -c <CONFIG>).required(false).requires("input"))
+        .arg(
+            arg!(config: -c <CONFIG>)
+                .required(false)
+                .value_parser(value_parser!(PathBuf))
+                .requires("input"),
+        )
         .get_matches();
 
     // Let's assume the old version 1.2.3
@@ -35,8 +47,11 @@ fn main() {
     let mut patch = 3;
 
     // See if --set-ver was used to set the version manually
-    let version = if let Some(ver) = matches.value_of("set-ver") {
-        ver.to_string()
+    let version = if let Some(ver) = matches
+        .get_one::<String>("set-ver")
+        .expect("matches definition")
+    {
+        ver.to_owned()
     } else {
         // Increment the one requested (in a real program, we'd reset the lower numbers)
         let (maj, min, pat) = (
@@ -58,12 +73,23 @@ fn main() {
     // Check for usage of -c
     if matches.is_present("config") {
         let input = matches
-            .value_of("INPUT_FILE")
-            .unwrap_or_else(|| matches.value_of("spec-in").unwrap());
+            .get_one::<PathBuf>("INPUT_FILE")
+            .expect("matches definition")
+            .unwrap_or_else(|| {
+                matches
+                    .get_one::<PathBuf>("spec-in")
+                    .expect("matches definition")
+                    .unwrap()
+            })
+            .display();
         println!(
             "Doing work using input {} and config {}",
             input,
-            matches.value_of("config").unwrap()
+            matches
+                .get_one::<PathBuf>("config")
+                .expect("matches definition")
+                .unwrap()
+                .display()
         );
     }
 }
diff --git a/examples/tutorial_builder/04_04_custom.rs b/examples/tutorial_builder/04_04_custom.rs
index 6993329d..4767ae68 100644
--- a/examples/tutorial_builder/04_04_custom.rs
+++ b/examples/tutorial_builder/04_04_custom.rs
@@ -1,6 +1,8 @@
 // Note: this requires the `cargo` feature
 
-use clap::{arg, command, ErrorKind};
+use std::path::PathBuf;
+
+use clap::{arg, command, value_parser, ErrorKind};
 
 fn main() {
     // Create application like normal
@@ -12,11 +14,19 @@ fn main() {
         .arg(arg!(--patch         "auto inc patch"))
         // Arguments can also be added to a group individually, these two arguments
         // are part of the "input" group which is not required
-        .arg(arg!([INPUT_FILE] "some regular input"))
-        .arg(arg!(--"spec-in" <SPEC_IN> "some special input argument").required(false))
+        .arg(arg!([INPUT_FILE] "some regular input").value_parser(value_parser!(PathBuf)))
+        .arg(
+            arg!(--"spec-in" <SPEC_IN> "some special input argument")
+                .required(false)
+                .value_parser(value_parser!(PathBuf)),
+        )
         // Now let's assume we have a -c [config] argument which requires one of
         // (but **not** both) the "input" arguments
-        .arg(arg!(config: -c <CONFIG>).required(false));
+        .arg(
+            arg!(config: -c <CONFIG>)
+                .required(false)
+                .value_parser(value_parser!(PathBuf)),
+        );
     let matches = cmd.get_matches_mut();
 
     // Let's assume the old version 1.2.3
@@ -25,7 +35,10 @@ fn main() {
     let mut patch = 3;
 
     // See if --set-ver was used to set the version manually
-    let version = if let Some(ver) = matches.value_of("set-ver") {
+    let version = if let Some(ver) = matches
+        .get_one::<String>("set-ver")
+        .expect("matches definition")
+    {
         if matches.is_present("major") || matches.is_present("minor") || matches.is_present("patch")
         {
             cmd.error(
@@ -62,19 +75,29 @@ fn main() {
     // Check for usage of -c
     if matches.is_present("config") {
         let input = matches
-            .value_of("INPUT_FILE")
-            .or_else(|| matches.value_of("spec-in"))
+            .get_one::<PathBuf>("INPUT_FILE")
+            .expect("matches definition")
+            .or_else(|| {
+                matches
+                    .get_one::<PathBuf>("spec-in")
+                    .expect("matches definition")
+            })
             .unwrap_or_else(|| {
                 cmd.error(
                     ErrorKind::MissingRequiredArgument,
                     "INPUT_FILE or --spec-in is required when using --config",
                 )
                 .exit()
-            });
+            })
+            .display();
         println!(
             "Doing work using input {} and config {}",
             input,
-            matches.value_of("config").unwrap()
+            matches
+                .get_one::<PathBuf>("config")
+                .expect("matches definition")
+                .unwrap()
+                .display()
         );
     }
 }
diff --git a/examples/tutorial_builder/05_01_assert.rs b/examples/tutorial_builder/05_01_assert.rs
index f40f6ebd..45a982fb 100644
--- a/examples/tutorial_builder/05_01_assert.rs
+++ b/examples/tutorial_builder/05_01_assert.rs
@@ -1,13 +1,14 @@
 // Note: this requires the `cargo` feature
 
-use clap::{arg, command};
+use clap::{arg, command, value_parser};
 
 fn main() {
     let matches = cmd().get_matches();
 
     // Note, it's safe to call unwrap() because the arg is required
-    let port: usize = matches
-        .value_of_t("PORT")
+    let port: usize = *matches
+        .get_one::<usize>("PORT")
+        .expect("matches definition")
         .expect("'PORT' is required and parsing will fail if its missing");
     println!("PORT = {}", port);
 }
@@ -16,7 +17,7 @@ fn cmd() -> clap::Command<'static> {
     command!().arg(
         arg!(<PORT>)
             .help("Network port to use")
-            .validator(|s| s.parse::<usize>()),
+            .value_parser(value_parser!(usize)),
     )
 }
 
diff --git a/examples/tutorial_builder/README.md b/examples/tutorial_builder/README.md
index fcbda4b3..b0012fb1 100644
--- a/examples/tutorial_builder/README.md
+++ b/examples/tutorial_builder/README.md
@@ -405,9 +405,6 @@ $ 04_01_possible medium
 error: "medium" isn't a valid value for '<MODE>'
 	[possible values: fast, slow]
 
-USAGE:
-    04_01_possible[EXE] <MODE>
-
 For more information try --help
 
 ```
@@ -441,16 +438,13 @@ $ 04_01_enum medium
 error: "medium" isn't a valid value for '<MODE>'
 	[possible values: fast, slow]
 
-USAGE:
-    04_01_enum[EXE] <MODE>
-
 For more information try --help
 
 ```
 
 ### Validated values
 
-More generally, you can parse into any data type.
+More generally, you can validate and parse into any data type.
 
 [Example:](04_02_parse.rs)
 ```console
@@ -477,9 +471,15 @@ error: Invalid value "foobar" for '<PORT>': invalid digit found in string
 
 For more information try --help
 
+$ 04_02_parse_derive 0
+? failed
+error: Invalid value "0" for '<PORT>': 0 is not in 1..=65535
+
+For more information try --help
+
 ```
 
-A custom validator can be used to improve the error messages or provide additional validation:
+A custom parser can be used to improve the error messages or provide additional validation:
 
 [Example:](04_02_validate.rs)
 ```console
diff --git a/examples/tutorial_derive/README.md b/examples/tutorial_derive/README.md
index 01b12242..93d35454 100644
--- a/examples/tutorial_derive/README.md
+++ b/examples/tutorial_derive/README.md
@@ -417,9 +417,6 @@ $ 04_01_enum_derive medium
 error: "medium" isn't a valid value for '<MODE>'
 	[possible values: fast, slow]
 
-USAGE:
-    04_01_enum_derive[EXE] <MODE>
-
 For more information try --help
 
 ```
diff --git a/src/builder/arg.rs b/src/builder/arg.rs
index 33e50818..2013dd64 100644
--- a/src/builder/arg.rs
+++ b/src/builder/arg.rs
@@ -4677,6 +4677,22 @@ impl<'help> Arg<'help> {
         }
     }
 
+    pub(crate) fn get_possible_values2(&self) -> Vec<PossibleValue<'help>> {
+        #![allow(deprecated)]
+        if !self.is_takes_value_set() {
+            vec![]
+        } else if let Some(pvs) = self.get_possible_values() {
+            // Check old first in case the user explicitly set possible values and the derive inferred
+            // a `ValueParser` with some.
+            pvs.to_vec()
+        } else {
+            self.get_value_parser()
+                .possible_values()
+                .map(|pvs| pvs.collect())
+                .unwrap_or_default()
+        }
+    }
+
     /// Get the names of values for this argument.
     #[inline]
     pub fn get_value_names(&self) -> Option<&[&'help str]> {
@@ -5300,6 +5316,7 @@ impl<'help> fmt::Debug for Arg<'help> {
             .field("name", &self.name)
             .field("help", &self.help)
             .field("long_help", &self.long_help)
+            .field("value_parser", &self.value_parser)
             .field("blacklist", &self.blacklist)
             .field("settings", &self.settings)
             .field("overrides", &self.overrides)
diff --git a/src/builder/command.rs b/src/builder/command.rs
index 3864b549..dbc3f4c4 100644
--- a/src/builder/command.rs
+++ b/src/builder/command.rs
@@ -4902,7 +4902,9 @@ impl<'help> App<'help> {
                 || v.is_hide_long_help_set()
                 || v.is_hide_short_help_set()
                 || cfg!(feature = "unstable-v4")
-                    && v.possible_vals.iter().any(PossibleValue::should_show_help)
+                    && v.get_possible_values2()
+                        .iter()
+                        .any(PossibleValue::should_show_help)
         };
 
         // Subcommands aren't checked because we prefer short help for them, deferring to
diff --git a/src/builder/value_parser.rs b/src/builder/value_parser.rs
index edf57c62..8105ab67 100644
--- a/src/builder/value_parser.rs
+++ b/src/builder/value_parser.rs
@@ -760,7 +760,6 @@ impl TypedValueParser for PathBufValueParser {
                 &[],
                 arg.map(ToString::to_string)
                     .unwrap_or_else(|| "...".to_owned()),
-                crate::output::Usage::new(cmd).create_usage_with_title(&[]),
             ));
         }
         Ok(Self::Value::from(value))
@@ -869,7 +868,6 @@ impl<E: crate::ArgEnum + Clone + Send + Sync + 'static> TypedValueParser for Arg
                 &possible_vals(),
                 arg.map(ToString::to_string)
                     .unwrap_or_else(|| "...".to_owned()),
-                crate::output::Usage::new(cmd).create_usage_with_title(&[]),
             )
         })?;
         let value = E::value_variants()
@@ -886,7 +884,6 @@ impl<E: crate::ArgEnum + Clone + Send + Sync + 'static> TypedValueParser for Arg
                 &possible_vals(),
                 arg.map(ToString::to_string)
                     .unwrap_or_else(|| "...".to_owned()),
-                crate::output::Usage::new(cmd).create_usage_with_title(&[]),
             )
             })?
             .clone();
@@ -997,7 +994,6 @@ impl TypedValueParser for PossibleValuesParser {
                 &possible_vals,
                 arg.map(ToString::to_string)
                     .unwrap_or_else(|| "...".to_owned()),
-                crate::output::Usage::new(cmd).create_usage_with_title(&[]),
             ))
         }
     }
@@ -1260,7 +1256,6 @@ impl TypedValueParser for BoolValueParser {
                 &possible_vals,
                 arg.map(ToString::to_string)
                     .unwrap_or_else(|| "...".to_owned()),
-                crate::output::Usage::new(cmd).create_usage_with_title(&[]),
             ));
         };
         Ok(value)
@@ -1529,7 +1524,6 @@ impl TypedValueParser for NonEmptyStringValueParser {
                 &[],
                 arg.map(ToString::to_string)
                     .unwrap_or_else(|| "...".to_owned()),
-                crate::output::Usage::new(cmd).create_usage_with_title(&[]),
             ));
         }
         let value = value.to_str().ok_or_else(|| {
diff --git a/src/error/mod.rs b/src/error/mod.rs
index 6cb27eed..d893ab09 100644
--- a/src/error/mod.rs
+++ b/src/error/mod.rs
@@ -288,20 +288,12 @@ impl Error {
             ])
     }
 
-    pub(crate) fn empty_value(
-        cmd: &Command,
-        good_vals: &[&str],
-        arg: String,
-        usage: String,
-    ) -> Self {
+    pub(crate) fn empty_value(cmd: &Command, good_vals: &[&str], arg: String) -> Self {
         let info = vec![arg.clone()];
         let mut err = Self::new(ErrorKind::EmptyValue)
             .with_cmd(cmd)
             .set_info(info)
-            .extend_context_unchecked([
-                (ContextKind::InvalidArg, ContextValue::String(arg)),
-                (ContextKind::Usage, ContextValue::String(usage)),
-            ]);
+            .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
         if !good_vals.is_empty() {
             err = err.insert_context_unchecked(
                 ContextKind::ValidValue,
@@ -327,7 +319,6 @@ impl Error {
         bad_val: String,
         good_vals: &[&str],
         arg: String,
-        usage: String,
     ) -> Self {
         let mut info = vec![arg.clone(), bad_val.clone()];
         info.extend(good_vals.iter().map(|s| (*s).to_owned()));
@@ -343,7 +334,6 @@ impl Error {
                     ContextKind::ValidValue,
                     ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()),
                 ),
-                (ContextKind::Usage, ContextValue::String(usage)),
             ]);
         if let Some(suggestion) = suggestion {
             err = err.insert_context_unchecked(
diff --git a/src/output/help.rs b/src/output/help.rs
index e1ee78ac..04e0a7a2 100644
--- a/src/output/help.rs
+++ b/src/output/help.rs
@@ -65,7 +65,7 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
         usage: &'cmd Usage<'help, 'cmd>,
         use_long: bool,
     ) -> Self {
-        debug!("Help::new");
+        debug!("Help::new cmd={}, use_long={}", cmd.get_name(), use_long);
         let term_w = match cmd.get_term_width() {
             Some(0) => usize::MAX,
             Some(w) => w,
@@ -456,7 +456,7 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
         if let Some(arg) = arg {
             const DASH_SPACE: usize = "- ".len();
             const COLON_SPACE: usize = ": ".len();
-            let possible_vals = get_possible_values(arg);
+            let possible_vals = arg.get_possible_values2();
             if self.use_long
                 && !arg.is_hide_possible_values_set()
                 && possible_vals.iter().any(PossibleValue::should_show_help)
@@ -658,7 +658,7 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
             }
         }
 
-        let possible_vals = get_possible_values(a);
+        let possible_vals = a.get_possible_values2();
         if !(a.is_hide_possible_values_set()
             || possible_vals.is_empty()
             || cfg!(feature = "unstable-v4")
@@ -1141,21 +1141,6 @@ fn text_wrapper(help: &str, width: usize) -> String {
         .join("\n")
 }
 
-fn get_possible_values<'help>(a: &Arg<'help>) -> Vec<PossibleValue<'help>> {
-    if !a.is_takes_value_set() {
-        vec![]
-    } else if let Some(pvs) = a.get_possible_values() {
-        // Check old first in case the user explicitly set possible values and the derive inferred
-        // a `ValueParser` with some.
-        pvs.to_vec()
-    } else {
-        a.get_value_parser()
-            .possible_values()
-            .map(|pvs| pvs.collect())
-            .unwrap_or_default()
-    }
-}
-
 #[cfg(test)]
 mod test {
     use super::*;
diff --git a/src/parser/validator.rs b/src/parser/validator.rs
index ddcd6bb1..f98a922b 100644
--- a/src/parser/validator.rs
+++ b/src/parser/validator.rs
@@ -40,15 +40,12 @@ impl<'help, 'cmd> Validator<'help, 'cmd> {
             if should_err {
                 return Err(Error::empty_value(
                     self.cmd,
-                    &o.possible_vals
+                    &get_possible_values(o)
                         .iter()
                         .filter(|pv| !pv.is_hide_set())
                         .map(PossibleValue::get_name)
                         .collect::<Vec<_>>(),
                     o.to_string(),
-                    Usage::new(self.cmd)
-                        .required(&self.required)
-                        .create_usage_with_title(&[]),
                 ));
             }
         }
@@ -110,12 +107,6 @@ impl<'help, 'cmd> Validator<'help, 'cmd> {
                     .iter()
                     .any(|pv| pv.matches(&val_str, arg.is_ignore_case_set()));
                 if !ok {
-                    let used: Vec<Id> = matcher
-                        .arg_names()
-                        .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent))
-                        .filter(|&n| self.cmd.find(n).map_or(true, |a| !a.is_hide_set()))
-                        .cloned()
-                        .collect();
                     return Err(Error::invalid_value(
                         self.cmd,
                         val_str.into_owned(),
@@ -125,9 +116,6 @@ impl<'help, 'cmd> Validator<'help, 'cmd> {
                             .map(PossibleValue::get_name)
                             .collect::<Vec<_>>(),
                         arg.to_string(),
-                        Usage::new(self.cmd)
-                            .required(&self.required)
-                            .create_usage_with_title(&used),
                     ));
                 }
             }
@@ -135,15 +123,12 @@ impl<'help, 'cmd> Validator<'help, 'cmd> {
                 debug!("Validator::validate_arg_values: illegal empty val found");
                 return Err(Error::empty_value(
                     self.cmd,
-                    &arg.possible_vals
+                    &get_possible_values(arg)
                         .iter()
                         .filter(|pv| !pv.is_hide_set())
                         .map(PossibleValue::get_name)
                         .collect::<Vec<_>>(),
                     arg.to_string(),
-                    Usage::new(self.cmd)
-                        .required(&self.required)
-                        .create_usage_with_title(&[]),
                 ));
             }
 
@@ -448,15 +433,12 @@ impl<'help, 'cmd> Validator<'help, 'cmd> {
         if a.is_takes_value_set() && !min_vals_zero && ma.all_val_groups_empty() {
             return Err(Error::empty_value(
                 self.cmd,
-                &a.possible_vals
+                &get_possible_values(a)
                     .iter()
                     .filter(|pv| !pv.is_hide_set())
                     .map(PossibleValue::get_name)
                     .collect::<Vec<_>>(),
                 a.to_string(),
-                Usage::new(self.cmd)
-                    .required(&self.required)
-                    .create_usage_with_title(&[]),
             ));
         }
         Ok(())
@@ -674,3 +656,19 @@ impl Conflicts {
         })
     }
 }
+
+fn get_possible_values<'help>(a: &Arg<'help>) -> Vec<PossibleValue<'help>> {
+    #![allow(deprecated)]
+    if !a.is_takes_value_set() {
+        vec![]
+    } else if let Some(pvs) = a.get_possible_values() {
+        // Check old first in case the user explicitly set possible values and the derive inferred
+        // a `ValueParser` with some.
+        pvs.to_vec()
+    } else {
+        a.get_value_parser()
+            .possible_values()
+            .map(|pvs| pvs.collect())
+            .unwrap_or_default()
+    }
+}


Test patch:
diff --git a/tests/builder/possible_values.rs b/tests/builder/possible_values.rs
index ccf17239..4c128816 100644
--- a/tests/builder/possible_values.rs
+++ b/tests/builder/possible_values.rs
@@ -8,9 +8,6 @@ static PV_ERROR: &str = "error: \"slo\" isn't a valid value for '-O <option>'
 
 \tDid you mean \"slow\"?
 
-USAGE:
-    clap-test -O <option>
-
 For more information try --help
 ";
 
@@ -18,9 +15,6 @@ For more information try --help
 static PV_ERROR: &str = "error: \"slo\" isn't a valid value for '-O <option>'
 \t[possible values: slow, fast, \"ludicrous speed\"]
 
-USAGE:
-    clap-test -O <option>
-
 For more information try --help
 ";
 
@@ -30,9 +24,6 @@ static PV_ERROR_ESCAPED: &str = "error: \"ludicrous\" isn't a valid value for '-
 
 \tDid you mean \"ludicrous speed\"?
 
-USAGE:
-    clap-test -O <option>
-
 For more information try --help
 ";
 
@@ -40,9 +31,6 @@ For more information try --help
 static PV_ERROR_ESCAPED: &str = "error: \"ludicrous\" isn't a valid value for '-O <option>'
 \t[possible values: slow, fast, \"ludicrous speed\"]
 
-USAGE:
-    clap-test -O <option>
-
 For more information try --help
 ";
 
@@ -286,9 +274,6 @@ static MISSING_PV_ERROR: &str =
     "error: The argument '-O <option>' requires a value but none was supplied
 \t[possible values: slow, fast, \"ludicrous speed\"]
 
-USAGE:
-    clap-test [OPTIONS]
-
 For more information try --help
 ";