Repository: clap-rs/clap
Pull request #2228 (2020-11-28 00:50:40)

Update MSRV to 1.46
<!--
If your PR closes some issues, please write `Closes #XXXX`
where `XXXX` is the number of the issue you want to fix.
Each issue goes on its own line.
-->



Patch:
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index 3bfcb543..fbaf9c9d 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -14,7 +14,7 @@ stages:
     jobs:
       - job:
         variables:
-          rust: 1.42.0
+          rust: 1.46.0
         strategy:
           matrix:
             MinGW 32-bit:
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 908bcd4d..5fa456b5 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -4,7 +4,16 @@ on:
     branches: [master]
     types: [opened, reopened, synchronize]
 jobs:
-  Benchmark:
+  prepare:
+    name: Prepare
+    runs-on: ubuntu-latest
+    steps:
+      - uses: fkirc/skip-duplicate-actions@master
+        with:
+          github_token: ${{ github.token }}
+  benchmark:
+    name: Benchmark
+    needs: [prepare]
     runs-on: ubuntu-latest
     steps:
       - name: Install rust
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ae92069c..91b1008a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,80 +1,68 @@
 name: CI
 on:
   push:
-    branches: [staging, trying]
+    branches: [master, staging, trying]
   pull_request:
     branches: [master]
     types: [opened, reopened, synchronize]
 jobs:
   ci:
     name: CI
-    needs: [test, test-release, lint, coverage]
+    needs: [test, wasm, lint]
     runs-on: ubuntu-latest
     steps:
       - name: Done
         run: exit 0
+  prepare:
+    name: Prepare
+    runs-on: ubuntu-latest
+    steps:
+      - uses: fkirc/skip-duplicate-actions@master
+        with:
+          github_token: ${{ github.token }}
   test:
     name: Tests
+    needs: [prepare]
     strategy:
       fail-fast: false
       matrix:
-        include:
-          - os: macos-latest
-            target: x86_64-apple-darwin
-            rust: 1.42.0
-            features: none
-          - os: macos-latest
+        rust: [1.46.0, stable, beta]
+        os: [windows-latest, macos-latest, ubuntu-latest]
+        target:
+          - i686-pc-windows-msvc
+          - x86_64-pc-windows-msvc
+          - x86_64-unknown-linux-gnu
+          - i686-unknown-linux-gnu
+          - x86_64-apple-darwin
+        features:
+          - none
+          - all
+          - release
+        exclude:
+          - features: release
+            rust: stable
+          - features: release
+            rust: beta
+          - os: windows-latest
             target: x86_64-apple-darwin
-            rust: 1.42.0
-            features: all
-          - os: ubuntu-latest
-            target: x86_64-unknown-linux-gnu
-            rust: 1.42.0
-            features: none
-          - os: ubuntu-latest
-            target: x86_64-unknown-linux-gnu
-            rust: 1.42.0
-            features: all
-          - os: ubuntu-latest
-            target: i686-unknown-linux-gnu
-            rust: 1.42.0
-            features: none
-          - os: ubuntu-latest
-            target: i686-unknown-linux-gnu
-            rust: 1.42.0
-            features: all
           - os: windows-latest
-            target: i686-pc-windows-msvc
-            rust: 1.42.0
-            features: none
+            target: x86_64-unknown-linux-gnu
           - os: windows-latest
+            target: i686-unknown-linux-gnu
+          - os: macos-latest
             target: i686-pc-windows-msvc
-            rust: 1.42.0
-            features: all
-          - os: windows-latest
-            target: x86_64-pc-windows-msvc
-            rust: 1.42.0
-            features: none
-          - os: windows-latest
+          - os: macos-latest
             target: x86_64-pc-windows-msvc
-            rust: 1.42.0
-            features: all
-          - os: ubuntu-latest
+          - os: macos-latest
             target: x86_64-unknown-linux-gnu
-            rust: stable
-            features: none
+          - os: macos-latest
+            target: i686-unknown-linux-gnu
           - os: ubuntu-latest
-            target: x86_64-unknown-linux-gnu
-            rust: stable
-            features: all
+            target: i686-pc-windows-msvc
           - os: ubuntu-latest
-            target: x86_64-unknown-linux-gnu
-            rust: beta
-            features: none
+            target: x86_64-pc-windows-msvc
           - os: ubuntu-latest
-            target: x86_64-unknown-linux-gnu
-            rust: beta
-            features: all
+            target: x86_64-apple-darwin
     runs-on: ${{ matrix.os }}
     steps:
       - name: Install rust
