Repository: clap-rs/clap
Pull request #1894 (2020-05-02 18:34:40)

Eradicate warnings
<!--
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 it's own line.
-->
Also removes some unused finctions


Patch:
diff --git a/.travis.yml b/.travis.yml
index 246eabdd..567b8f2b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,7 +39,7 @@ jobs:
       script:
         - echo "Checking codebase with Clippy release `cargo clippy --version`."
         - cargo clippy --lib --features "yaml unstable" -- -D warnings
-        - cargo clippy --tests --examples --features "yaml unstable"
+        - cargo clippy --tests --examples --features "yaml unstable" -- -D warnings
         - cargo fmt -- --check
     - name: Coverage
       addons:
diff --git a/Cargo.toml b/Cargo.toml
index 016a7a47..d2603c78 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -63,7 +63,7 @@ bitflags              = "1.2"
 unicode-width         = "0.1"
 textwrap              = "0.11"
 indexmap              = "1.0"
-os_str_bytes = { version = "2.3", features = ["raw"] }
+os_str_bytes          = "2.2"
 vec_map               = "0.8"
 strsim      = { version = "0.10",  optional = true }
 yaml-rust   = { version = "0.4.1",  optional = true }
diff --git a/README.md b/README.md
index e8e7e464..8ab9eb45 100644
--- a/README.md
+++ b/README.md
@@ -152,7 +152,7 @@ Below are a few of the features which `clap` supports, full descriptions and usa
 * **Default Values**
   - Also supports conditional default values (i.e. a default which only applies if specific arguments are used, or specific values of those arguments)
 * **Automatic Version from Cargo.toml**: `clap` is fully compatible with Rust's `env!()` macro for automatically setting the version of your application to the version in your Cargo.toml. See [09_auto_version example](examples/09_auto_version.rs) for how to do this (Thanks to [jhelwig](https://github.com/jhelwig) for pointing this out)
