Repository: clap-rs/clap (language: rust)

Issue:
try_from_str and try_from_os_str functions are called twice
### Please complete the following tasks

- [X] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [X] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues

### Rust Version

rustc 1.58.1 (db9d1b20b 2022-01-20)

### Clap Version

3.1.6

### Minimal reproducible code

```rust
use clap::Parser;
use std::sync::atomic::{AtomicU8, Ordering};

static COUNT: AtomicU8 = AtomicU8::new(0);

#[derive(Parser)]
#[clap(name = "from_str")]
struct Opt {
    #[clap(parse(try_from_str = spy))]
    s: String,
}

fn main() {
    let opt = Opt::parse_from(&["foo", "h"]);
    assert_eq!(COUNT.load(Ordering::SeqCst), 1);
}

fn spy(s: &str) -> Result<String, String> {
    let c = COUNT.fetch_add(1, Ordering::SeqCst);
    println!("spy called {}", c);
    Ok(s.to_string())
}
```


### Steps to reproduce the bug with the above code

```cargo run```

### Actual Behaviour

When specifying a parser for an attribute using `try_from_str` and the `#[derive(Parser)]` macro the function passed used for parising is called twice, once as part of the validate phase and then again to convert the argument to the expected type.

This causes issues when the function is not idemponent, such as opening a file, or performing a network request.

### Expected Behaviour

The `try_from_str` should only be called once.

### Additional Context

_No response_

### Debug Output

```
[        clap::build::command] 	App::_do_parse
[        clap::build::command] 	App::_build
[        clap::build::command] 	App::_propagate:from_str
[        clap::build::command] 	App::_check_help_and_version: from_str
[        clap::build::command] 	App::_check_help_and_version: Removing generated version
[        clap::build::command] 	App::_propagate_global_args:from_str
[        clap::build::command] 	App::_derive_display_order:from_str
[  clap::build::debug_asserts] 	Command::_debug_asserts
[  clap::build::debug_asserts] 	Arg::_debug_asserts:help
[  clap::build::debug_asserts] 	Arg::_debug_asserts:s
[  clap::build::debug_asserts] 	Command::_verify_positionals
[         clap::parse::parser] 	Parser::get_matches_with
[         clap::parse::parser] 	Parser::get_matches_with: Begin parsing 'RawOsStr("h")' ([104])
[         clap::parse::parser] 	Parser::get_matches_with: Positional counter...1
[         clap::parse::parser] 	Parser::get_matches_with: Low index multiples...false
[         clap::parse::parser] 	Parser::possible_subcommand: arg=RawOsStr("h")
[         clap::parse::parser] 	Parser::get_matches_with: sc=None
[         clap::parse::parser] 	Parser::remove_overrides: id=s
[    clap::parse::arg_matcher] 	ArgMatcher::inc_occurrence_of_arg: id=s
[        clap::build::command] 	App::groups_for_arg: id=s
[         clap::parse::parser] 	Parser::add_val_to_arg; arg=s, val=RawOsStr("h")
[         clap::parse::parser] 	Parser::add_val_to_arg; trailing_values=false, DontDelimTrailingVals=false
[         clap::parse::parser] 	Parser::add_single_val_to_arg: adding val..."h"
[         clap::parse::parser] 	Parser::add_single_val_to_arg: cur_idx:=1
[        clap::build::command] 	App::groups_for_arg: id=s
[    clap::parse::arg_matcher] 	ArgMatcher::needs_more_vals: o=s
[      clap::parse::validator] 	Validator::validate
[         clap::parse::parser] 	Parser::add_defaults
[         clap::parse::parser] 	Parser::add_defaults:iter:s:
[         clap::parse::parser] 	Parser::add_value: doesn't have conditional defaults
[         clap::parse::parser] 	Parser::add_value:iter:s: doesn't have default vals
[         clap::parse::parser] 	Parser::add_value:iter:s: doesn't have default missing vals
[      clap::parse::validator] 	Validator::validate_conflicts
[      clap::parse::validator] 	Validator::validate_exclusive
[      clap::parse::validator] 	Validator::validate_exclusive:iter:s
[      clap::parse::validator] 	Validator::validate_conflicts::iter: id=s
[      clap::parse::validator] 	Conflicts::gather_conflicts
[      clap::parse::validator] 	Validator::validate_required: required=ChildGraph([Child { id: s, children: [] }])
[      clap::parse::validator] 	Validator::gather_requires
[      clap::parse::validator] 	Validator::gather_requires:iter:s
[      clap::parse::validator] 	Validator::validate_required_unless
[      clap::parse::validator] 	Validator::validate_matched_args
[      clap::parse::validator] 	Validator::validate_matched_args:iter:s: vals=Flatten {
    inner: FlattenCompat {
        iter: Fuse {
            iter: Some(
                Iter(
                    [
                        [
                            "h",
                        ],
                    ],
                ),
            ),
        },
        frontiter: None,
        backiter: None,
    },
}
[      clap::parse::validator] 	Validator::validate_arg_num_vals
[      clap::parse::validator] 	Validator::validate_arg_values: arg="s"
[      clap::parse::validator] 	Validator::validate_arg_values: checking validator...
spy called 0
[      clap::parse::validator] 	good
[      clap::parse::validator] 	Validator::validate_arg_num_occurs: "s"=1
[    clap::parse::arg_matcher] 	ArgMatcher::get_global_values: global_arg_vec=[help]
spy called 1
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `2`,
 right: `1`', src/main.rs:15:5
 ```


Discussion / hints:
I think that fixing this should be as simple as just never adding the validators, if there is an error in the get phase it is beautifully printed in exactly the same way as the validator would. 
Huh, we had a previous PR for this but no issue.  https://github.com/clap-rs/clap/issues/2683 is the tracking issue for resolving this

> I think that fixing this should be as simple as just never adding the validators, if there is an error in the get phase it is beautifully printed in exactly the same way as the validator would.

There are subtle differences, like color support.
Bah, you are of course right it is not as simple as I thought, 
with validator (pretty colours for the `<S>` and `"/tmp/not-found"`):
```
error: Invalid value "/tmp/not-found" for '<S>': No such file or directory (os error 2)

For more information try --help
```
without validator (only `error:` gets color):
```
error: Invalid value for s: No such file or directory (os error 2)

USAGE:
    foo [OPTIONS] <S>
```

Since the nice error message relies on the `context` which can't be set via the public API, I don't think it is worth adding the colours, but adding the value is easy.

Would you be interested in a PR dropping the validate calls or should I just wait for 4.0?

>  I don't think it is worth adding the colours,

I would consider that a blocker for a solution

> Would you be interested in a PR dropping the validate calls 

Even if the final message ends up looking exactly the same, this would be borderline for breaking compatibility due to changes in expectations of behavior.  Let's just wait for that 4.0 work to go through.
4.0 it is then! Thank you for all your hard work and speedy responses.

Fix patch:
diff --git a/README.md b/README.md
index 94b9bb9ea9f..ae18179d336 100644
--- a/README.md
+++ b/README.md
@@ -44,11 +44,11 @@ use clap::Parser;
 #[clap(author, version, about, long_about = None)]
 struct Args {
     /// Name of the person to greet
-    #[clap(short, long)]
+    #[clap(short, long, value_parser)]
     name: String,
 
     /// Number of times to greet
-    #[clap(short, long, default_value_t = 1)]
+    #[clap(short, long, value_parser, default_value_t = 1)]
     count: u8,
 }
 
diff --git a/clap_complete/src/generator/utils.rs b/clap_complete/src/generator/utils.rs
index 735cad60d7e..251f1416897 100644
--- a/clap_complete/src/generator/utils.rs
+++ b/clap_complete/src/generator/utils.rs
@@ -128,7 +128,9 @@ pub fn flags<'help>(p: &Command<'help>) -> Vec<Arg<'help>> {
 
 /// Get the possible values for completion
 pub fn possible_values<'help>(a: &Arg<'help>) -> Option<Vec<clap::PossibleValue<'help>>> {