@@ -100,25 +88,16 @@ jobs:
         if: matrix.features == 'all'
         with:
           command: test
-          args: --target ${{ matrix.target }} --features "yaml unstable"
-  test-release:
-    name: Release Profile Tests
-    runs-on: ubuntu-latest
-    steps:
-      - name: Install rust
-        uses: actions-rs/toolchain@v1
-        with:
-          toolchain: 1.42.0
-          override: true
-      - name: Checkout
-        uses: actions/checkout@v2
-      - name: Test
+          args: --target ${{ matrix.target }} --features "wrap_help yaml regex unstable"
+      - name: Test release
         uses: actions-rs/cargo@v1
+        if: matrix.features == 'release'
         with:
           command: test
-          args: --release --features "yaml unstable"
+          args: --target ${{ matrix.target }} --features "wrap_help yaml regex unstable" --release
   nightly:
     name: Nightly Tests
+    needs: [prepare]
     runs-on: ubuntu-latest
     steps:
       - name: Install rust
@@ -138,9 +117,10 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           command: test
-          args: --features "yaml unstable"
+          args: --features "wrap_help yaml regex unstable"
   wasm:
     name: Wasm Check
+    needs: [prepare]
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
@@ -150,7 +130,7 @@ jobs:
       - name: Install rust
         uses: actions-rs/toolchain@v1
         with:
-          toolchain: 1.42.0
+          toolchain: 1.46.0
           target: ${{ matrix.target }}
           override: true
       - name: Checkout
@@ -159,9 +139,10 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           command: check
-          args: --target ${{ matrix.target }} --features "yaml unstable"
+          args: --target ${{ matrix.target }} --features "yaml regex unstable"
   lint:
     name: Linting (fmt + clippy)
+    needs: [prepare]
     runs-on: ubuntu-latest
     steps:
       - name: Install rust
@@ -182,7 +163,7 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           command: clippy
-          args: --features "yaml unstable" -- -D warnings
+          args: --features "wrap_help yaml regex unstable" -- -D warnings
       - name: Format check
         uses: actions-rs/cargo@v1
         with:
@@ -190,6 +171,7 @@ jobs:
           args: -- --check
   coverage:
     name: Coverage
+    needs: [prepare]
     continue-on-error: true
     runs-on: ubuntu-latest
     steps:
@@ -211,7 +193,7 @@ jobs:
         uses: actions-rs/cargo@v1
         with:
           command: tarpaulin
-          args: --features "yaml unstable" --timeout 300 --out Lcov --output-dir coverage
+          args: --features "wrap_help yaml regex unstable" --timeout 300 --out Lcov --output-dir coverage
       - name: Coveralls
         uses: coverallsapp/github-action@master
         with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 90eb4ab7..e957d038 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ TODO: `cargo`, `std` features
 
 
 #### Minimum Required Rust
-* As of this release, `clap` requires `rustc 1.42.0` or greater.
+* As of this release, `clap` requires `rustc 1.46.0` or greater.
 
 #### BREAKING CHANGES
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7aac6adf..f8038875 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,7 +9,7 @@ Another really great way to help is if you find an interesting, or helpful way i
 To test with all features both enabled and disabled, you can run these commands:
 
 ```sh
-$ cargo test --features "yaml unstable"
+$ cargo test --features "wrap_help yaml regex unstable"
 ```
 
 Alternatively, if you have [`just`](https://github.com/casey/just) installed you can run the prebuilt recipes. *Not* using `just` is perfectly fine as well, it simply bundles commands automatically.
@@ -39,8 +39,7 @@ During the CI process `clap` runs against many different lints using [`clippy`](
 In order to check the code for lints and to format it run either:
 
 ```sh
-$ cargo clippy --lib --features "yaml unstable" -- -D warnings
-$ cargo clippy --tests --examples --features "yaml unstable"
+$ cargo clippy --features "wrap_help yaml regex unstable" -- -D warnings
 $ cargo fmt -- --check
 
 # Or
diff --git a/README.md b/README.md
index aff22124..cc0d0679 100644
--- a/README.md
+++ b/README.md
@@ -457,9 +457,10 @@ features = [ "suggestions", "color" ]
 
 #### Opt-in features
 
+* **"wrap_help"**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`)
 * **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
+* **"regex"**: Enables regex validators. (builds dependency `regex`)
 * **"unstable"**: Enables unstable `clap` features that may change from release to release
-* **"wrap_help"**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`)
 
 ### More Information
 
@@ -518,7 +519,7 @@ The following is a list of the minimum required version of Rust to compile `clap
 
 |  clap  |  MSRV  |
 | :----: | :----: |
-| >=3.0  | 1.42.0 |
+| >=3.0  | 1.46.0 |
 | >=2.21 | 1.24.0 |
 | >=2.2  | 1.12.0 |
 | >=2.1  | 1.6.0  |
diff --git a/clap_derive/src/derives/from_arg_matches.rs b/clap_derive/src/derives/from_arg_matches.rs
index a214d1f2..747f6185 100644
--- a/clap_derive/src/derives/from_arg_matches.rs
+++ b/clap_derive/src/derives/from_arg_matches.rs
@@ -43,11 +43,11 @@ pub fn gen_for_struct(
         )]
         #[deny(clippy::correctness)]
         impl ::clap::FromArgMatches for #struct_name {