-* **Typed Values**: You can use several convenience macros provided by `clap` to get typed values (i.e. `i32`, `u8`, etc.) from positional or option arguments so long as the type you request implements `std::str::FromStr` See the [12_typed_values example](examples/12_typed_values.rs). You can also use `clap`s `arg_enum!` macro to create an enum with variants that automatically implement `std::str::FromStr`. See [13_enum_values example](examples/13_enum_values.rs) for details
+* **Typed Values**: You can use several convenience macros provided by `clap` to get typed values (i.e. `i32`, `u8`, etc.) from positional or option arguments so long as the type you request implements `std::str::FromStr` See the [12_typed_values example](examples/12_typed_values.rs). You can also use `clap`s `arg_enum!` macro to create an enum with variants that automatically implement `std::str::FromStr`. See [13a_enum_values_automatic example](examples/13a_enum_values_automatic.rs) for details
 * **Suggestions**: Suggests corrections when the user enters a typo. For example, if you defined a `--myoption` argument, and the user mistakenly typed `--moyption` (notice `y` and `o` transposed), they would receive a `Did you mean '--myoption'?` error and exit gracefully. This also works for subcommands and flags. (Thanks to [Byron](https://github.com/Byron) for the implementation) (This feature can optionally be disabled, see 'Optional Dependencies / Features')
 * **Colorized Errors (Non Windows OS only)**: Error message are printed in in colored text (this feature can optionally be disabled, see 'Optional Dependencies / Features').
 * **Global Arguments**: Arguments can optionally be defined once, and be available to all child subcommands. There values will also be propagated up/down throughout all subcommands.
@@ -506,7 +506,7 @@ These videos slowly trickle out as I finish them and currently a work in progres
 
 ## How to Contribute
 
-Details on how to contribute can be found in the [CONTRIBUTING.md](CONTRIBUTING.md) file.
+Details on how to contribute can be found in the [CONTRIBUTING.md](.github/CONTRIBUTING.md) file.
 
 ### Compatibility Policy
 
diff --git a/clap_derive/src/derives/arg_enum.rs b/clap_derive/src/derives/arg_enum.rs
index 2b3c1f70..88f021a8 100644
--- a/clap_derive/src/derives/arg_enum.rs
+++ b/clap_derive/src/derives/arg_enum.rs
@@ -7,11 +7,8 @@
 // <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 crate::{
-    attrs::{Attrs, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
-    utils::Sp,
-};
+use crate::derives::attrs::{Attrs, Name, DEFAULT_CASING, DEFAULT_ENV_CASING};
+use crate::derives::spanned::Sp;
 
 use proc_macro2::{Span, TokenStream};
 use quote::quote;
diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/derives/attrs.rs
similarity index 97%
rename from clap_derive/src/attrs.rs
rename to clap_derive/src/derives/attrs.rs
index 860fcc2f..2220da16 100644
--- a/clap_derive/src/attrs.rs
+++ b/clap_derive/src/derives/attrs.rs
@@ -12,10 +12,7 @@
 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
 // MIT/Apache 2.0 license.
 
-use crate::{
-    parse::*,
-    utils::{process_doc_comment, Sp, Ty},
-};
+use super::{doc_comments::process_doc_comment, parse::*, spanned::Sp, ty::Ty};
 
 use std::env;
 
@@ -41,7 +38,6 @@ pub enum Kind {
     Subcommand(Sp<Ty>),
     Flatten,
     Skip(Option<Expr>),
-    ExternalSubcommand,
 }
 
 #[derive(Clone)]
@@ -300,11 +296,6 @@ impl Attrs {
                     self.set_kind(kind);
                 }
 
-                ExternalSubcommand(ident) => {
-                    let kind = Sp::new(Kind::ExternalSubcommand, ident.span());
-                    self.set_kind(kind);
-                }
-
                 Flatten(ident) => {
                     let kind = Sp::new(Kind::Flatten, ident.span());
                     self.set_kind(kind);
@@ -431,7 +422,7 @@ impl Attrs {
         match &*res.kind {
             Kind::Subcommand(_) => abort!(res.kind.span(), "subcommand is only allowed on fields"),
             Kind::Skip(_) => abort!(res.kind.span(), "skip is only allowed on fields"),
-            Kind::Arg(_) | Kind::Flatten | Kind::ExternalSubcommand => res,
+            Kind::Arg(_) | Kind::Flatten => res,
         }
     }
 
@@ -466,13 +457,6 @@ impl Attrs {
                     );
                 }
             }
-
-            Kind::ExternalSubcommand => {
-                abort! { res.kind.span(),
-                    "`external_subcommand` can be used only on enum variants"
-                }
-            }
-
             Kind::Subcommand(_) => {
                 if res.has_custom_parser {
                     abort!(
@@ -584,7 +568,7 @@ impl Attrs {
         } else {
             abort!(
                 kind.span(),
-                "`subcommand`, `flatten`, `external_subcommand` and `skip` cannot be used together"
+                "subcommand, flatten and skip cannot be used together"
             );
         }
     }
diff --git a/clap_derive/src/derives/clap.rs b/clap_derive/src/derives/clap.rs
index 9c454e11..b16163e8 100644
--- a/clap_derive/src/derives/clap.rs
+++ b/clap_derive/src/derives/clap.rs
@@ -12,11 +12,7 @@
 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
 // MIT/Apache 2.0 license.
 
-use crate::{
-    derives::{arg_enum, from_argmatches, into_app, subcommand},
-    dummies,
-};
-
+use super::{arg_enum, dummies, from_argmatches, into_app, subcommand};
 use proc_macro2::TokenStream;
 use proc_macro_error::abort_call_site;
 use quote::quote;
diff --git a/clap_derive/src/utils/doc_comments.rs b/clap_derive/src/derives/doc_comments.rs
similarity index 99%
rename from clap_derive/src/utils/doc_comments.rs
rename to clap_derive/src/derives/doc_comments.rs
index 49c794c9..7f8822f8 100644
--- a/clap_derive/src/utils/doc_comments.rs
+++ b/clap_derive/src/derives/doc_comments.rs
@@ -3,8 +3,7 @@
 //! #[derive(Clap)] works in terms of "paragraphs". Paragraph is a sequence of
 //! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines.
 
-use crate::attrs::Method;
-
+use super::attrs::Method;
 use quote::{format_ident, quote};
 use std::iter;
 
diff --git a/clap_derive/src/dummies.rs b/clap_derive/src/derives/dummies.rs
similarity index 100%
rename from clap_derive/src/dummies.rs
rename to clap_derive/src/derives/dummies.rs
diff --git a/clap_derive/src/derives/from_argmatches.rs b/clap_derive/src/derives/from_argmatches.rs
index 06dc125d..df7b2713 100644
--- a/clap_derive/src/derives/from_argmatches.rs
+++ b/clap_derive/src/derives/from_argmatches.rs
@@ -12,14 +12,10 @@
 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
 // MIT/Apache 2.0 license.
 use proc_macro2::TokenStream;
-use proc_macro_error::abort;
 use quote::{quote, quote_spanned};
 use syn::{punctuated::Punctuated, spanned::Spanned, Field, Ident, Token, Type};
 
-use crate::{
-    attrs::{Attrs, Kind, ParserKind},
-    utils::{sub_type, subty_if_name, Ty},
-};
+use super::{sub_type, subty_if_name, Attrs, Kind, ParserKind, Ty};
 
 pub fn gen_for_struct(
     struct_name: &Ident,
@@ -93,12 +89,7 @@ pub fn gen_constructor(
         );
         let field_name = field.ident.as_ref().unwrap();
         let kind = attrs.kind();
-        match &*kind {
-            Kind::ExternalSubcommand => {
-                abort! { kind.span(),
-                    "`external_subcommand` can be used only on enum variants"
-                }
-            }
+        match &*attrs.kind() {
             Kind::Subcommand(ty) => {
                 let subcmd_type = match (**ty, sub_type(&field.ty)) {
                     (Ty::Option, Some(sub_type)) => sub_type,
diff --git a/clap_derive/src/derives/into_app.rs b/clap_derive/src/derives/into_app.rs
index a006c364..38a64204 100644
--- a/clap_derive/src/derives/into_app.rs
+++ b/clap_derive/src/derives/into_app.rs
@@ -19,9 +19,9 @@ use proc_macro_error::abort;
 use quote::{quote, quote_spanned};
 use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Field, Ident, Token, Type};
 
-use crate::{
-    attrs::{Attrs, GenOutput, Kind, Name, ParserKind, DEFAULT_CASING, DEFAULT_ENV_CASING},
-    utils::{sub_type, subty_if_name, Sp, Ty},
+use super::{
+    spanned::Sp, sub_type, subty_if_name, ty::Ty, Attrs, GenOutput, Kind, Name, ParserKind,
+    DEFAULT_CASING, DEFAULT_ENV_CASING,
 };
 
 pub fn gen_for_struct(
@@ -179,7 +179,7 @@ pub fn gen_app_augmentation(
         );
         let kind = attrs.kind();
         match &*kind {
-            Kind::Subcommand(_) | Kind::Skip(_) | Kind::ExternalSubcommand => None,
+            Kind::Subcommand(_) | Kind::Skip(_) => None,
             Kind::Flatten => {
                 let ty = &field.ty;
                 Some(quote_spanned! { kind.span()=>
diff --git a/clap_derive/src/derives/mod.rs b/clap_derive/src/derives/mod.rs
index 8fe7aed2..e3afdc33 100644
--- a/clap_derive/src/derives/mod.rs
+++ b/clap_derive/src/derives/mod.rs
@@ -12,9 +12,20 @@
 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
 // MIT/Apache 2.0 license.
 mod arg_enum;
+pub mod attrs;
 mod clap;
+mod doc_comments;
+mod dummies;
 mod from_argmatches;
 mod into_app;
+pub mod parse;
+pub mod spanned;
 mod subcommand;
+pub mod ty;
 
+pub use self::attrs::{
+    Attrs, CasingStyle, GenOutput, Kind, Name, Parser, ParserKind, DEFAULT_CASING,
+    DEFAULT_ENV_CASING,
+};
 pub use self::clap::derive_clap;
+pub use self::ty::{sub_type, subty_if_name, Ty};
diff --git a/clap_derive/src/parse.rs b/clap_derive/src/derives/parse.rs
similarity index 98%
rename from clap_derive/src/parse.rs
rename to clap_derive/src/derives/parse.rs
index 04fc2453..bbdc4777 100644
--- a/clap_derive/src/parse.rs
+++ b/clap_derive/src/derives/parse.rs
@@ -19,7 +19,6 @@ pub enum ClapAttr {
     ArgEnum(Ident),
     Subcommand(Ident),
     VerbatimDocComment(Ident),
-    ExternalSubcommand(Ident),
 
     // ident [= "string literal"]
     About(Ident, Option<LitStr>),
@@ -171,7 +170,7 @@ impl Parse for ClapAttr {
                 "flatten" => Ok(Flatten(name)),
                 "arg_enum" => Ok(ArgEnum(name)),
                 "subcommand" => Ok(Subcommand(name)),
-                "external_subcommand" => Ok(ExternalSubcommand(name)),
+
                 "verbatim_doc_comment" => Ok(VerbatimDocComment(name)),
 
                 "default_value" => Ok(DefaultValue(name, None)),
diff --git a/clap_derive/src/utils/spanned.rs b/clap_derive/src/derives/spanned.rs
similarity index 97%
rename from clap_derive/src/utils/spanned.rs
rename to clap_derive/src/derives/spanned.rs
index c8ba8062..adde02dd 100644
--- a/clap_derive/src/utils/spanned.rs
+++ b/clap_derive/src/derives/spanned.rs
@@ -81,7 +81,7 @@ impl<T: AsRef<str>> AsRef<str> for Sp<T> {
 impl<T: ToTokens> ToTokens for Sp<T> {
     fn to_tokens(&self, stream: &mut TokenStream) {
         // this is the simplest way out of correct ones to change span on
-        // arbitrary token tree I could come up with
+        // arbitrary token tree I can come up with
         let tt = self.val.to_token_stream().into_iter().map(|mut tt| {
             tt.set_span(self.span.clone());
             tt
diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs
index ccee7c6c..739bfc73 100644
--- a/clap_derive/src/derives/subcommand.rs
+++ b/clap_derive/src/derives/subcommand.rs
@@ -1,8 +1,5 @@
-use crate::{
-    attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
-    derives::{from_argmatches, into_app},
-    utils::{is_simple_ty, subty_if_name, Sp},
-};
+use crate::derives::attrs::{Attrs, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING};
+use crate::derives::{from_argmatches, into_app, spanned::Sp};
 
 use proc_macro2::{Ident, Span, TokenStream};
 use proc_macro_error::{abort, abort_call_site};
@@ -49,72 +46,62 @@ fn gen_augment_subcommands(
 ) -> TokenStream {
     use syn::Fields::*;
 
-    let subcommands: Vec<_> = variants
-        .iter()
-        .map(|variant| {
-            let attrs = Attrs::from_struct(
-                variant.span(),
-                &variant.attrs,
-                Name::Derived(variant.ident.clone()),
-                parent_attribute.casing(),
-                parent_attribute.env_casing(),
-            );
-            let kind = attrs.kind();
-
-            match &*kind {
-                Kind::ExternalSubcommand => {
-                    quote_spanned! { kind.span()=>
-                        let app = app.setting(::clap::AppSettings::AllowExternalSubcommands);
+    let subcommands = variants.iter().map(|variant| {
+        let attrs = Attrs::from_struct(
+            variant.span(),
+            &variant.attrs,
+            Name::Derived(variant.ident.clone()),
+            parent_attribute.casing(),
+            parent_attribute.env_casing(),
+        );
+        let kind = attrs.kind();
+        match &*kind {
+            Kind::Flatten => match variant.fields {
+                Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
+                    let ty = &unnamed[0];
+                    quote! {
+                        let app = <#ty as ::clap::Subcommand>::augment_subcommands(app);
                     }
                 }
-
-                Kind::Flatten => match variant.fields {
+                _ => abort!(
+                    variant,
+                    "`flatten` is usable only with single-typed tuple variants"
+                ),
+            },
+
+            _ => {
+                let app_var = Ident::new("subcommand", Span::call_site());
+                let arg_block = match variant.fields {
+                    Named(ref fields) => {
+                        into_app::gen_app_augmentation(&fields.named, &app_var, &attrs)
+                    }
+                    Unit => quote!( #app_var ),
                     Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
                         let ty = &unnamed[0];
-                        quote! {
-                            let app = <#ty as ::clap::Subcommand>::augment_subcommands(app);
-                        }
-                    }
-                    _ => abort!(
-                        variant,
-                        "`flatten` is usable only with single-typed tuple variants"
-                    ),
-                },
-
-                _ => {
-                    let app_var = Ident::new("subcommand", Span::call_site());
-                    let arg_block = match variant.fields {
-                        Named(ref fields) => {
-                            into_app::gen_app_augmentation(&fields.named, &app_var, &attrs)
-                        }
-                        Unit => quote!( #app_var ),
-                        Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
-                            let ty = &unnamed[0];
-                            quote_spanned! { ty.span()=>
-                                {
-                                    <#ty as ::clap::IntoApp>::augment_clap(#app_var)
-                                }
+                        quote_spanned! { ty.span()=>
+                            {
+                                <#ty as ::clap::IntoApp>::augment_clap(#app_var)
                             }
                         }
-                        Unnamed(..) => {
-                            abort!(variant, "non single-typed tuple enums are not supported")
-                        }
-                    };
-
-                    let name = attrs.cased_name();
-                    let from_attrs = attrs.top_level_methods();
-                    let version = attrs.version();
-                    quote! {
-                        let app = app.subcommand({
-                            let #app_var = ::clap::App::new(#name);
-                            let #app_var = #arg_block;
-                            #app_var#from_attrs#version
-                        });
                     }
+                    Unnamed(..) => {
+                        abort!(variant, "non single-typed tuple enums are not supported")
+                    }
+                };
+
+                let name = attrs.cased_name();
+                let from_attrs = attrs.top_level_methods();
+                let version = attrs.version();
+                quote! {
+                    let app = app.subcommand({
+                        let #app_var = ::clap::App::new(#name);
+                        let #app_var = #arg_block;
+                        #app_var#from_attrs#version
+                    });
                 }
             }
-        })
-        .collect();
+        }
+    });
 
     let app_methods = parent_attribute.top_level_methods();
     let version = parent_attribute.version();
@@ -133,12 +120,9 @@ fn gen_from_subcommand(
     parent_attribute: &Attrs,
 ) -> TokenStream {
     use syn::Fields::*;
-
-    let mut ext_subcmd = None;
-
     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
         .iter()
-        .filter_map(|variant| {
+        .map(|variant| {
             let attrs = Attrs::from_struct(
                 variant.span(),
                 &variant.attrs,
@@ -146,56 +130,7 @@ fn gen_from_subcommand(
                 parent_attribute.casing(),
                 parent_attribute.env_casing(),
             );
-
-            if let Kind::ExternalSubcommand = &*attrs.kind() {
-                if ext_subcmd.is_some() {
-                    abort!(
-                        attrs.kind().span(),
-                        "Only one variant can be marked with `external_subcommand`, \
-                         this is the second"
-                    );
-                }
-
-                let ty = match variant.fields {
-                    Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
-
-                    _ => abort!(
-                        variant,
-                        "The enum variant marked with `external_attribute` must be \
-                         a single-typed tuple, and the type must be either `Vec<String>` \
-                         or `Vec<OsString>`."
-                    ),
-                };
-
-                let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") {
-                    Some(subty) => {
-                        if is_simple_ty(subty, "String") {
-                            (
-                                subty.span(),
-                                quote!(::std::string::String),
-                                quote!(values_of),
-                            )
-                        } else {
-                            (
-                                subty.span(),
-                                quote!(::std::ffi::OsString),
-                                quote!(values_of_os),
-                            )
-                        }
-                    }
-
-                    None => abort!(
-                        ty.span(),
-                        "The type must be either `Vec<String>` or `Vec<OsString>` \
-                         to be used with `external_subcommand`."
-                    ),
-                };
-
-                ext_subcmd = Some((span, &variant.ident, str_ty, values_of));
-                None
-            } else {
-                Some((variant, attrs))
-            }
+            (variant, attrs)
         })
         .partition(|(_, attrs)| {
             let kind = attrs.kind();
@@ -242,45 +177,16 @@ fn gen_from_subcommand(
         }
     });
 
-    let wildcard = match ext_subcmd {
-        Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
-            ("", ::std::option::Option::None) => ::std::option::Option::None,
-
-            (external, Some(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)
-                    )
-                    .collect::<::std::vec::Vec<_>>()
-                ))
-            }
-
-            (external, None) => {
-                ::std::option::Option::Some(#name::#var_name({
-                    let mut v = ::std::vec::Vec::with_capacity(1);
-                    v.push(#str_ty::from(external));
-                    v
-                }))
-            }
-        },
-
-        None => quote!(_ => None),
-    };
-
     quote! {
         fn from_subcommand<'b>(
             name: &'b str,
             sub: Option<&'b ::clap::ArgMatches>) -> Option<Self>
         {
             match (name, sub) {
-                #( #match_arms, )*
+                #( #match_arms ),*,
                 other => {
-                    #( #child_subcommands )else*
-
-                    match other {
-                        #wildcard
-                    }
+                    #( #child_subcommands )*;
+                    None
                 }
             }
         }
diff --git a/clap_derive/src/utils/ty.rs b/clap_derive/src/derives/ty.rs
similarity index 97%
rename from clap_derive/src/utils/ty.rs
rename to clap_derive/src/derives/ty.rs
index c2cec83f..b046ac6d 100644
--- a/clap_derive/src/utils/ty.rs
+++ b/clap_derive/src/derives/ty.rs
@@ -84,7 +84,7 @@ pub fn subty_if_name<'a>(ty: &'a syn::Type, name: &str) -> Option<&'a syn::Type>
     subty_if(ty, |seg| seg.ident == name)
 }
 
-pub fn is_simple_ty(ty: &syn::Type, name: &str) -> bool {
+fn is_simple_ty(ty: &syn::Type, name: &str) -> bool {
     only_last_segment(ty)
         .map(|segment| {
             if let PathArguments::None = segment.arguments {
diff --git a/clap_derive/src/lib.rs b/clap_derive/src/lib.rs
index 68244122..4bb373ae 100644
--- a/clap_derive/src/lib.rs
+++ b/clap_derive/src/lib.rs
@@ -20,11 +20,7 @@ extern crate proc_macro;
 
 use proc_macro_error::proc_macro_error;
 
-mod attrs;
 mod derives;
-mod dummies;
-mod parse;
-mod utils;
 
 // /// It is required to have this seperate and specificly defined.
 // #[proc_macro_derive(ArgEnum, attributes(case_sensitive))]
diff --git a/clap_derive/src/utils/mod.rs b/clap_derive/src/utils/mod.rs
deleted file mode 100644
index 89691332..00000000
--- a/clap_derive/src/utils/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-mod doc_comments;
-mod spanned;
-mod ty;
-
-pub use self::{
-    doc_comments::process_doc_comment,
-    spanned::Sp,
-    ty::{is_simple_ty, sub_type, subty_if_name, Ty},
-};
diff --git a/examples/01a_quick_example.rs b/examples/01a_quick_example.rs
index 2311bf80..548bf4fd 100644
--- a/examples/01a_quick_example.rs
+++ b/examples/01a_quick_example.rs
@@ -58,7 +58,7 @@ fn main() {
         0 => println!("Debug mode is off"),
         1 => println!("Debug mode is kind of on"),
         2 => println!("Debug mode is on"),
-        3 | _ => println!("Don't be crazy"),
+        _ => println!("Don't be crazy"),
     }
 
     // You can check for the existence of subcommands, and if found use their
diff --git a/examples/01b_quick_example.rs b/examples/01b_quick_example.rs
index dd2d924c..3eac6a3d 100644
--- a/examples/01b_quick_example.rs
+++ b/examples/01b_quick_example.rs
@@ -76,7 +76,7 @@ fn main() {
         0 => println!("Debug mode is off"),
         1 => println!("Debug mode is kind of on"),
         2 => println!("Debug mode is on"),
-        3 | _ => println!("Don't be crazy"),
+        _ => println!("Don't be crazy"),
     }
 
     // You can check for the existence of subcommands, and if found use their
diff --git a/examples/01c_quick_example.rs b/examples/01c_quick_example.rs
index 73cacb9d..937dfb57 100644
--- a/examples/01c_quick_example.rs
+++ b/examples/01c_quick_example.rs
@@ -58,7 +58,7 @@ fn main() {
         0 => println!("Debug mode is off"),
         1 => println!("Debug mode is kind of on"),
         2 => println!("Debug mode is on"),
-        3 | _ => println!("Don't be crazy"),
+        _ => println!("Don't be crazy"),
     }
 
     // You can information about subcommands by requesting their matches by name
diff --git a/examples/05_flag_args.rs b/examples/05_flag_args.rs
index e800d77d..ee1b02e0 100644
--- a/examples/05_flag_args.rs
+++ b/examples/05_flag_args.rs
@@ -47,7 +47,7 @@ fn main() {
         0 => println!("Nothing is awesome"),
         1 => println!("Some things are awesome"),
         2 => println!("Lots of things are awesome"),
-        3 | _ => println!("EVERYTHING is awesome!"),
+        _ => println!("EVERYTHING is awesome!"),
     }
 
     // Continued program logic goes here...
diff --git a/examples/18_builder_macro.rs b/examples/18_builder_macro.rs
index 38bc2973..01ce0ab6 100644
--- a/examples/18_builder_macro.rs
+++ b/examples/18_builder_macro.rs
@@ -63,7 +63,7 @@ fn main() {
         0 => println!("Debug mode is off"),
         1 => println!("Debug mode is kind of on"),
         2 => println!("Debug mode is on"),
-        3 | _ => println!("Don't be crazy"),
+        _ => println!("Don't be crazy"),
     }
 
     // You can check for the existence of subcommands, and if found use their
diff --git a/src/build/app/mod.rs b/src/build/app/mod.rs
index 8a38feff..704f1283 100644
--- a/src/build/app/mod.rs
+++ b/src/build/app/mod.rs
@@ -31,11 +31,12 @@ use crate::{
 
 // FIXME (@CreepySkeleton): some of these variants are never constructed
 #[derive(Clone, Debug, PartialEq, Eq)]
-#[allow(unused)]
 pub(crate) enum Propagation {
     To(Id),
     Full,
+    #[cfg_attr(not(test), allow(unused))]
     NextLevel,
+    #[allow(unused)]
     None,
 }
 
@@ -1116,7 +1117,7 @@ impl<'b> App<'b> {
         let p = Parser::new(self);
         let mut c = Colorizer::new(false, p.color_help());
 
-        Help::new(HelpWriter::Buffer(&mut c), &p, false).write_help()?;
+        Help::new(HelpWriter::Buffer(&mut c), &p, true).write_help()?;
 
         Ok(c.print()?)
     }
@@ -1582,6 +1583,7 @@ impl<'b> App<'b> {
         }
     }
 
+    #[cfg(debug_assertions)]
     fn two_args_of<F>(&self, condition: F) -> Option<(&Arg, &Arg)>
     where
         F: Fn(&Arg<'_>) -> bool,
@@ -1600,6 +1602,7 @@ impl<'b> App<'b> {
 
     // Perform some expensive assertions on the Parser itself
     #[allow(clippy::cognitive_complexity)]
+    #[cfg(debug_assertions)]
     fn _debug_asserts(&self) {
         debug!("App::_debug_asserts");
 
@@ -2006,6 +2009,7 @@ impl<'b> App<'b> {
             .any(|sc| !sc.is_set(AppSettings::Hidden))
     }
 
+    #[cfg(debug_assertions)]
     pub(crate) fn id_exists(&self, id: &Id) -> bool {
         self.args.args.iter().any(|x| x.id == *id) || self.groups.iter().any(|x| x.id == *id)
     }
diff --git a/src/macros.rs b/src/macros.rs
index 82c18f47..537dde03 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -113,7 +113,7 @@ macro_rules! crate_authors {
         use std::cell::Cell;
 
         #[allow(missing_copy_implementations)]
-        #[allow(dead_code)]
+        #[allow(unused)]
         struct CargoAuthors {
             authors: Cell<Option<&'static str>>,
             __private_field: (),
diff --git a/src/output/help.rs b/src/output/help.rs
index fddb3389..d1654037 100644
--- a/src/output/help.rs
+++ b/src/output/help.rs
@@ -145,11 +145,6 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
         write_method!(self, msg, warning)
     }
 
-    #[allow(dead_code)]
-    fn error(&mut self, msg: &str) -> io::Result<()> {
-        write_method!(self, msg, error)
-    }
-
     fn none(&mut self, msg: &str) -> io::Result<()> {
         write_method!(self, msg, none)
     }
diff --git a/src/parse/arg_matcher.rs b/src/parse/arg_matcher.rs
index 9dda0a8d..e4a520be 100644
--- a/src/parse/arg_matcher.rs
+++ b/src/parse/arg_matcher.rs
@@ -86,13 +86,6 @@ impl ArgMatcher {
         self.0.args.swap_remove(arg);
     }
 
-    #[allow(dead_code)]
-    pub(crate) fn remove_all(&mut self, args: &[Id]) {
-        for arg in args {
-            self.0.args.swap_remove(arg);
-        }
-    }
-
     pub(crate) fn insert(&mut self, name: &Id) {
         self.0.args.insert(name.clone(), MatchedArg::new());
     }
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 56bc6362..7fff483d 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -381,7 +381,6 @@ where
         let has_args = self.has_args();
 
         let mut subcmd_name: Option<String> = None;
-        let mut external_subcommand = false;
         let mut needs_val_of: ParseResult = ParseResult::NotFound;
         let mut pos_counter = 1;
         let mut replace: Option<&[&str]> = None;
@@ -678,8 +677,6 @@ where
                     matches: sc_m.into_inner(),
                 });
 
-                external_subcommand = true;
-
                 break;
             } else if !((self.is_set(AS::AllowLeadingHyphen)
                 || self.is_set(AS::AllowNegativeNumbers))
@@ -721,30 +718,28 @@ where
             }
         }
 
-        if !external_subcommand {
-            if let Some(ref pos_sc_name) = subcmd_name {
-                let sc_name = find_subcmd!(self.app, *pos_sc_name)
-                    .expect(INTERNAL_ERROR_MSG)
-                    .name
-                    .clone();
-                self.parse_subcommand(&sc_name, matcher, it)?;
-            } else if self.is_set(AS::SubcommandRequired) {
-                let bn = self.app.bin_name.as_ref().unwrap_or(&self.app.name);
-                return Err(ClapError::missing_subcommand(
-                    bn,
-                    &Usage::new(self).create_usage_with_title(&[]),
-                    self.app.color(),
-                )?);
-            } else if self.is_set(AS::SubcommandRequiredElseHelp) {
-                debug!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
-                let message = self.write_help_err()?;
-                return Err(ClapError {
-                    cause: String::new(),
-                    message,
-                    kind: ErrorKind::MissingArgumentOrSubcommand,
-                    info: None,
-                });
-            }
+        if let Some(ref pos_sc_name) = subcmd_name {
+            let sc_name = find_subcmd!(self.app, *pos_sc_name)
+                .expect(INTERNAL_ERROR_MSG)
+                .name
+                .clone();
+            self.parse_subcommand(&sc_name, matcher, it)?;
+        } else if self.is_set(AS::SubcommandRequired) {
+            let bn = self.app.bin_name.as_ref().unwrap_or(&self.app.name);
+            return Err(ClapError::missing_subcommand(
+                bn,
+                &Usage::new(self).create_usage_with_title(&[]),
+                self.app.color(),
+            )?);
+        } else if self.is_set(AS::SubcommandRequiredElseHelp) {
+            debug!("Parser::get_matches_with: SubcommandRequiredElseHelp=true");
+            let message = self.write_help_err()?;
+            return Err(ClapError {
+                cause: String::new(),
+                message,
+                kind: ErrorKind::MissingArgumentOrSubcommand,
+                info: None,
+            });
         }
 
         self.remove_overrides(matcher);
@@ -817,6 +812,9 @@ where
     // Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
     fn possible_subcommand(&self, arg_os: &ArgStr<'_>) -> Option<&str> {
         debug!("Parser::possible_subcommand: arg={:?}", arg_os);
+        fn starts(h: &str, n: &ArgStr<'_>) -> bool {
+            h.is_char_boundary(n.len()) && h.as_bytes().starts_with(n.as_raw_bytes())
+        }
 
         if self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound) {
             return None;
@@ -824,7 +822,7 @@ where
 
         if self.is_set(AS::InferSubcommands) {
             let v = sc_names!(self.app)
-                .filter(|s| arg_os.is_prefix_of(s))
+                .filter(|s| starts(s, &*arg_os))
                 .collect::<Vec<_>>();
 
             if v.len() == 1 {
diff --git a/src/util/argstr.rs b/src/util/argstr.rs
index 805fc0b1..098712dc 100644
--- a/src/util/argstr.rs
+++ b/src/util/argstr.rs
@@ -1,12 +1,12 @@
 use std::{
     borrow::Cow,
     ffi::{OsStr, OsString},
-    fmt::{self, Debug},
     str,
 };
 
-use os_str_bytes::{raw, OsStrBytes, OsStringBytes};
+use os_str_bytes::{OsStrBytes, OsStringBytes};
 
+#[derive(Debug)]
 pub(crate) struct ArgStr<'a>(Cow<'a, [u8]>);
 
 impl<'a> ArgStr<'a> {
@@ -18,10 +18,6 @@ impl<'a> ArgStr<'a> {
         self.0.starts_with(s.as_bytes())
     }
 
-    pub(crate) fn is_prefix_of(&self, s: &str) -> bool {
-        raw::starts_with(s, &self.0)
-    }
-
     fn to_borrowed(&'a self) -> Self {
         Self(Cow::Borrowed(&self.0))
     }
@@ -106,7 +102,6 @@ impl<'a> ArgStr<'a> {
         }
     }
 
-    #[allow(dead_code)]
     pub(crate) fn as_raw_bytes(&self) -> &[u8] {
         &self.0
     }
@@ -132,12 +127,6 @@ impl<'a> ArgStr<'a> {
     }
 }
 
-impl<'a> Debug for ArgStr<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{:?}", self.to_string_lossy())
-    }
-}
-
 impl<'a> PartialEq<str> for ArgStr<'a> {
     fn eq(&self, other: &str) -> bool {
         self.0 == other.as_bytes()


Test patch:
diff --git a/clap_derive/tests/subcommands.rs b/clap_derive/tests/subcommands.rs
index 9fbb6323..f012b3a5 100644
--- a/clap_derive/tests/subcommands.rs
+++ b/clap_derive/tests/subcommands.rs
@@ -161,96 +161,6 @@ fn test_tuple_commands() {
     assert!(!output.contains("Not shown"));
 }
 
-#[test]
-fn external_subcommand() {
-    #[derive(Debug, PartialEq, Clap)]
-    struct Opt {
-        #[clap(subcommand)]
-        sub: Subcommands,
-    }
-
-    #[derive(Debug, PartialEq, Clap)]
-    enum Subcommands {
-        Add,
-        Remove,
-        #[clap(external_subcommand)]
-        Other(Vec<String>),
-    }
-
-    assert_eq!(
-        Opt::parse_from(&["test", "add"]),
-        Opt {
-            sub: Subcommands::Add
-        }
-    );
-
-    assert_eq!(
-        Opt::parse_from(&["test", "remove"]),
-        Opt {
-            sub: Subcommands::Remove
-        }
-    );
-
-    assert!(Opt::try_parse_from(&["test"]).is_err());
-
-    assert_eq!(
-        Opt::try_parse_from(&["test", "git", "status"]).unwrap(),
-        Opt {
-            sub: Subcommands::Other(vec!["git".into(), "status".into()])
-        }
-    );
-}
-
-#[test]
-fn external_subcommand_os_string() {
-    use std::ffi::OsString;
-
-    #[derive(Debug, PartialEq, Clap)]
-    struct Opt {
-        #[clap(subcommand)]
-        sub: Subcommands,
-    }
-
-    #[derive(Debug, PartialEq, Clap)]
-    enum Subcommands {
-        #[clap(external_subcommand)]
-        Other(Vec<OsString>),
-    }
-
-    assert_eq!(
-        Opt::try_parse_from(&["test", "git", "status"]).unwrap(),
-        Opt {
-            sub: Subcommands::Other(vec!["git".into(), "status".into()])
-        }
-    );
-
-    assert!(Opt::try_parse_from(&["test"]).is_err());
-}
-
-#[test]
-fn external_subcommand_optional() {
-    #[derive(Debug, PartialEq, Clap)]
-    struct Opt {
-        #[clap(subcommand)]
-        sub: Option<Subcommands>,
-    }
-
-    #[derive(Debug, PartialEq, Clap)]
-    enum Subcommands {
-        #[clap(external_subcommand)]
-        Other(Vec<String>),
-    }
-
-    assert_eq!(
-        Opt::try_parse_from(&["test", "git", "status"]).unwrap(),
-        Opt {
-            sub: Some(Subcommands::Other(vec!["git".into(), "status".into()]))
-        }
-    );
-
-    assert_eq!(Opt::try_parse_from(&["test"]).unwrap(), Opt { sub: None });
-}
-
 // #[test]
 // #[ignore] // FIXME (@CreepySkeleton)
 // fn enum_in_enum_subsubcommand() {
diff --git a/clap_derive/tests/ui/external_subcommand_misuse.rs b/clap_derive/tests/ui/external_subcommand_misuse.rs
deleted file mode 100644
index 19d24258..00000000
--- a/clap_derive/tests/ui/external_subcommand_misuse.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use clap::Clap;
-
-#[derive(Clap, Debug)]
-struct Opt {
-    #[clap(external_subcommand)]
-    field: String,
-}
-
-fn main() {
-    let _ = Opt::parse();
-}
diff --git a/clap_derive/tests/ui/external_subcommand_misuse.stderr b/clap_derive/tests/ui/external_subcommand_misuse.stderr
deleted file mode 100644
index 14540920..00000000
--- a/clap_derive/tests/ui/external_subcommand_misuse.stderr
+++ /dev/null
@@ -1,5 +0,0 @@
-error: `external_subcommand` can be used only on enum variants
- --> $DIR/external_subcommand_misuse.rs:5:12
-  |
-5 |     #[clap(external_subcommand)]
-  |            ^^^^^^^^^^^^^^^^^^^
diff --git a/clap_derive/tests/ui/external_subcommand_wrong_type.rs b/clap_derive/tests/ui/external_subcommand_wrong_type.rs
deleted file mode 100644
index e8233ef6..00000000
--- a/clap_derive/tests/ui/external_subcommand_wrong_type.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use clap::Clap;
-use std::ffi::CString;
-
-#[derive(Clap, Debug)]
-enum Opt {
-    #[clap(external_subcommand)]
-    Other(Vec<CString>),
-}
-
-#[derive(Clap, Debug)]
-enum Opt2 {
-    #[clap(external_subcommand)]
-    Other(String),
-}
-
-#[derive(Clap, Debug)]
-enum Opt3 {
-    #[clap(external_subcommand)]
-    Other { a: String },
-}
-
-fn main() {
-    let _ = Opt::parse();
-    let _ = Opt2::parse();
-    let _ = Opt3::parse();
-}
diff --git a/clap_derive/tests/ui/external_subcommand_wrong_type.stderr b/clap_derive/tests/ui/external_subcommand_wrong_type.stderr
deleted file mode 100644
index d4ff9026..00000000
--- a/clap_derive/tests/ui/external_subcommand_wrong_type.stderr
+++ /dev/null
@@ -1,21 +0,0 @@
-error: The type must be either `Vec<String>` or `Vec<OsString>` to be used with `external_subcommand`.
-  --> $DIR/external_subcommand_wrong_type.rs:13:11
-   |
-13 |     Other(String),
-   |           ^^^^^^
-
-error: The enum variant marked with `external_attribute` must be a single-typed tuple, and the type must be either `Vec<String>` or `Vec<OsString>`.
-  --> $DIR/external_subcommand_wrong_type.rs:18:5
-   |
-18 | /     #[clap(external_subcommand)]
-19 | |     Other { a: String },
-   | |_______________________^
-
-error[E0308]: mismatched types
- --> $DIR/external_subcommand_wrong_type.rs:7:15
-  |
-7 |     Other(Vec<CString>),
-  |               ^^^^^^^ expected struct `std::ffi::CString`, found struct `std::ffi::OsString`
-  |
-  = note: expected struct `std::vec::Vec<std::ffi::CString>`
-             found struct `std::vec::Vec<std::ffi::OsString>`
diff --git a/clap_derive/tests/ui/multiple_external_subcommand.rs b/clap_derive/tests/ui/multiple_external_subcommand.rs
deleted file mode 100644
index 3cd342a3..00000000
--- a/clap_derive/tests/ui/multiple_external_subcommand.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use clap::Clap;
-
-#[derive(Clap, Debug)]
-struct Opt {
-    #[clap(subcommand)]
-    cmd: Command,
-}
-
-#[derive(Clap, Debug)]
-enum Command {
-    #[clap(external_subcommand)]
-    Run(Vec<String>),
-
-    #[clap(external_subcommand)]
-    Other(Vec<String>),
-}
-
-fn main() {
-    let opt = Opt::parse();
-    println!("{:?}", opt);
-}
diff --git a/clap_derive/tests/ui/multiple_external_subcommand.stderr b/clap_derive/tests/ui/multiple_external_subcommand.stderr
deleted file mode 100644
index 270fd36d..00000000
--- a/clap_derive/tests/ui/multiple_external_subcommand.stderr
+++ /dev/null
@@ -1,5 +0,0 @@
-error: Only one variant can be marked with `external_subcommand`, this is the second
-  --> $DIR/multiple_external_subcommand.rs:14:12
-   |
-14 |     #[clap(external_subcommand)]
-   |            ^^^^^^^^^^^^^^^^^^^
diff --git a/clap_derive/tests/ui/skip_flatten.stderr b/clap_derive/tests/ui/skip_flatten.stderr
index 328dc9c3..e5230c0d 100644
--- a/clap_derive/tests/ui/skip_flatten.stderr
+++ b/clap_derive/tests/ui/skip_flatten.stderr
@@ -1,4 +1,4 @@
-error: `subcommand`, `flatten`, `external_subcommand` and `skip` cannot be used together
+error: subcommand, flatten and skip cannot be used together
   --> $DIR/skip_flatten.rs:17:18
    |
 17 |     #[clap(skip, flatten)]
diff --git a/clap_derive/tests/ui/skip_subcommand.stderr b/clap_derive/tests/ui/skip_subcommand.stderr
index aeea1029..fff8796a 100644
--- a/clap_derive/tests/ui/skip_subcommand.stderr
+++ b/clap_derive/tests/ui/skip_subcommand.stderr
@@ -1,4 +1,4 @@
-error: `subcommand`, `flatten`, `external_subcommand` and `skip` cannot be used together
+error: subcommand, flatten and skip cannot be used together
   --> $DIR/skip_subcommand.rs:17:24
    |
 17 |     #[clap(subcommand, skip)]
diff --git a/clap_derive/tests/ui/subcommand_and_flatten.stderr b/clap_derive/tests/ui/subcommand_and_flatten.stderr
index c9fcc431..b509b737 100644
--- a/clap_derive/tests/ui/subcommand_and_flatten.stderr
+++ b/clap_derive/tests/ui/subcommand_and_flatten.stderr
@@ -1,4 +1,4 @@
-error: `subcommand`, `flatten`, `external_subcommand` and `skip` cannot be used together
+error: subcommand, flatten and skip cannot be used together
   --> $DIR/subcommand_and_flatten.rs:16:24
    |
 16 |     #[clap(subcommand, flatten)]
diff --git a/tests/app_settings.rs b/tests/app_settings.rs
index 50920567..cbc50a32 100644
--- a/tests/app_settings.rs
+++ b/tests/app_settings.rs
@@ -470,8 +470,8 @@ fn unset_settings() {
     let m = m
         .unset_setting(AppSettings::AllowInvalidUtf8)
         .unset_setting(AppSettings::ColorAuto);
-    assert!(!m.is_set(AppSettings::AllowInvalidUtf8), "{:#?}");
-    assert!(!m.is_set(AppSettings::ColorAuto));
+    assert!(!m.is_set(AppSettings::AllowInvalidUtf8), "{:#?}", m);
+    assert!(!m.is_set(AppSettings::ColorAuto), "{:#?}", m);
 }
 
 #[test]