-    if let Some(pvs) = a.get_possible_values() {
+    if !a.is_takes_value_set() {
+        None
+    } 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.
         Some(pvs.to_vec())
diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs
index aaee167e31c..03e87b2b99c 100644
--- a/clap_derive/src/attrs.rs
+++ b/clap_derive/src/attrs.rs
@@ -42,6 +42,7 @@ pub struct Attrs {
     ty: Option<Type>,
     doc_comment: Vec<Method>,
     methods: Vec<Method>,
+    value_parser: Option<ValueParser>,
     parser: Sp<Parser>,
     verbatim_doc_comment: Option<Ident>,
     next_display_order: Option<Method>,
@@ -64,6 +65,12 @@ impl Attrs {
         res.push_attrs(attrs);
         res.push_doc_comment(attrs, "about");
 
+        if let Some(parser) = res.value_parser.as_ref() {
+            abort!(
+                parser.span(),
+                "`value_parse` attribute is only allowed on fields"
+            );
+        }
         if res.has_custom_parser {
             abort!(
                 res.parser.span(),
@@ -101,6 +108,12 @@ impl Attrs {
 
         match &*res.kind {
             Kind::Flatten => {
+                if let Some(parser) = res.value_parser.as_ref() {
+                    abort!(
+                        parser.span(),
+                        "`value_parse` attribute is only allowed flattened entry"
+                    );
+                }
                 if res.has_custom_parser {
                     abort!(
                         res.parser.span(),
@@ -121,6 +134,12 @@ impl Attrs {
             Kind::ExternalSubcommand => (),
 
             Kind::Subcommand(_) => {
+                if let Some(parser) = res.value_parser.as_ref() {
+                    abort!(
+                        parser.span(),
+                        "`value_parse` attribute is only allowed for subcommand"
+                    );
+                }
                 if res.has_custom_parser {
                     abort!(
                         res.parser.span(),
@@ -189,6 +208,12 @@ impl Attrs {
         res.push_attrs(&variant.attrs);
         res.push_doc_comment(&variant.attrs, "help");
 
+        if let Some(parser) = res.value_parser.as_ref() {
+            abort!(
+                parser.span(),
+                "`value_parse` attribute is only allowed on fields"
+            );
+        }
         if res.has_custom_parser {
             abort!(
                 res.parser.span(),
@@ -226,6 +251,12 @@ impl Attrs {
 
         match &*res.kind {
             Kind::Flatten => {
+                if let Some(parser) = res.value_parser.as_ref() {
+                    abort!(
+                        parser.span(),
+                        "`value_parse` attribute is not allowed for flattened entry"
+                    );
+                }
                 if res.has_custom_parser {
                     abort!(
                         res.parser.span(),
@@ -250,6 +281,12 @@ impl Attrs {
             }
 
             Kind::Subcommand(_) => {
+                if let Some(parser) = res.value_parser.as_ref() {
+                    abort!(
+                        parser.span(),
+                        "`value_parse` attribute is not allowed for subcommand"
+                    );
+                }
                 if res.has_custom_parser {
                     abort!(
                         res.parser.span(),
@@ -297,6 +334,12 @@ impl Attrs {
             Kind::Arg(orig_ty) => {
                 let mut ty = Ty::from_syn_ty(&field.ty);
                 if res.has_custom_parser {
+                    if let Some(parser) = res.value_parser.as_ref() {
+                        abort!(
+                            parser.span(),
+                            "`value_parse` attribute conflicts with `parse` attribute"
+                        );
+                    }
                     match *ty {
                         Ty::Option | Ty::Vec | Ty::OptionVec => (),
                         _ => ty = Sp::new(Ty::Other, ty.span()),
@@ -369,6 +412,7 @@ impl Attrs {
             env_casing,
             doc_comment: vec![],
             methods: vec![],
+            value_parser: None,
             parser: Parser::default_spanned(default_span),
             verbatim_doc_comment: None,
             next_display_order: None,
@@ -383,6 +427,8 @@ impl Attrs {
     fn push_method(&mut self, name: Ident, arg: impl ToTokens) {
         if name == "name" {
             self.name = Name::Assigned(quote!(#arg));
+        } else if name == "value_parser" {
+            self.value_parser = Some(ValueParser::Explicit(Method::new(name, quote!(#arg))));
         } else {
             self.methods.push(Method::new(name, quote!(#arg)));
         }
@@ -403,6 +449,11 @@ impl Attrs {
                     self.push_method(ident, self.name.clone().translate(*self.casing));
                 }
 
+                ValueParser(ident) => {
+                    use crate::attrs::ValueParser;
+                    self.value_parser = Some(ValueParser::Implicit(ident));
+                }
+
                 Env(ident) => {
                     self.push_method(ident, self.name.clone().translate(*self.env_casing));
                 }
@@ -689,6 +740,16 @@ impl Attrs {
         self.name.clone().translate(CasingStyle::ScreamingSnake)
     }
 
+    pub fn value_parser(&self) -> ValueParser {
+        self.value_parser
+            .clone()
+            .unwrap_or_else(|| ValueParser::Explicit(self.parser.value_parser()))
+    }
+
+    pub fn custom_value_parser(&self) -> bool {
+        self.value_parser.is_some()
+    }
+
     pub fn parser(&self) -> &Sp<Parser> {
         &self.parser
     }
@@ -732,6 +793,31 @@ impl Attrs {
     }
 }
 
+#[derive(Clone)]
+pub enum ValueParser {
+    Explicit(Method),
+    Implicit(Ident),
+}
+
+impl ValueParser {
+    pub fn resolve(self, inner_type: &Type) -> Method {
+        match self {
+            Self::Explicit(method) => method,
+            Self::Implicit(ident) => {
+                let func = Ident::new("value_parser", ident.span());
+                Method::new(func, quote!(clap::value_parser!(#inner_type)))
+            }
+        }
+    }
+
+    pub fn span(&self) -> Span {
+        match self {
+            Self::Explicit(method) => method.name.span(),
+            Self::Implicit(ident) => ident.span(),
+        }
+    }
+}
+
 #[allow(clippy::large_enum_variant)]
 #[derive(Clone)]
 pub enum Kind {
@@ -865,9 +951,23 @@ impl Parser {
         let parser = Parser { kind, func };
         Sp::new(parser, parse_ident.span())
     }
+
+    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())),
+        }
+    }
 }
 
-#[derive(Debug, PartialEq, Clone)]
+#[derive(Debug, PartialEq, Clone, Copy)]
 pub enum ParserKind {
     FromStr,
     TryFromStr,
diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs
index dddddbef008..ec251bd4ba8 100644
--- a/clap_derive/src/derives/args.rs
+++ b/clap_derive/src/derives/args.rs
@@ -132,11 +132,19 @@ pub fn gen_from_arg_matches_for_struct(
         #[deny(clippy::correctness)]
         impl #impl_generics clap::FromArgMatches for #struct_name #ty_generics #where_clause {
             fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+                Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
+            }
+
+            fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
                 let v = #struct_name #constructor;
                 ::std::result::Result::Ok(v)
             }
 
             fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
+                self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
+            }
+
+            fn update_from_arg_matches_mut(&mut self, __clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
                 #updater
                 ::std::result::Result::Ok(())
             }
@@ -238,11 +246,12 @@ pub fn gen_augment(
                 let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
                 let flag = *attrs.parser().kind == ParserKind::FromFlag;
 
+                let value_parser = attrs.value_parser().resolve(convert_type);
                 let parser = attrs.parser();
                 let func = &parser.func;
 
                 let validator = match *parser.kind {
-                    _ if attrs.is_enum() => quote!(),
+                    _ if attrs.custom_value_parser() || attrs.is_enum() => quote!(),
                     ParserKind::TryFromStr => quote_spanned! { func.span()=>
                         .validator(|s| {
                             #func(s)
@@ -257,21 +266,9 @@ pub fn gen_augment(
                     | ParserKind::FromFlag
                     | ParserKind::FromOccurrences => quote!(),
                 };
-                let allow_invalid_utf8 = match *parser.kind {
-                    _ if attrs.is_enum() => quote!(),
-                    ParserKind::FromOsStr | ParserKind::TryFromOsStr => {
-                        quote_spanned! { func.span()=>
-                            .allow_invalid_utf8(true)
-                        }
-                    }
-                    ParserKind::FromStr
-                    | ParserKind::TryFromStr
-                    | ParserKind::FromFlag
-                    | ParserKind::FromOccurrences => quote!(),
-                };
 
                 let value_name = attrs.value_name();
-                let possible_values = if attrs.is_enum() {
+                let possible_values = if attrs.is_enum() && !attrs.custom_value_parser() {
                     gen_arg_enum_possible_values(convert_type)
                 } else {
                     quote!()
@@ -286,7 +283,7 @@ pub fn gen_augment(
                             .value_name(#value_name)
                             #possible_values
                             #validator
-                            #allow_invalid_utf8
+                            #value_parser
                         }
                     }
 
@@ -298,7 +295,7 @@ pub fn gen_augment(
                         .multiple_values(false)
                         #possible_values
                         #validator
-                        #allow_invalid_utf8
+                        #value_parser
                     },
 
                     Ty::OptionVec => quote_spanned! { ty.span()=>
@@ -307,7 +304,7 @@ pub fn gen_augment(
                         .multiple_occurrences(true)
                         #possible_values
                         #validator
-                        #allow_invalid_utf8
+                        #value_parser
                     },
 
                     Ty::Vec => {
@@ -317,7 +314,7 @@ pub fn gen_augment(
                             .multiple_occurrences(true)
                             #possible_values
                             #validator
-                            #allow_invalid_utf8
+                            #value_parser
                         }
                     }
 
@@ -337,7 +334,7 @@ pub fn gen_augment(
                             .required(#required)
                             #possible_values
                             #validator
-                            #allow_invalid_utf8
+                            #value_parser
                         }
                     }
                 };
@@ -398,7 +395,7 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att
                         quote_spanned! { kind.span()=>
                             #field_name: {
                                 if #arg_matches.subcommand_name().map(<#subcmd_type as clap::Subcommand>::has_subcommand).unwrap_or(false) {
-                                    Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches(#arg_matches)?)
+                                    Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?)
                                 } else {
                                     None
                                 }
@@ -408,7 +405,7 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att
                     _ => {
                         quote_spanned! { kind.span()=>
                             #field_name: {
-                                <#subcmd_type as clap::FromArgMatches>::from_arg_matches(#arg_matches)?
+                                <#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)?
                             }
                         }
                     },
@@ -416,7 +413,7 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att
             }
 
             Kind::Flatten => quote_spanned! { kind.span()=>
-                #field_name: clap::FromArgMatches::from_arg_matches(#arg_matches)?
+                #field_name: clap::FromArgMatches::from_arg_matches_mut(#arg_matches)?
             },
 
             Kind::Skip(val) => match val {
@@ -472,7 +469,7 @@ pub fn gen_updater(
                 };
 
                 let updater = quote_spanned! { ty.span()=>
-                    <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches(#field_name, #arg_matches)?;
+                    <#subcmd_type as clap::FromArgMatches>::update_from_arg_matches_mut(#field_name, #arg_matches)?;
                 };
 
                 let updater = match **ty {
@@ -480,7 +477,7 @@ pub fn gen_updater(
                         if let Some(#field_name) = #field_name.as_mut() {
                             #updater
                         } else {
-                            *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches(
+                            *#field_name = Some(<#subcmd_type as clap::FromArgMatches>::from_arg_matches_mut(
                                 #arg_matches
                             )?);
                         }
@@ -500,7 +497,7 @@ pub fn gen_updater(
 
             Kind::Flatten => quote_spanned! { kind.span()=> {
                     #access
-                    clap::FromArgMatches::update_from_arg_matches(#field_name, #arg_matches)?;
+                    clap::FromArgMatches::update_from_arg_matches_mut(#field_name, #arg_matches)?;
                 }
             },
 
@@ -529,35 +526,51 @@ fn gen_parsers(
     let span = parser.kind.span();
     let convert_type = inner_type(**ty, &field.ty);
     let id = attrs.id();
-    let (get_one, get_many, mut parse) = match *parser.kind {
+    let (get_one, get_many, deref, mut parse) = match *parser.kind {
+        FromOccurrences => (
+            quote_spanned!(span=> occurrences_of),
+            quote!(),
+            quote!(|s| ::std::ops::Deref::deref(s)),
+            func.clone(),
+        ),
+        FromFlag => (
+            quote!(),
+            quote!(),
+            quote!(|s| ::std::ops::Deref::deref(s)),
+            func.clone(),
+        ),
+        _ if attrs.custom_value_parser() => (
+            quote_spanned!(span=> remove_one::<#convert_type>),
+            quote_spanned!(span=> remove_many::<#convert_type>),
+            quote!(|s| ::std::sync::Arc::try_unwrap(s).unwrap_or_else(|arc| (*arc).clone())),
+            quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(s)),
+        ),
         FromStr => (
             quote_spanned!(span=> get_one::<String>),
             quote_spanned!(span=> get_many::<String>),
+            quote!(|s| ::std::ops::Deref::deref(s)),
             quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))),
         ),
         TryFromStr => (
             quote_spanned!(span=> get_one::<String>),
             quote_spanned!(span=> get_many::<String>),
+            quote!(|s| ::std::ops::Deref::deref(s)),
             quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
         ),
         FromOsStr => (
             quote_spanned!(span=> get_one::<::std::ffi::OsString>),
             quote_spanned!(span=> get_many::<::std::ffi::OsString>),
+            quote!(|s| ::std::ops::Deref::deref(s)),
             quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(#func(s))),
         ),
         TryFromOsStr => (
             quote_spanned!(span=> get_one::<::std::ffi::OsString>),
             quote_spanned!(span=> get_many::<::std::ffi::OsString>),
+            quote!(|s| ::std::ops::Deref::deref(s)),
             quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
         ),
-        FromOccurrences => (
-            quote_spanned!(span=> occurrences_of),
-            quote!(),
-            func.clone(),
-        ),
-        FromFlag => (quote!(), quote!(), func.clone()),
     };
-    if attrs.is_enum() {
+    if attrs.is_enum() && !attrs.custom_value_parser() {
         let ci = attrs.ignore_case();
 
         parse = quote_spanned! { convert_type.span()=>
@@ -589,7 +602,7 @@ fn gen_parsers(
             quote_spanned! { ty.span()=>
                 #arg_matches.#get_one(#id)
                     .expect("unexpected type")
-                    .map(|s| ::std::ops::Deref::deref(s))
+                    .map(#deref)
                     .map(#parse)
                     .transpose()?
             }
@@ -600,7 +613,7 @@ fn gen_parsers(
                 Some(
                     #arg_matches.#get_one(#id)
                         .expect("unexpected type")
-                        .map(|s| ::std::ops::Deref::deref(s))
+                        .map(#deref)
                         .map(#parse).transpose()?
                 )
             } else {
@@ -612,7 +625,7 @@ fn gen_parsers(
             if #arg_matches.is_present(#id) {
                 Some(#arg_matches.#get_many(#id)
                     .expect("unexpected type")
-                    .map(|v| v.map(|s| ::std::ops::Deref::deref(s)).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
+                    .map(|v| v.map(#deref).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
                     .transpose()?
                     .unwrap_or_else(Vec::new))
             } else {
@@ -624,7 +637,7 @@ fn gen_parsers(
             quote_spanned! { ty.span()=>
                 #arg_matches.#get_many(#id)
                     .expect("unexpected type")
-                    .map(|v| v.map(|s| ::std::ops::Deref::deref(s)).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
+                    .map(|v| v.map(#deref).map::<::std::result::Result<#convert_type, clap::Error>, _>(#parse).collect::<::std::result::Result<Vec<_>, clap::Error>>())
                     .transpose()?
                     .unwrap_or_else(Vec::new)
             }
@@ -646,7 +659,7 @@ fn gen_parsers(
             quote_spanned! { ty.span()=>
                 #arg_matches.#get_one(#id)
                     .expect("unexpected type")
-                    .map(|s| ::std::ops::Deref::deref(s))
+                    .map(#deref)
                     .ok_or_else(|| clap::Error::raw(clap::ErrorKind::MissingRequiredArgument, format!("The following required argument was not provided: {}", #id)))
                     .and_then(#parse)?
             }
diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs
index ac7a8a1dcdf..7edc2eb00f1 100644
--- a/clap_derive/src/derives/subcommand.rs
+++ b/clap_derive/src/derives/subcommand.rs
@@ -122,7 +122,15 @@ fn gen_from_arg_matches_for_enum(
         )]
         #[deny(clippy::correctness)]
         impl #impl_generics clap::FromArgMatches for #name #ty_generics #where_clause {
+            fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+                Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
+            }
+
             #from_arg_matches
+
+            fn update_from_arg_matches(&mut self, __clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<(), clap::Error> {
+                self.update_from_arg_matches_mut(&mut __clap_arg_matches.clone())
+            }
             #update_from_arg_matches
         }
     }
@@ -400,7 +408,7 @@ fn gen_from_arg_matches(
     let mut ext_subcmd = None;
 
     let subcommand_name_var = format_ident!("__clap_name");
-    let sub_arg_matches_var = format_ident!("__clap_sub_arg_matches");
+    let sub_arg_matches_var = format_ident!("__clap_arg_matches");
     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
         .iter()
         .filter_map(|variant| {
@@ -471,7 +479,7 @@ fn gen_from_arg_matches(
             Unit => quote!(),
             Unnamed(ref fields) if fields.unnamed.len() == 1 => {
                 let ty = &fields.unnamed[0];
-                quote!( ( <#ty as clap::FromArgMatches>::from_arg_matches(__clap_arg_matches)? ) )
+                quote!( ( <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)? ) )
             }
             Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
         };
@@ -496,8 +504,12 @@ fn gen_from_arg_matches(
             Unnamed(ref fields) if fields.unnamed.len() == 1 => {
                 let ty = &fields.unnamed[0];
                 quote! {
-                    if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
-                        let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches(__clap_arg_matches)?;
+                    if __clap_arg_matches
+                        .subcommand_name()
+                        .map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
+                        .unwrap_or_default()
+                    {
+                        let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
                         return ::std::result::Result::Ok(#name :: #variant_name (__clap_res));
                     }
                 }
@@ -515,9 +527,10 @@ fn gen_from_arg_matches(
                     ::std::iter::once(#str_ty::from(#subcommand_name_var))
                     .chain(
                         #sub_arg_matches_var
-                            .get_many::<#str_ty>("")
+                            .remove_many::<#str_ty>("")
                             .expect("unexpected type")
                             .into_iter().flatten()  // `""` isn't present, bug in `unstable-v4`
+                            .map(|f| ::std::sync::Arc::try_unwrap(f).unwrap_or_else(|arc| (*arc).clone()))
                             .map(#str_ty::from)
                     )
                     .collect::<::std::vec::Vec<_>>()
@@ -530,14 +543,12 @@ fn gen_from_arg_matches(
     };
 
     quote! {
-        fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
-            if let Some((#subcommand_name_var, #sub_arg_matches_var)) = __clap_arg_matches.subcommand() {
-                {
-                    let __clap_arg_matches = #sub_arg_matches_var;
-                    #( #subcommands )*
-                }
+        fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
+            #( #child_subcommands )else*
 
-                #( #child_subcommands )else*
+            if let Some((#subcommand_name_var, mut __clap_arg_sub_matches)) = __clap_arg_matches.remove_subcommand() {
+                let #sub_arg_matches_var = &mut __clap_arg_sub_matches;
+                #( #subcommands )*
 
                 #wildcard
             } else {
@@ -564,7 +575,7 @@ fn gen_update_from_arg_matches(
             );
 
             match &*attrs.kind() {
-                // Fallback to `from_arg_matches`
+                // Fallback to `from_arg_matches_mut`
                 Kind::ExternalSubcommand => None,
                 _ => Some((variant, attrs)),
             }
@@ -602,9 +613,9 @@ fn gen_update_from_arg_matches(
                 if fields.unnamed.len() == 1 {
                     (
                         quote!((ref mut __clap_arg)),
-                        quote!(clap::FromArgMatches::update_from_arg_matches(
+                        quote!(clap::FromArgMatches::update_from_arg_matches_mut(
                             __clap_arg,
-                            __clap_sub_arg_matches
+                            __clap_arg_matches
                         )?),
                     )
                 } else {
@@ -615,7 +626,8 @@ fn gen_update_from_arg_matches(
 
         quote! {
             #name :: #variant_name #pattern if #sub_name == __clap_name => {
-                let __clap_arg_matches = __clap_sub_arg_matches;
+                let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap();
+                let __clap_arg_matches = &mut __clap_arg_sub_matches;
                 #updater
             }
         }
@@ -629,7 +641,7 @@ fn gen_update_from_arg_matches(
                 quote! {
                     if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
                         if let #name :: #variant_name (child) = s {
-                            <#ty as clap::FromArgMatches>::update_from_arg_matches(child, __clap_arg_matches)?;
+                            <#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?;
                             return ::std::result::Result::Ok(());
                         }
                     }
@@ -643,16 +655,16 @@ fn gen_update_from_arg_matches(
     });
 
     quote! {
-        fn update_from_arg_matches<'b>(
+        fn update_from_arg_matches_mut<'b>(
             &mut self,
-            __clap_arg_matches: &clap::ArgMatches,
+            __clap_arg_matches: &mut clap::ArgMatches,
         ) -> ::std::result::Result<(), clap::Error> {
-            if let Some((__clap_name, __clap_sub_arg_matches)) = __clap_arg_matches.subcommand() {
+            if let Some(__clap_name) = __clap_arg_matches.subcommand_name() {
                 match self {
                     #( #subcommands ),*
                     s => {
                         #( #child_subcommands )*
-                        *s = <Self as clap::FromArgMatches>::from_arg_matches(__clap_arg_matches)?;
+                        *s = <Self as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
                     }
                 }
             }
diff --git a/clap_derive/src/parse.rs b/clap_derive/src/parse.rs
index 2973cf670f4..9dbc1454ada 100644
--- a/clap_derive/src/parse.rs
+++ b/clap_derive/src/parse.rs
@@ -26,6 +26,7 @@ pub enum ClapAttr {
     // single-identifier attributes
     Short(Ident),
     Long(Ident),
+    ValueParser(Ident),
     Env(Ident),
     Flatten(Ident),
     ArgEnum(Ident),
@@ -182,6 +183,7 @@ impl Parse for ClapAttr {
             match name_str.as_ref() {
                 "long" => Ok(Long(name)),
                 "short" => Ok(Short(name)),
+                "value_parser" => Ok(ValueParser(name)),
                 "env" => Ok(Env(name)),
                 "flatten" => Ok(Flatten(name)),
                 "arg_enum" => Ok(ArgEnum(name)),
diff --git a/examples/cargo-example-derive.rs b/examples/cargo-example-derive.rs
index 1ee0a02551f..6667a4a7df9 100644
--- a/examples/cargo-example-derive.rs
+++ b/examples/cargo-example-derive.rs
@@ -12,7 +12,7 @@ enum Cargo {
 #[derive(clap::Args)]
 #[clap(author, version, about, long_about = None)]
 struct ExampleDerive {
-    #[clap(long, parse(from_os_str))]
+    #[clap(long, value_parser)]
     manifest_path: Option<std::path::PathBuf>,
 }
 
diff --git a/examples/demo.rs b/examples/demo.rs
index 262a81a2c1a..95748972444 100644
--- a/examples/demo.rs
+++ b/examples/demo.rs
@@ -7,11 +7,11 @@ use clap::Parser;
 #[clap(author, version, about, long_about = None)]
 struct Args {
     /// Name of the person to greet
-    #[clap(short, long)]
+    #[clap(short, long, value_parser)]
     name: String,
 
     /// Number of times to greet
-    #[clap(short, long, default_value_t = 1)]
+    #[clap(short, long, value_parser, default_value_t = 1)]
     count: u8,
 }
 
diff --git a/examples/derive_ref/README.md b/examples/derive_ref/README.md
index 11001dde85f..2cde95ff913 100644
--- a/examples/derive_ref/README.md
+++ b/examples/derive_ref/README.md
@@ -173,6 +173,8 @@ These correspond to a `clap::Arg`.
 **Magic attributes**:
 - `name = <expr>`: `clap::Arg::new`
   - When not present: case-converted field name is used
+- `value_parser [= <expr>]`: `clap::Arg::value_parser`
+  - When not present: will auto-select an implementation based on the field type
 - `help = <expr>`: `clap::Arg::help`
   - When not present: [Doc comment summary](#doc-comments)
 - `long_help = <expr>`: `clap::Arg::long_help`
@@ -198,6 +200,7 @@ These correspond to a `clap::Arg`.
   - When `Option<T>`, the subcommand becomes optional
 - `from_global`: Read a `clap::Arg::global` argument (raw attribute), regardless of what subcommand you are in
 - `parse(<kind> [= <function>])`: `clap::Arg::validator` and `clap::ArgMatches::values_of_t`
+  - **Deprecated:** except for `from_flag` or `from_occurrences`, instead use `value_parser`
   - Default: `try_from_str`
   - Warning: for `Path` / `OsString`, be sure to use `try_from_os_str`
   - See [Arg Types](#arg-types) for more details
diff --git a/examples/derive_ref/hand_subcommand.rs b/examples/derive_ref/hand_subcommand.rs
index 5afd9413271..d85a29493cd 100644
--- a/examples/derive_ref/hand_subcommand.rs
+++ b/examples/derive_ref/hand_subcommand.rs
@@ -3,12 +3,14 @@ use clap::{ArgMatches, Args as _, Command, FromArgMatches, Parser, Subcommand};
 
 #[derive(Parser, Debug)]
 struct AddArgs {
+    #[clap(value_parser)]
     name: Vec<String>,
 }
 #[derive(Parser, Debug)]
 struct RemoveArgs {
     #[clap(short, long)]
     force: bool,
+    #[clap(value_parser)]
     name: Vec<String>,
 }
 
diff --git a/examples/escaped-positional-derive.rs b/examples/escaped-positional-derive.rs
index 8038395aff2..dd52c090cc1 100644
--- a/examples/escaped-positional-derive.rs
+++ b/examples/escaped-positional-derive.rs
@@ -8,10 +8,10 @@ struct Cli {
     #[clap(short = 'f')]
     eff: bool,
 
-    #[clap(short = 'p', value_name = "PEAR")]
+    #[clap(short = 'p', value_name = "PEAR", value_parser)]
     pea: Option<String>,
 
-    #[clap(last = true)]
+    #[clap(last = true, value_parser)]
     slop: Vec<String>,
 }
 
diff --git a/examples/git-derive.rs b/examples/git-derive.rs
index 7e44edb1aff..ecbda3fe9d3 100644
--- a/examples/git-derive.rs
+++ b/examples/git-derive.rs
@@ -20,19 +20,21 @@ enum Commands {
     #[clap(arg_required_else_help = true)]
     Clone {
         /// The remote to clone
+        #[clap(value_parser)]
         remote: String,
     },
     /// pushes things
     #[clap(arg_required_else_help = true)]
     Push {
         /// The remote to target
+        #[clap(value_parser)]
         remote: String,
     },
     /// adds things
     #[clap(arg_required_else_help = true)]
     Add {
         /// Stuff to add
-        #[clap(required = true, parse(from_os_str))]
+        #[clap(required = true, value_parser)]
         path: Vec<PathBuf>,
     },
     Stash(Stash),
@@ -53,13 +55,19 @@ struct Stash {
 #[derive(Debug, Subcommand)]
 enum StashCommands {
     Push(StashPush),
-    Pop { stash: Option<String> },
-    Apply { stash: Option<String> },
+    Pop {
+        #[clap(value_parser)]
+        stash: Option<String>,
+    },
+    Apply {
+        #[clap(value_parser)]
+        stash: Option<String>,
+    },
 }
 
 #[derive(Debug, Args)]
 struct StashPush {
-    #[clap(short, long)]
+    #[clap(short, long, value_parser)]
     message: Option<String>,
 }
 
diff --git a/examples/tutorial_derive/01_quick.rs b/examples/tutorial_derive/01_quick.rs
index f840b8a9b2f..7bc55b2a62d 100644
--- a/examples/tutorial_derive/01_quick.rs
+++ b/examples/tutorial_derive/01_quick.rs
@@ -6,10 +6,11 @@ use clap::{Parser, Subcommand};
 #[clap(author, version, about, long_about = None)]
 struct Cli {
     /// Optional name to operate on
+    #[clap(value_parser)]
     name: Option<String>,
 
     /// Sets a custom config file
-    #[clap(short, long, parse(from_os_str), value_name = "FILE")]
+    #[clap(short, long, value_parser, value_name = "FILE")]
     config: Option<PathBuf>,
 
     /// Turn debugging information on
@@ -25,7 +26,7 @@ enum Commands {
     /// does testing things
     Test {
         /// lists test values
-        #[clap(short, long)]
+        #[clap(short, long, value_parser)]
         list: bool,
     },
 }
diff --git a/examples/tutorial_derive/02_app_settings.rs b/examples/tutorial_derive/02_app_settings.rs
index bccd353f602..87965cd36bb 100644
--- a/examples/tutorial_derive/02_app_settings.rs
+++ b/examples/tutorial_derive/02_app_settings.rs
@@ -6,9 +6,9 @@ use clap::{AppSettings, Parser};
 #[clap(allow_negative_numbers = true)]
 #[clap(global_setting(AppSettings::DeriveDisplayOrder))]
 struct Cli {
-    #[clap(long)]
+    #[clap(long, value_parser)]
     two: String,
-    #[clap(long)]
+    #[clap(long, value_parser)]
     one: String,
 }
 
diff --git a/examples/tutorial_derive/02_apps.rs b/examples/tutorial_derive/02_apps.rs
index 442e928a9f6..b97ce1eed6a 100644
--- a/examples/tutorial_derive/02_apps.rs
+++ b/examples/tutorial_derive/02_apps.rs
@@ -6,9 +6,9 @@ use clap::Parser;
 #[clap(version = "1.0")]
 #[clap(about = "Does awesome things", long_about = None)]
 struct Cli {
-    #[clap(long)]
+    #[clap(long, value_parser)]
     two: String,
-    #[clap(long)]
+    #[clap(long, value_parser)]
     one: String,
 }
 
diff --git a/examples/tutorial_derive/02_crate.rs b/examples/tutorial_derive/02_crate.rs
index 93f7888af31..df16468d0bb 100644
--- a/examples/tutorial_derive/02_crate.rs
+++ b/examples/tutorial_derive/02_crate.rs
@@ -3,9 +3,9 @@ use clap::Parser;
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
 struct Cli {
-    #[clap(long)]
+    #[clap(long, value_parser)]
     two: String,
-    #[clap(long)]
+    #[clap(long, value_parser)]
     one: String,
 }
 
diff --git a/examples/tutorial_derive/03_01_flag_bool.rs b/examples/tutorial_derive/03_01_flag_bool.rs
index 8b574b7481e..34c9a352a46 100644
--- a/examples/tutorial_derive/03_01_flag_bool.rs
+++ b/examples/tutorial_derive/03_01_flag_bool.rs
@@ -3,7 +3,7 @@ use clap::Parser;
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
 struct Cli {
-    #[clap(short, long)]
+    #[clap(short, long, value_parser)]
     verbose: bool,
 }
 
diff --git a/examples/tutorial_derive/03_02_option.rs b/examples/tutorial_derive/03_02_option.rs
index b09aadf20de..75b67afe753 100644
--- a/examples/tutorial_derive/03_02_option.rs
+++ b/examples/tutorial_derive/03_02_option.rs
@@ -3,7 +3,7 @@ use clap::Parser;
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
 struct Cli {
-    #[clap(short, long)]
+    #[clap(short, long, value_parser)]
     name: Option<String>,
 }
 
diff --git a/examples/tutorial_derive/03_03_positional.rs b/examples/tutorial_derive/03_03_positional.rs
index f7850ddccfe..7478951f1a8 100644
--- a/examples/tutorial_derive/03_03_positional.rs
+++ b/examples/tutorial_derive/03_03_positional.rs
@@ -3,6 +3,7 @@ use clap::Parser;
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
 struct Cli {
+    #[clap(value_parser)]
     name: Option<String>,
 }
 
diff --git a/examples/tutorial_derive/03_04_subcommands.rs b/examples/tutorial_derive/03_04_subcommands.rs
index 86cf444c21b..62a45a97eaf 100644
--- a/examples/tutorial_derive/03_04_subcommands.rs
+++ b/examples/tutorial_derive/03_04_subcommands.rs
@@ -11,7 +11,10 @@ struct Cli {
 #[derive(Subcommand)]
 enum Commands {
     /// Adds files to myapp
-    Add { name: Option<String> },
+    Add {
+        #[clap(value_parser)]
+        name: Option<String>,
+    },
 }
 
 fn main() {
diff --git a/examples/tutorial_derive/03_04_subcommands_alt.rs b/examples/tutorial_derive/03_04_subcommands_alt.rs
index 0a5b60682d4..b6124936c93 100644
--- a/examples/tutorial_derive/03_04_subcommands_alt.rs
+++ b/examples/tutorial_derive/03_04_subcommands_alt.rs
@@ -16,6 +16,7 @@ enum Commands {
 
 #[derive(Args)]
 struct Add {
+    #[clap(value_parser)]
     name: Option<String>,
 }
 
diff --git a/examples/tutorial_derive/03_05_default_values.rs b/examples/tutorial_derive/03_05_default_values.rs
index af4532bbc7d..10a1ec80880 100644
--- a/examples/tutorial_derive/03_05_default_values.rs
+++ b/examples/tutorial_derive/03_05_default_values.rs
@@ -3,7 +3,7 @@ use clap::Parser;
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
 struct Cli {
-    #[clap(default_value_t = String::from("alice"))]
+    #[clap(default_value_t = String::from("alice"), value_parser)]
     name: String,
 }
 
diff --git a/examples/tutorial_derive/04_01_enum.rs b/examples/tutorial_derive/04_01_enum.rs
index 3a2df391ffb..84b4cace495 100644
--- a/examples/tutorial_derive/04_01_enum.rs
+++ b/examples/tutorial_derive/04_01_enum.rs
@@ -4,7 +4,7 @@ use clap::{ArgEnum, Parser};
 #[clap(author, version, about, long_about = None)]
 struct Cli {
     /// What mode to run the program in
-    #[clap(arg_enum)]
+    #[clap(arg_enum, value_parser)]
     mode: Mode,
 }
 
diff --git a/examples/tutorial_derive/04_02_parse.rs b/examples/tutorial_derive/04_02_parse.rs
index 5f4cbadc045..6336a9cf155 100644
--- a/examples/tutorial_derive/04_02_parse.rs
+++ b/examples/tutorial_derive/04_02_parse.rs
@@ -4,8 +4,8 @@ use clap::Parser;
 #[clap(author, version, about, long_about = None)]
 struct Cli {
     /// Network port to use
-    #[clap(parse(try_from_str))]
-    port: usize,
+    #[clap(value_parser = clap::value_parser!(u16).range(1..))]
+    port: u16,
 }
 
 fn main() {
diff --git a/examples/tutorial_derive/04_02_validate.rs b/examples/tutorial_derive/04_02_validate.rs
index 434f40c869f..7dac79c9fb7 100644
--- a/examples/tutorial_derive/04_02_validate.rs
+++ b/examples/tutorial_derive/04_02_validate.rs
@@ -6,8 +6,8 @@ use clap::Parser;
 #[clap(author, version, about, long_about = None)]
 struct Cli {
     /// Network port to use
-    #[clap(parse(try_from_str=port_in_range))]
-    port: usize,
+    #[clap(value_parser = port_in_range)]
+    port: u16,
 }
 
 fn main() {
@@ -18,12 +18,12 @@ fn main() {
 
 const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
 
-fn port_in_range(s: &str) -> Result<usize, 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(port)
+        Ok(port as u16)
     } else {
         Err(format!(
             "Port not in range {}-{}",
diff --git a/examples/tutorial_derive/04_03_relations.rs b/examples/tutorial_derive/04_03_relations.rs
index f0e1e5913ba..32b85805658 100644
--- a/examples/tutorial_derive/04_03_relations.rs
+++ b/examples/tutorial_derive/04_03_relations.rs
@@ -9,7 +9,7 @@ use clap::{ArgGroup, Parser};
         ))]
 struct Cli {
     /// set version manually
-    #[clap(long, value_name = "VER")]
+    #[clap(long, value_name = "VER", value_parser)]
     set_ver: Option<String>,
 
     /// auto inc major
@@ -25,14 +25,14 @@ struct Cli {
     patch: bool,
 
     /// some regular input
-    #[clap(group = "input")]
+    #[clap(group = "input", value_parser)]
     input_file: Option<String>,
 
     /// some special input argument
-    #[clap(long, group = "input")]
+    #[clap(long, group = "input", value_parser)]
     spec_in: Option<String>,
 
-    #[clap(short, requires = "input")]
+    #[clap(short, requires = "input", value_parser)]
     config: Option<String>,
 }
 
diff --git a/examples/tutorial_derive/04_04_custom.rs b/examples/tutorial_derive/04_04_custom.rs
index a03345b8297..d163e20ef24 100644
--- a/examples/tutorial_derive/04_04_custom.rs
+++ b/examples/tutorial_derive/04_04_custom.rs
@@ -4,7 +4,7 @@ use clap::{CommandFactory, ErrorKind, Parser};
 #[clap(author, version, about, long_about = None)]
 struct Cli {
     /// set version manually
-    #[clap(long, value_name = "VER")]
+    #[clap(long, value_name = "VER", value_parser)]
     set_ver: Option<String>,
 
     /// auto inc major
@@ -20,13 +20,14 @@ struct Cli {
     patch: bool,
 
     /// some regular input
+    #[clap(value_parser)]
     input_file: Option<String>,
 
     /// some special input argument
-    #[clap(long)]
+    #[clap(long, value_parser)]
     spec_in: Option<String>,
 
-    #[clap(short)]
+    #[clap(short, value_parser)]
     config: Option<String>,
 }
 
diff --git a/examples/tutorial_derive/05_01_assert.rs b/examples/tutorial_derive/05_01_assert.rs
index 12fdba9b923..f5fa3f6c599 100644
--- a/examples/tutorial_derive/05_01_assert.rs
+++ b/examples/tutorial_derive/05_01_assert.rs
@@ -4,8 +4,8 @@ use clap::Parser;
 #[clap(author, version, about, long_about = None)]
 struct Cli {
     /// Network port to use
-    #[clap(parse(try_from_str))]
-    port: usize,
+    #[clap(value_parser)]
+    port: u16,
 }
 
 fn main() {
diff --git a/examples/tutorial_derive/README.md b/examples/tutorial_derive/README.md
index 706e76c09e7..01b122421b6 100644
--- a/examples/tutorial_derive/README.md
+++ b/examples/tutorial_derive/README.md
@@ -453,6 +453,12 @@ 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 parser can be used to improve the error messages or provide additional validation:
diff --git a/examples/typed-derive.rs b/examples/typed-derive.rs
index 237bbe15e8d..b99370780df 100644
--- a/examples/typed-derive.rs
+++ b/examples/typed-derive.rs
@@ -6,23 +6,23 @@ use std::error::Error;
 #[derive(Parser, Debug)]
 struct Args {
     /// Implicitly using `std::str::FromStr`
-    #[clap(short = 'O')]
+    #[clap(short = 'O', value_parser)]
     optimization: Option<usize>,
 
     /// Allow invalid UTF-8 paths
-    #[clap(short = 'I', parse(from_os_str), value_name = "DIR", value_hint = clap::ValueHint::DirPath)]
+    #[clap(short = 'I', value_parser, value_name = "DIR", value_hint = clap::ValueHint::DirPath)]
     include: Option<std::path::PathBuf>,
 
     /// Handle IP addresses
-    #[clap(long)]
+    #[clap(long, value_parser)]
     bind: Option<std::net::IpAddr>,
 
     /// Allow human-readable durations
-    #[clap(long)]
+    #[clap(long, value_parser)]
     sleep: Option<humantime::Duration>,
 
     /// Hand-written parser for tuples
-    #[clap(short = 'D', parse(try_from_str = parse_key_val), multiple_occurrences(true))]
+    #[clap(short = 'D', value_parser = parse_key_val::<String, i32>, multiple_occurrences(true))]
     defines: Vec<(String, i32)>,
 }
 
diff --git a/src/builder/value_parser.rs b/src/builder/value_parser.rs
index 02af4460272..edf57c623a2 100644
--- a/src/builder/value_parser.rs
+++ b/src/builder/value_parser.rs
@@ -1125,7 +1125,7 @@ impl<T: std::convert::TryFrom<i64>> RangedI64ValueParser<T> {
             std::ops::Bound::Unbounded => i64::MIN.to_string(),
         };
         result.push_str("..");
-        match self.bounds.0 {
+        match self.bounds.1 {
             std::ops::Bound::Included(i) => {
                 result.push('=');
                 result.push_str(&i.to_string());
diff --git a/src/derive.rs b/src/derive.rs
index 110af93727d..55be23bb13e 100644
--- a/src/derive.rs
+++ b/src/derive.rs
@@ -79,9 +79,9 @@ use std::ffi::OsString;
 pub trait Parser: FromArgMatches + CommandFactory + Sized {
     /// Parse from `std::env::args_os()`, exit on error
     fn parse() -> Self {
-        let matches = <Self as CommandFactory>::command().get_matches();
-        let res =
-            <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>);
+        let mut matches = <Self as CommandFactory>::command().get_matches();
+        let res = <Self as FromArgMatches>::from_arg_matches_mut(&mut matches)
+            .map_err(format_error::<Self>);
         match res {
             Ok(s) => s,
             Err(e) => {
@@ -94,8 +94,8 @@ pub trait Parser: FromArgMatches + CommandFactory + Sized {
 
     /// Parse from `std::env::args_os()`, return Err on error.
     fn try_parse() -> Result<Self, Error> {
-        let matches = <Self as CommandFactory>::command().try_get_matches()?;
-        <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>)
+        let mut matches = <Self as CommandFactory>::command().try_get_matches()?;
+        <Self as FromArgMatches>::from_arg_matches_mut(&mut matches).map_err(format_error::<Self>)
     }
 
     /// Parse from iterator, exit on error
@@ -104,9 +104,9 @@ pub trait Parser: FromArgMatches + CommandFactory + Sized {
         I: IntoIterator<Item = T>,
         T: Into<OsString> + Clone,
     {
-        let matches = <Self as CommandFactory>::command().get_matches_from(itr);
-        let res =
-            <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>);
+        let mut matches = <Self as CommandFactory>::command().get_matches_from(itr);
+        let res = <Self as FromArgMatches>::from_arg_matches_mut(&mut matches)
+            .map_err(format_error::<Self>);
         match res {
             Ok(s) => s,
             Err(e) => {
@@ -123,8 +123,8 @@ pub trait Parser: FromArgMatches + CommandFactory + Sized {
         I: IntoIterator<Item = T>,
         T: Into<OsString> + Clone,
     {
-        let matches = <Self as CommandFactory>::command().try_get_matches_from(itr)?;
-        <Self as FromArgMatches>::from_arg_matches(&matches).map_err(format_error::<Self>)
+        let mut matches = <Self as CommandFactory>::command().try_get_matches_from(itr)?;
+        <Self as FromArgMatches>::from_arg_matches_mut(&mut matches).map_err(format_error::<Self>)
     }
 
     /// Update from iterator, exit on error
@@ -133,8 +133,8 @@ pub trait Parser: FromArgMatches + CommandFactory + Sized {
         I: IntoIterator<Item = T>,
         T: Into<OsString> + Clone,
     {
-        let matches = <Self as CommandFactory>::command_for_update().get_matches_from(itr);
-        let res = <Self as FromArgMatches>::update_from_arg_matches(self, &matches)
+        let mut matches = <Self as CommandFactory>::command_for_update().get_matches_from(itr);
+        let res = <Self as FromArgMatches>::update_from_arg_matches_mut(self, &mut matches)
             .map_err(format_error::<Self>);
         if let Err(e) = res {
             // Since this is more of a development-time error, we aren't doing as fancy of a quit
@@ -149,8 +149,9 @@ pub trait Parser: FromArgMatches + CommandFactory + Sized {
         I: IntoIterator<Item = T>,
         T: Into<OsString> + Clone,
     {
-        let matches = <Self as CommandFactory>::command_for_update().try_get_matches_from(itr)?;
-        <Self as FromArgMatches>::update_from_arg_matches(self, &matches)
+        let mut matches =
+            <Self as CommandFactory>::command_for_update().try_get_matches_from(itr)?;
+        <Self as FromArgMatches>::update_from_arg_matches_mut(self, &mut matches)
             .map_err(format_error::<Self>)
     }
 
@@ -165,11 +166,11 @@ pub trait Parser: FromArgMatches + CommandFactory + Sized {
         <Self as CommandFactory>::command()
     }
 
-    /// Deprecated, `StructOpt::from_clap` replaced with [`FromArgMatches::from_arg_matches`] (derive as part of
+    /// Deprecated, `StructOpt::from_clap` replaced with [`FromArgMatches::from_arg_matches_mut`] (derive as part of
     /// [`Parser`])
     #[deprecated(
         since = "3.0.0",
-        note = "`StructOpt::from_clap` is replaced with `FromArgMatches::from_arg_matches` (derived as part of `Parser`)"
+        note = "`StructOpt::from_clap` is replaced with `FromArgMatches::from_arg_matches_mut` (derived as part of `Parser`)"
     )]
     #[doc(hidden)]
     fn from_clap(matches: &ArgMatches) -> Self {
@@ -232,7 +233,7 @@ pub trait Parser: FromArgMatches + CommandFactory + Sized {
 pub trait CommandFactory: Sized {
     /// Build a [`Command`] that can instantiate `Self`.
     ///
-    /// See [`FromArgMatches::from_arg_matches`] for instantiating `Self`.
+    /// See [`FromArgMatches::from_arg_matches_mut`] for instantiating `Self`.
     fn command<'help>() -> Command<'help> {
         #[allow(deprecated)]
         Self::into_app()
@@ -242,7 +243,7 @@ pub trait CommandFactory: Sized {
     fn into_app<'help>() -> Command<'help>;
     /// Build a [`Command`] that can update `self`.
     ///
-    /// See [`FromArgMatches::update_from_arg_matches`] for updating `self`.
+    /// See [`FromArgMatches::update_from_arg_matches_mut`] for updating `self`.
     fn command_for_update<'help>() -> Command<'help> {
         #[allow(deprecated)]
         Self::into_app_for_update()
@@ -293,8 +294,49 @@ pub trait FromArgMatches: Sized {
     /// ```
     fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error>;
 
+    /// Instantiate `Self` from [`ArgMatches`], parsing the arguments as needed.
+    ///
+    /// Motivation: If our application had two CLI options, `--name
+    /// <STRING>` and the flag `--debug`, we may create a struct as follows:
+    ///
+    #[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
+    #[cfg_attr(feature = "derive", doc = " ```no_run")]
+    /// struct Context {
+    ///     name: String,
+    ///     debug: bool
+    /// }
+    /// ```
+    ///
+    /// We then need to convert the `ArgMatches` that `clap` generated into our struct.
+    /// `from_arg_matches_mut` serves as the equivalent of:
+    ///
+    #[cfg_attr(not(feature = "derive"), doc = " ```ignore")]
+    #[cfg_attr(feature = "derive", doc = " ```no_run")]
+    /// # use clap::ArgMatches;
+    /// # struct Context {
+    /// #   name: String,
+    /// #   debug: bool
+    /// # }
+    /// impl From<ArgMatches> for Context {
+    ///    fn from(m: ArgMatches) -> Self {
+    ///        Context {
+    ///            name: m.value_of("name").unwrap().to_string(),
+    ///            debug: m.is_present("debug"),
+    ///        }
+    ///    }
+    /// }
+    /// ```
+    fn from_arg_matches_mut(matches: &mut ArgMatches) -> Result<Self, Error> {
+        Self::from_arg_matches(matches)
+    }
+
     /// Assign values from `ArgMatches` to `self`.
     fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error>;
+
+    /// Assign values from `ArgMatches` to `self`.
+    fn update_from_arg_matches_mut(&mut self, matches: &mut ArgMatches) -> Result<(), Error> {
+        self.update_from_arg_matches(matches)
+    }
 }
 
 /// Parse a set of arguments into a user-defined container.
@@ -480,9 +522,15 @@ impl<T: FromArgMatches> FromArgMatches for Box<T> {
     fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> {
         <T as FromArgMatches>::from_arg_matches(matches).map(Box::new)
     }
+    fn from_arg_matches_mut(matches: &mut ArgMatches) -> Result<Self, Error> {
+        <T as FromArgMatches>::from_arg_matches_mut(matches).map(Box::new)
+    }
     fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> {
         <T as FromArgMatches>::update_from_arg_matches(self, matches)
     }
+    fn update_from_arg_matches_mut(&mut self, matches: &mut ArgMatches) -> Result<(), Error> {
+        <T as FromArgMatches>::update_from_arg_matches_mut(self, matches)
+    }
 }
 
 impl<T: Args> Args for Box<T> {
diff --git a/src/output/help.rs b/src/output/help.rs
index f7401118821..e1ee78ac3dc 100644
--- a/src/output/help.rs
+++ b/src/output/help.rs
@@ -456,27 +456,23 @@ 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);
             if self.use_long
                 && !arg.is_hide_possible_values_set()
-                && arg
-                    .possible_vals
-                    .iter()
-                    .any(PossibleValue::should_show_help)
+                && possible_vals.iter().any(PossibleValue::should_show_help)
             {
-                debug!("Help::help: Found possible vals...{:?}", arg.possible_vals);
+                debug!("Help::help: Found possible vals...{:?}", possible_vals);
                 if !help.is_empty() {
                     self.none("\n\n")?;
                     self.spaces(spaces)?;
                 }
                 self.none("Possible values:")?;
-                let longest = arg
-                    .possible_vals
+                let longest = possible_vals
                     .iter()
                     .filter_map(|f| f.get_visible_quoted_name().map(|name| display_width(&name)))
                     .max()
                     .expect("Only called with possible value");
-                let help_longest = arg
-                    .possible_vals
+                let help_longest = possible_vals
                     .iter()
                     .filter_map(|f| f.get_visible_help().map(display_width))
                     .max()
@@ -494,7 +490,7 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
                     spaces + longest + DASH_SPACE + COLON_SPACE
                 };
 
-                for pv in arg.possible_vals.iter().filter(|pv| !pv.is_hide_set()) {
+                for pv in possible_vals.iter().filter(|pv| !pv.is_hide_set()) {
                     self.none("\n")?;
                     self.spaces(spaces)?;
                     self.none("- ")?;
@@ -662,19 +658,16 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
             }
         }
 
+        let possible_vals = get_possible_values(a);
         if !(a.is_hide_possible_values_set()
-            || a.possible_vals.is_empty()
+            || possible_vals.is_empty()
             || cfg!(feature = "unstable-v4")
                 && self.use_long
-                && a.possible_vals.iter().any(PossibleValue::should_show_help))
+                && possible_vals.iter().any(PossibleValue::should_show_help))
         {
-            debug!(
-                "Help::spec_vals: Found possible vals...{:?}",
-                a.possible_vals
-            );
+            debug!("Help::spec_vals: Found possible vals...{:?}", possible_vals);
 
-            let pvs = a
-                .possible_vals
+            let pvs = possible_vals
                 .iter()
                 .filter_map(PossibleValue::get_visible_quoted_name)
                 .collect::<Vec<_>>()
@@ -1148,6 +1141,21 @@ 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::*;


Test patch:
diff --git a/tests/derive/options.rs b/tests/derive/options.rs
index 26db1f2e7ce..a2c80494283 100644
--- a/tests/derive/options.rs
+++ b/tests/derive/options.rs
@@ -471,3 +471,29 @@ fn two_option_vec_types() {
         Opt::try_parse_from(&["test"]).unwrap()
     );
 }
+
+#[test]
+fn explicit_value_parser() {
+    #[derive(Parser, PartialEq, Debug)]
+    struct Opt {
+        #[clap(long, value_parser = clap::value_parser!(i32))]
+        arg: i32,
+    }
+    assert_eq!(
+        Opt { arg: 42 },
+        Opt::try_parse_from(&["test", "--arg", "42"]).unwrap()
+    );
+}
+
+#[test]
+fn implicit_value_parser() {
+    #[derive(Parser, PartialEq, Debug)]
+    struct Opt {
+        #[clap(long, value_parser)]
+        arg: i32,
+    }
+    assert_eq!(
+        Opt { arg: 42 },
+        Opt::try_parse_from(&["test", "--arg", "42"]).unwrap()
+    );
+}
diff --git a/tests/derive_ui/parse_with_value_parser.rs b/tests/derive_ui/parse_with_value_parser.rs
new file mode 100644
index 00000000000..ffbb6e848c5
--- /dev/null
+++ b/tests/derive_ui/parse_with_value_parser.rs
@@ -0,0 +1,21 @@
+// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use clap::Parser;
+
+#[derive(Parser, Debug)]
+#[clap(name = "basic")]
+struct Opt {
+    #[clap(parse(from_str), value_parser = clap::value_parser!(String))]
+    s: String,
+}
+
+fn main() {
+    let opt = Opt::parse();
+    println!("{:?}", opt);
+}
diff --git a/tests/derive_ui/parse_with_value_parser.stderr b/tests/derive_ui/parse_with_value_parser.stderr
new file mode 100644
index 00000000000..9b62635277b
--- /dev/null
+++ b/tests/derive_ui/parse_with_value_parser.stderr
@@ -0,0 +1,5 @@
+error: `value_parse` attribute conflicts with `parse` attribute
+  --> tests/derive_ui/parse_with_value_parser.rs:14:29
+   |
+14 |     #[clap(parse(from_str), value_parser = clap::value_parser!(String))]
+   |                             ^^^^^^^^^^^^