-            fn from_arg_matches(matches: &::clap::ArgMatches) -> Self {
+            fn from_arg_matches(arg_matches: &::clap::ArgMatches) -> Self {
                 #struct_name #constructor
             }
 
-            fn update_from_arg_matches(&mut self, matches: &::clap::ArgMatches) {
+            fn update_from_arg_matches(&mut self, arg_matches: &::clap::ArgMatches) {
                 #updater
             }
         }
@@ -69,11 +69,11 @@ pub fn gen_for_enum(name: &Ident) -> TokenStream {
         )]
         #[deny(clippy::correctness)]
         impl ::clap::FromArgMatches for #name {
-            fn from_arg_matches(matches: &::clap::ArgMatches) -> Self {
-                <#name as ::clap::Subcommand>::from_subcommand(matches.subcommand()).unwrap()
+            fn from_arg_matches(arg_matches: &::clap::ArgMatches) -> Self {
+                <#name as ::clap::Subcommand>::from_subcommand(arg_matches.subcommand()).unwrap()
             }
-            fn update_from_arg_matches(&mut self, matches: &::clap::ArgMatches) {
-                <#name as ::clap::Subcommand>::update_from_subcommand(self, matches.subcommand());
+            fn update_from_arg_matches(&mut self, arg_matches: &::clap::ArgMatches) {
+                <#name as ::clap::Subcommand>::update_from_subcommand(self, arg_matches.subcommand());
             }
         }
     }
@@ -136,11 +136,11 @@ fn gen_parsers(
         Ty::Bool => {
             if update.is_some() {
                 quote_spanned! { ty.span()=>
-                    *#field_name || matches.is_present(#name)
+                    *#field_name || arg_matches.is_present(#name)
                 }
             } else {
                 quote_spanned! { ty.span()=>
-                    matches.is_present(#name)
+                    arg_matches.is_present(#name)
                 }
             }
         }
@@ -153,22 +153,22 @@ fn gen_parsers(
             }
 
             quote_spanned! { ty.span()=>
-                matches.#value_of(#name)
+                arg_matches.#value_of(#name)
                     .map(#parse)
             }
         }
 
         Ty::OptionOption => quote_spanned! { ty.span()=>
-            if matches.is_present(#name) {
-                Some(matches.#value_of(#name).map(#parse))
+            if arg_matches.is_present(#name) {
+                Some(arg_matches.#value_of(#name).map(#parse))
             } else {
                 None
             }
         },
 
         Ty::OptionVec => quote_spanned! { ty.span()=>
-            if matches.is_present(#name) {
-                Some(matches.#values_of(#name)
+            if arg_matches.is_present(#name) {
+                Some(arg_matches.#values_of(#name)
                      .map(|v| v.map(#parse).collect())
                      .unwrap_or_else(Vec::new))
             } else {
@@ -184,18 +184,18 @@ fn gen_parsers(
             }
 
             quote_spanned! { ty.span()=>
-                matches.#values_of(#name)
+                arg_matches.#values_of(#name)
                     .map(|v| v.map(#parse).collect())
                     .unwrap_or_else(Vec::new)
             }
         }
 
         Ty::Other if occurrences => quote_spanned! { ty.span()=>
-            #parse(matches.#value_of(#name))
+            #parse(arg_matches.#value_of(#name))
         },
 
         Ty::Other if flag => quote_spanned! { ty.span()=>
-            #parse(matches.is_present(#name))
+            #parse(arg_matches.is_present(#name))
         },
 
         Ty::Other => {
@@ -204,7 +204,7 @@ fn gen_parsers(
             }
 
             quote_spanned! { ty.span()=>
-                matches.#value_of(#name)
+                arg_matches.#value_of(#name)
                     .map(#parse)
                     .unwrap()
             }
@@ -213,7 +213,7 @@ fn gen_parsers(
 
     if let Some(access) = update {
         quote_spanned! { field.span()=>
-            if matches.is_present(#name) {
+            if arg_matches.is_present(#name) {
                 #access
                 *#field_name = #field_value
             }
@@ -249,14 +249,14 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att
                 };
                 quote_spanned! { kind.span()=>
                     #field_name: {
-                        <#subcmd_type as ::clap::Subcommand>::from_subcommand(matches.subcommand())
+                        <#subcmd_type as ::clap::Subcommand>::from_subcommand(arg_matches.subcommand())
                         #unwrapper
                     }
                 }
             }
 
             Kind::Flatten => quote_spanned! { kind.span()=>
-                #field_name: ::clap::FromArgMatches::from_arg_matches(matches)
+                #field_name: ::clap::FromArgMatches::from_arg_matches(arg_matches)
             },
 
             Kind::Skip(val) => match val {
@@ -329,7 +329,7 @@ pub fn gen_updater(
 
                 quote_spanned! { kind.span()=>
                     {
-                        let subcmd = matches.subcommand();
+                        let subcmd = arg_matches.subcommand();
                         #access
                         #updater
                     }
@@ -338,7 +338,7 @@ pub fn gen_updater(
 
             Kind::Flatten => quote_spanned! { kind.span()=> {
                     #access
-                    ::clap::FromArgMatches::update_from_arg_matches(#field_name, matches);
+                    ::clap::FromArgMatches::update_from_arg_matches(#field_name, arg_matches);
                 }
             },
 
diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs
index 5631a85b..a7b365b0 100644
--- a/clap_derive/src/derives/subcommand.rs
+++ b/clap_derive/src/derives/subcommand.rs
@@ -240,13 +240,13 @@ fn gen_from_subcommand(
             Unit => quote!(),
             Unnamed(ref fields) if fields.unnamed.len() == 1 => {
                 let ty = &fields.unnamed[0];
-                quote!( ( <#ty as ::clap::FromArgMatches>::from_arg_matches(matches) ) )
+                quote!( ( <#ty as ::clap::FromArgMatches>::from_arg_matches(arg_matches) ) )
             }
             Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
         };
 
         quote! {
-            Some((#sub_name, matches)) => {
+            Some((#sub_name, arg_matches)) => {
                 Some(#name :: #variant_name #constructor_block)
             }
         }
@@ -273,11 +273,11 @@ fn gen_from_subcommand(
         Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
             None => ::std::option::Option::None,
 
-            Some((external, matches)) => {
+            Some((external, arg_matches)) => {
                 ::std::option::Option::Some(#name::#var_name(
                     ::std::iter::once(#str_ty::from(external))
                     .chain(
-                        matches.#values_of("").into_iter().flatten().map(#str_ty::from)
+                        arg_matches.#values_of("").into_iter().flatten().map(#str_ty::from)
                     )
                     .collect::<::std::vec::Vec<_>>()
                 ))
@@ -361,7 +361,8 @@ fn gen_update_from_subcommand(
                     (
                         quote!((ref mut arg)),
                         quote!(::clap::FromArgMatches::update_from_arg_matches(
-                            arg, matches
+                            arg,
+                            arg_matches
                         )),
                     )
                 } else {
@@ -384,7 +385,7 @@ fn gen_update_from_subcommand(
                 (
                     quote!((ref mut arg)),
                     quote! {
-                        <#ty as ::clap::Subcommand>::update_from_subcommand(arg, Some((name, matches)));
+                        <#ty as ::clap::Subcommand>::update_from_subcommand(arg, Some((name, arg_matches)));
                     },
                 )
             }
@@ -403,11 +404,11 @@ fn gen_update_from_subcommand(
             &mut self,
             subcommand: Option<(&str, &::clap::ArgMatches)>
         ) {
-            if let Some((name, matches)) = subcommand {
+            if let Some((name, arg_matches)) = subcommand {
                 match (name, self) {
                     #( #subcommands ),*
                     #( #child_subcommands ),*
-                    (_, s) => if let Some(sub) = <Self as ::clap::Subcommand>::from_subcommand(Some((name, matches))) {
+                    (_, s) => if let Some(sub) = <Self as ::clap::Subcommand>::from_subcommand(Some((name, arg_matches))) {
                         *s = sub;
                     }
                 }
diff --git a/clap_generate/src/generators/shells/fish.rs b/clap_generate/src/generators/shells/fish.rs
index 592620ac..5e556b4a 100644
--- a/clap_generate/src/generators/shells/fish.rs
+++ b/clap_generate/src/generators/shells/fish.rs
@@ -80,7 +80,7 @@ fn gen_fish_inner(root_command: &str, app: &App, buffer: &mut String) {
         template.push_str(value_completion(option).as_str());
 
         buffer.push_str(template.as_str());
-        buffer.push_str("\n");
+        buffer.push('\n');
     }
 
     for flag in Fish::flags(app) {
@@ -105,7 +105,7 @@ fn gen_fish_inner(root_command: &str, app: &App, buffer: &mut String) {
         }
 
         buffer.push_str(template.as_str());
-        buffer.push_str("\n");
+        buffer.push('\n');
     }
 
     for subcommand in app.get_subcommands() {
@@ -119,7 +119,7 @@ fn gen_fish_inner(root_command: &str, app: &App, buffer: &mut String) {
         }
 
         buffer.push_str(template.as_str());
-        buffer.push_str("\n");
+        buffer.push('\n');
     }
 
     // generate options of subcommands
diff --git a/justfile b/justfile
index 32f849b7..0ab5459d 100644
--- a/justfile
+++ b/justfile
@@ -5,7 +5,7 @@ debug TESTG TEST="":
 	cargo test --test {{TESTG}} --features debug -- {{TEST}}
 
 run-tests:
-	cargo test --features "yaml unstable"
+	cargo test --features "wrap_help yaml regex unstable"
 
 @bench:
 	cargo bench
@@ -13,8 +13,7 @@ run-tests:
 @lint:
 	rustup component add clippy
 	rustup component add rustfmt
-	cargo clippy --lib --features "yaml unstable" -- -D warnings
-	cargo clippy --tests --examples --features "yaml unstable"
+	cargo clippy --features "wrap_help yaml regex unstable" -- -D warnings
 	cargo fmt -- --check
 
 clean:
diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs
index c172922e..3101b334 100644
--- a/src/build/arg/mod.rs
+++ b/src/build/arg/mod.rs
@@ -4410,6 +4410,7 @@ impl Arg<'_> {
                 assert!(
                     self.is_set(ArgSettings::MultipleValues),
                     "Argument '{}' uses hint CommandWithArguments and must accept multiple values",
+                    self.name
                 )
             }
         }
diff --git a/src/output/usage.rs b/src/output/usage.rs
index c05f372e..f746cbba 100644
--- a/src/output/usage.rs
+++ b/src/output/usage.rs
@@ -134,10 +134,10 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
                     usage.push_str(" [-- <");
                 }
                 usage.push_str(&*pos.name_no_brackets());
-                usage.push_str(">");
+                usage.push('>');
                 usage.push_str(pos.multiple_str());
                 if !req {
-                    usage.push_str("]");
+                    usage.push(']');
                 }
             }
         }
@@ -155,25 +155,25 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
 
                     usage.push_str(" <");
                     usage.push_str(placeholder);
-                    usage.push_str(">");
+                    usage.push('>');
                 } else {
                     usage.push_str("\n    ");
                     usage.push_str(&*name);
 
                     usage.push_str(" <");
                     usage.push_str(placeholder);
-                    usage.push_str(">");
+                    usage.push('>');
                 }
             } else if self.p.is_set(AS::SubcommandRequired)
                 || self.p.is_set(AS::SubcommandRequiredElseHelp)
             {
                 usage.push_str(" <");
                 usage.push_str(placeholder);
-                usage.push_str(">");
+                usage.push('>');
             } else {
                 usage.push_str(" [");
                 usage.push_str(placeholder);
-                usage.push_str("]");
+                usage.push(']');
             }
         }
         usage.shrink_to_fit();
@@ -204,7 +204,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> {
         if self.p.is_set(AS::SubcommandRequired) {
             usage.push_str(" <");
             usage.push_str(self.p.app.subcommand_placeholder.unwrap_or("SUBCOMMAND"));
-            usage.push_str(">");
+            usage.push('>');
         }
         usage.shrink_to_fit();
         usage


Test patch:
diff --git a/tests/help.rs b/tests/help.rs
index 5d26fd7f..1d1d33cd 100644
--- a/tests/help.rs
+++ b/tests/help.rs
@@ -892,7 +892,7 @@ fn wrapped_help() {
     static WRAPPED_HELP: &str = "test 
 
 USAGE:
-    rust_test.exe [FLAGS]
+    test [FLAGS]
 
 FLAGS:
     -a, --all
@@ -951,7 +951,7 @@ fn unwrapped_help() {
     static UNWRAPPED_HELP: &str = "test 
 
 USAGE:
-    rust_test.exe [FLAGS]
+    test [FLAGS]
 
 FLAGS:
     -a, --all              Also do versioning for private crates