1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 //! This crate is custom derive for `StructOpt`. It should not be used
10 //! directly. See [structopt documentation](https://docs.rs/structopt)
11 //! for the usage of `#[derive(StructOpt)]`.
12 
13 #![allow(clippy::large_enum_variant)]
14 // FIXME: remove when and if our MSRV hits 1.42
15 #![allow(clippy::match_like_matches_macro)]
16 #![forbid(unsafe_code)]
17 
18 extern crate proc_macro;
19 
20 mod attrs;
21 mod doc_comments;
22 mod parse;
23 mod spanned;
24 mod ty;
25 
26 use crate::{
27     attrs::{Attrs, CasingStyle, Kind, Name, ParserKind},
28     spanned::Sp,
29     ty::{is_simple_ty, sub_type, subty_if_name, Ty},
30 };
31 
32 use proc_macro2::{Span, TokenStream};
33 use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy};
34 use quote::{format_ident, quote, quote_spanned};
35 use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *};
36 
37 /// Default casing style for generated arguments.
38 const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
39 
40 /// Default casing style for environment variables
41 const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
42 
43 /// Output for the `gen_xxx()` methods were we need more than a simple stream of tokens.
44 ///
45 /// The output of a generation method is not only the stream of new tokens but also the attribute
46 /// information of the current element. These attribute information may contain valuable information
47 /// for any kind of child arguments.
48 struct GenOutput {
49     tokens: TokenStream,
50     attrs: Attrs,
51 }
52 
53 /// Generates the `StructOpt` impl.
54 #[proc_macro_derive(StructOpt, attributes(structopt))]
55 #[proc_macro_error]
structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream56 pub fn structopt(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
57     let input: DeriveInput = syn::parse(input).unwrap();
58     let gen = impl_structopt(&input);
59     gen.into()
60 }
61 
62 /// Generate a block of code to add arguments/subcommands corresponding to
63 /// the `fields` to an app.
gen_augmentation( fields: &Punctuated<Field, Comma>, app_var: &Ident, parent_attribute: &Attrs, ) -> TokenStream64 fn gen_augmentation(
65     fields: &Punctuated<Field, Comma>,
66     app_var: &Ident,
67     parent_attribute: &Attrs,
68 ) -> TokenStream {
69     let mut subcmds = fields.iter().filter_map(|field| {
70         let attrs = Attrs::from_field(
71             field,
72             Some(parent_attribute),
73             parent_attribute.casing(),
74             parent_attribute.env_casing(),
75         );
76         let kind = attrs.kind();
77         if let Kind::Subcommand(ty) = &*kind {
78             let subcmd_type = match (**ty, sub_type(&field.ty)) {
79                 (Ty::Option, Some(sub_type)) => sub_type,
80                 _ => &field.ty,
81             };
82             let required = if **ty == Ty::Option {
83                 quote!()
84             } else {
85                 quote_spanned! { kind.span()=>
86                     let #app_var = #app_var.setting(
87                         ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
88                     );
89                 }
90             };
91 
92             let span = field.span();
93             let ts = quote! {
94                 let #app_var = <#subcmd_type as ::structopt::StructOptInternal>::augment_clap(
95                     #app_var
96                 );
97                 #required
98             };
99             Some((span, ts))
100         } else {
101             None
102         }
103     });
104 
105     let subcmd = subcmds.next().map(|(_, ts)| ts);
106     if let Some((span, _)) = subcmds.next() {
107         abort!(
108             span,
109             "multiple subcommand sets are not allowed, that's the second"
110         );
111     }
112 
113     let args = fields.iter().filter_map(|field| {
114         let attrs = Attrs::from_field(
115             field,
116             Some(parent_attribute),
117             parent_attribute.casing(),
118             parent_attribute.env_casing(),
119         );
120         let kind = attrs.kind();
121         match &*kind {
122             Kind::ExternalSubcommand => abort!(
123                 kind.span(),
124                 "`external_subcommand` is only allowed on enum variants"
125             ),
126             Kind::Subcommand(_) | Kind::Skip(_) => None,
127             Kind::Flatten => {
128                 let ty = &field.ty;
129                 Some(quote_spanned! { kind.span()=>
130                     let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(#app_var);
131                     let #app_var = if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
132                         #app_var.setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp)
133                     } else {
134                         #app_var
135                     };
136                 })
137             }
138             Kind::Arg(ty) => {
139                 let convert_type = match **ty {
140                     Ty::Vec | Ty::Option => sub_type(&field.ty).unwrap_or(&field.ty),
141                     Ty::OptionOption | Ty::OptionVec => {
142                         sub_type(&field.ty).and_then(sub_type).unwrap_or(&field.ty)
143                     }
144                     _ => &field.ty,
145                 };
146 
147                 let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
148                 let flag = *attrs.parser().kind == ParserKind::FromFlag;
149 
150                 let parser = attrs.parser();
151                 let func = &parser.func;
152                 let validator = match *parser.kind {
153                     ParserKind::TryFromStr => quote_spanned! { func.span()=>
154                         .validator(|s| {
155                             #func(s.as_str())
156                             .map(|_: #convert_type| ())
157                             .map_err(|e| e.to_string())
158                         })
159                     },
160                     ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
161                         .validator_os(|s| #func(&s).map(|_: #convert_type| ()))
162                     },
163                     _ => quote!(),
164                 };
165 
166                 let modifier = match **ty {
167                     Ty::Bool => quote_spanned! { ty.span()=>
168                         .takes_value(false)
169                         .multiple(false)
170                     },
171 
172                     Ty::Option => quote_spanned! { ty.span()=>
173                         .takes_value(true)
174                         .multiple(false)
175                         #validator
176                     },
177 
178                     Ty::OptionOption => quote_spanned! { ty.span()=>
179                             .takes_value(true)
180                             .multiple(false)
181                             .min_values(0)
182                             .max_values(1)
183                             #validator
184                     },
185 
186                     Ty::OptionVec => quote_spanned! { ty.span()=>
187                         .takes_value(true)
188                         .multiple(true)
189                         .min_values(0)
190                         #validator
191                     },
192 
193                     Ty::Vec => quote_spanned! { ty.span()=>
194                         .takes_value(true)
195                         .multiple(true)
196                         #validator
197                     },
198 
199                     Ty::Other if occurrences => quote_spanned! { ty.span()=>
200                         .takes_value(false)
201                         .multiple(true)
202                     },
203 
204                     Ty::Other if flag => quote_spanned! { ty.span()=>
205                         .takes_value(false)
206                         .multiple(false)
207                     },
208 
209                     Ty::Other => {
210                         let required = !attrs.has_method("default_value");
211                         quote_spanned! { ty.span()=>
212                             .takes_value(true)
213                             .multiple(false)
214                             .required(#required)
215                             #validator
216                         }
217                     }
218                 };
219 
220                 let name = attrs.cased_name();
221                 let methods = attrs.field_methods();
222 
223                 Some(quote_spanned! { field.span()=>
224                     let #app_var = #app_var.arg(
225                         ::structopt::clap::Arg::with_name(#name)
226                             #modifier
227                             #methods
228                     );
229                 })
230             }
231         }
232     });
233 
234     let app_methods = parent_attribute.top_level_methods();
235     let version = parent_attribute.version();
236     quote! {{
237         let #app_var = #app_var#app_methods;
238         #( #args )*
239         #subcmd
240         #app_var#version
241     }}
242 }
243 
gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream244 fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
245     // This ident is used in several match branches below,
246     // and the `quote[_spanned]` invocations have different spans.
247     //
248     // Given that this ident is used in several places and
249     // that the branches are located inside of a loop, it is possible that
250     // this ident will be given _different_ spans in different places, and
251     // thus will not be the _same_ ident anymore. To make sure the `matches`
252     // is always the same, we factor it out.
253     let matches = format_ident!("matches");
254 
255     let fields = fields.iter().map(|field| {
256         let attrs = Attrs::from_field(
257             field,
258             Some(parent_attribute),
259             parent_attribute.casing(),
260             parent_attribute.env_casing(),
261         );
262         let field_name = field.ident.as_ref().unwrap();
263         let kind = attrs.kind();
264         match &*kind {
265             Kind::ExternalSubcommand => abort!(
266                 kind.span(),
267                 "`external_subcommand` is allowed only on enum variants"
268             ),
269 
270             Kind::Subcommand(ty) => {
271                 let subcmd_type = match (**ty, sub_type(&field.ty)) {
272                     (Ty::Option, Some(sub_type)) => sub_type,
273                     _ => &field.ty,
274                 };
275                 let unwrapper = match **ty {
276                     Ty::Option => quote!(),
277                     _ => quote_spanned!( ty.span()=> .unwrap() ),
278                 };
279                 quote_spanned! { kind.span()=>
280                     #field_name: <#subcmd_type as ::structopt::StructOptInternal>::from_subcommand(
281                         #matches.subcommand())
282                         #unwrapper
283                 }
284             }
285 
286             Kind::Flatten => quote_spanned! { kind.span()=>
287                 #field_name: ::structopt::StructOpt::from_clap(#matches)
288             },
289 
290             Kind::Skip(val) => match val {
291                 None => quote_spanned!(kind.span()=> #field_name: Default::default()),
292                 Some(val) => quote_spanned!(kind.span()=> #field_name: (#val).into()),
293             },
294 
295             Kind::Arg(ty) => {
296                 use crate::attrs::ParserKind::*;
297 
298                 let parser = attrs.parser();
299                 let func = &parser.func;
300                 let span = parser.kind.span();
301                 let (value_of, values_of, parse) = match *parser.kind {
302                     FromStr => (
303                         quote_spanned!(span=> value_of),
304                         quote_spanned!(span=> values_of),
305                         func.clone(),
306                     ),
307                     TryFromStr => (
308                         quote_spanned!(span=> value_of),
309                         quote_spanned!(span=> values_of),
310                         quote_spanned!(func.span()=> |s| #func(s).unwrap()),
311                     ),
312                     FromOsStr => (
313                         quote_spanned!(span=> value_of_os),
314                         quote_spanned!(span=> values_of_os),
315                         func.clone(),
316                     ),
317                     TryFromOsStr => (
318                         quote_spanned!(span=> value_of_os),
319                         quote_spanned!(span=> values_of_os),
320                         quote_spanned!(func.span()=> |s| #func(s).unwrap()),
321                     ),
322                     FromOccurrences => (
323                         quote_spanned!(span=> occurrences_of),
324                         quote!(),
325                         func.clone(),
326                     ),
327                     FromFlag => (quote!(), quote!(), func.clone()),
328                 };
329 
330                 let flag = *attrs.parser().kind == ParserKind::FromFlag;
331                 let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
332                 let name = attrs.cased_name();
333                 let field_value = match **ty {
334                     Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)),
335 
336                     Ty::Option => quote_spanned! { ty.span()=>
337                         #matches.#value_of(#name)
338                             .map(#parse)
339                     },
340 
341                     Ty::OptionOption => quote_spanned! { ty.span()=>
342                         if #matches.is_present(#name) {
343                             Some(#matches.#value_of(#name).map(#parse))
344                         } else {
345                             None
346                         }
347                     },
348 
349                     Ty::OptionVec => quote_spanned! { ty.span()=>
350                         if #matches.is_present(#name) {
351                             Some(#matches.#values_of(#name)
352                                  .map_or_else(Vec::new, |v| v.map(#parse).collect()))
353                         } else {
354                             None
355                         }
356                     },
357 
358                     Ty::Vec => quote_spanned! { ty.span()=>
359                         #matches.#values_of(#name)
360                             .map_or_else(Vec::new, |v| v.map(#parse).collect())
361                     },
362 
363                     Ty::Other if occurrences => quote_spanned! { ty.span()=>
364                         #parse(#matches.#value_of(#name))
365                     },
366 
367                     Ty::Other if flag => quote_spanned! { ty.span()=>
368                         #parse(#matches.is_present(#name))
369                     },
370 
371                     Ty::Other => quote_spanned! { ty.span()=>
372                         #matches.#value_of(#name)
373                             .map(#parse)
374                             .unwrap()
375                     },
376                 };
377 
378                 quote_spanned!(field.span()=> #field_name: #field_value )
379             }
380         }
381     });
382 
383     quote! {{
384         #( #fields ),*
385     }}
386 }
387 
gen_from_clap( struct_name: &Ident, fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs, ) -> TokenStream388 fn gen_from_clap(
389     struct_name: &Ident,
390     fields: &Punctuated<Field, Comma>,
391     parent_attribute: &Attrs,
392 ) -> TokenStream {
393     let field_block = gen_constructor(fields, parent_attribute);
394 
395     quote! {
396         fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
397             #struct_name #field_block
398         }
399     }
400 }
401 
gen_clap(attrs: &[Attribute]) -> GenOutput402 fn gen_clap(attrs: &[Attribute]) -> GenOutput {
403     let name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
404 
405     let attrs = Attrs::from_struct(
406         Span::call_site(),
407         attrs,
408         Name::Assigned(quote!(#name)),
409         None,
410         Sp::call_site(DEFAULT_CASING),
411         Sp::call_site(DEFAULT_ENV_CASING),
412     );
413     let tokens = {
414         let name = attrs.cased_name();
415         quote!(::structopt::clap::App::new(#name))
416     };
417 
418     GenOutput { tokens, attrs }
419 }
420 
gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput421 fn gen_clap_struct(struct_attrs: &[Attribute]) -> GenOutput {
422     let initial_clap_app_gen = gen_clap(struct_attrs);
423     let clap_tokens = initial_clap_app_gen.tokens;
424 
425     let augmented_tokens = quote! {
426         fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
427             let app = #clap_tokens;
428             <Self as ::structopt::StructOptInternal>::augment_clap(app)
429         }
430     };
431 
432     GenOutput {
433         tokens: augmented_tokens,
434         attrs: initial_clap_app_gen.attrs,
435     }
436 }
437 
gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream438 fn gen_augment_clap(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
439     let app_var = Ident::new("app", Span::call_site());
440     let augmentation = gen_augmentation(fields, &app_var, parent_attribute);
441     quote! {
442         fn augment_clap<'a, 'b>(
443             #app_var: ::structopt::clap::App<'a, 'b>
444         ) -> ::structopt::clap::App<'a, 'b> {
445             #augmentation
446         }
447     }
448 }
449 
gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput450 fn gen_clap_enum(enum_attrs: &[Attribute]) -> GenOutput {
451     let initial_clap_app_gen = gen_clap(enum_attrs);
452     let clap_tokens = initial_clap_app_gen.tokens;
453 
454     let tokens = quote! {
455         fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
456             let app = #clap_tokens
457                 .setting(::structopt::clap::AppSettings::SubcommandRequiredElseHelp);
458             <Self as ::structopt::StructOptInternal>::augment_clap(app)
459         }
460     };
461 
462     GenOutput {
463         tokens,
464         attrs: initial_clap_app_gen.attrs,
465     }
466 }
467 
gen_augment_clap_enum( variants: &Punctuated<Variant, Comma>, parent_attribute: &Attrs, ) -> TokenStream468 fn gen_augment_clap_enum(
469     variants: &Punctuated<Variant, Comma>,
470     parent_attribute: &Attrs,
471 ) -> TokenStream {
472     use syn::Fields::*;
473 
474     let subcommands = variants.iter().map(|variant| {
475         let attrs = Attrs::from_struct(
476             variant.span(),
477             &variant.attrs,
478             Name::Derived(variant.ident.clone()),
479             Some(parent_attribute),
480             parent_attribute.casing(),
481             parent_attribute.env_casing(),
482         );
483 
484         let kind = attrs.kind();
485         match &*kind {
486             Kind::ExternalSubcommand => {
487                 let app_var = Ident::new("app", Span::call_site());
488                 quote_spanned! { attrs.kind().span()=>
489                     let #app_var = #app_var.setting(
490                         ::structopt::clap::AppSettings::AllowExternalSubcommands
491                     );
492                 }
493             },
494 
495             Kind::Flatten => {
496                 match variant.fields {
497                     Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
498                         let ty = &unnamed[0];
499                         quote! {
500                             let app = <#ty as ::structopt::StructOptInternal>::augment_clap(app);
501                         }
502                     },
503                     _ => abort!(
504                         variant,
505                         "`flatten` is usable only with single-typed tuple variants"
506                     ),
507                 }
508             },
509 
510             _ => {
511                 let app_var = Ident::new("subcommand", Span::call_site());
512                 let arg_block = match variant.fields {
513                     Named(ref fields) => gen_augmentation(&fields.named, &app_var, &attrs),
514                     Unit => quote!( #app_var ),
515                     Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
516                         let ty = &unnamed[0];
517                         quote_spanned! { ty.span()=>
518                             {
519                                 let #app_var = <#ty as ::structopt::StructOptInternal>::augment_clap(
520                                     #app_var
521                                 );
522                                 if <#ty as ::structopt::StructOptInternal>::is_subcommand() {
523                                     #app_var.setting(
524                                         ::structopt::clap::AppSettings::SubcommandRequiredElseHelp
525                                     )
526                                 } else {
527                                     #app_var
528                                 }
529                             }
530                         }
531                     }
532                     Unnamed(..) => abort!(variant, "non single-typed tuple enums are not supported"),
533                 };
534 
535                 let name = attrs.cased_name();
536                 let from_attrs = attrs.top_level_methods();
537                 let version = attrs.version();
538                 quote! {
539                     let app = app.subcommand({
540                         let #app_var = ::structopt::clap::SubCommand::with_name(#name);
541                         let #app_var = #arg_block;
542                         #app_var#from_attrs#version
543                     });
544                 }
545             },
546         }
547     });
548 
549     let app_methods = parent_attribute.top_level_methods();
550     let version = parent_attribute.version();
551     quote! {
552         fn augment_clap<'a, 'b>(
553             app: ::structopt::clap::App<'a, 'b>
554         ) -> ::structopt::clap::App<'a, 'b> {
555             let app = app #app_methods;
556             #( #subcommands )*;
557             app #version
558         }
559     }
560 }
561 
gen_from_clap_enum() -> TokenStream562 fn gen_from_clap_enum() -> TokenStream {
563     quote! {
564         fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
565             <Self as ::structopt::StructOptInternal>::from_subcommand(matches.subcommand())
566                 .expect("structopt misuse: You likely tried to #[flatten] a struct \
567                          that contains #[subcommand]. This is forbidden.")
568         }
569     }
570 }
571 
gen_from_subcommand( name: &Ident, variants: &Punctuated<Variant, Comma>, parent_attribute: &Attrs, ) -> TokenStream572 fn gen_from_subcommand(
573     name: &Ident,
574     variants: &Punctuated<Variant, Comma>,
575     parent_attribute: &Attrs,
576 ) -> TokenStream {
577     use syn::Fields::*;
578 
579     let mut ext_subcmd = None;
580 
581     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
582         .iter()
583         .filter_map(|variant| {
584             let attrs = Attrs::from_struct(
585                 variant.span(),
586                 &variant.attrs,
587                 Name::Derived(variant.ident.clone()),
588                 Some(parent_attribute),
589                 parent_attribute.casing(),
590                 parent_attribute.env_casing(),
591             );
592 
593             let variant_name = &variant.ident;
594 
595             if let Kind::ExternalSubcommand = *attrs.kind() {
596                 if ext_subcmd.is_some() {
597                     abort!(
598                         attrs.kind().span(),
599                         "Only one variant can be marked with `external_subcommand`, \
600                          this is the second"
601                     );
602                 }
603 
604                 let ty = match variant.fields {
605                     Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
606 
607                     _ => abort!(
608                         variant,
609                         "The enum variant marked with `external_attribute` must be \
610                          a single-typed tuple, and the type must be either `Vec<String>` \
611                          or `Vec<OsString>`."
612                     ),
613                 };
614 
615                 let (span, str_ty, values_of) = match subty_if_name(ty, "Vec") {
616                     Some(subty) => {
617                         if is_simple_ty(subty, "String") {
618                             (
619                                 subty.span(),
620                                 quote!(::std::string::String),
621                                 quote!(values_of),
622                             )
623                         } else {
624                             (
625                                 subty.span(),
626                                 quote!(::std::ffi::OsString),
627                                 quote!(values_of_os),
628                             )
629                         }
630                     }
631 
632                     None => abort!(
633                         ty,
634                         "The type must be either `Vec<String>` or `Vec<OsString>` \
635                          to be used with `external_subcommand`."
636                     ),
637                 };
638 
639                 ext_subcmd = Some((span, variant_name, str_ty, values_of));
640                 None
641             } else {
642                 Some((variant, attrs))
643             }
644         })
645         .partition(|(_, attrs)| match &*attrs.kind() {
646             Kind::Flatten => true,
647             _ => false,
648         });
649 
650     let other = format_ident!("other");
651     let matches = format_ident!("matches");
652 
653     let external = match ext_subcmd {
654         Some((span, var_name, str_ty, values_of)) => quote_spanned! { span=>
655             match #other {
656                 ("", ::std::option::Option::None) => None,
657 
658                 (external, Some(#matches)) => {
659                     ::std::option::Option::Some(#name::#var_name(
660                         ::std::iter::once(#str_ty::from(external))
661                         .chain(
662                             #matches.#values_of("").into_iter().flatten().map(#str_ty::from)
663                         )
664                         .collect::<::std::vec::Vec<_>>()
665                     ))
666                 }
667 
668                 (external, None) => {
669                     ::std::option::Option::Some(#name::#var_name(
670                         ::std::iter::once(#str_ty::from(external))
671                             .collect::<::std::vec::Vec<_>>()
672                     ))
673                 }
674             }
675         },
676 
677         None => quote!(None),
678     };
679 
680     let match_arms = variants.iter().map(|(variant, attrs)| {
681         let sub_name = attrs.cased_name();
682         let variant_name = &variant.ident;
683         let constructor_block = match variant.fields {
684             Named(ref fields) => gen_constructor(&fields.named, &attrs),
685             Unit => quote!(),
686             Unnamed(ref fields) if fields.unnamed.len() == 1 => {
687                 let ty = &fields.unnamed[0];
688                 quote!( ( <#ty as ::structopt::StructOpt>::from_clap(#matches) ) )
689             }
690             Unnamed(..) => abort!(
691                 variant.ident,
692                 "non single-typed tuple enums are not supported"
693             ),
694         };
695 
696         quote! {
697             (#sub_name, Some(#matches)) => {
698                 Some(#name :: #variant_name #constructor_block)
699             }
700         }
701     });
702 
703     let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
704         let variant_name = &variant.ident;
705         match variant.fields {
706             Unnamed(ref fields) if fields.unnamed.len() == 1 => {
707                 let ty = &fields.unnamed[0];
708                 quote! {
709                     if let Some(res) =
710                         <#ty as ::structopt::StructOptInternal>::from_subcommand(#other)
711                     {
712                         return Some(#name :: #variant_name (res));
713                     }
714                 }
715             }
716             _ => abort!(
717                 variant,
718                 "`flatten` is usable only with single-typed tuple variants"
719             ),
720         }
721     });
722 
723     quote! {
724         fn from_subcommand<'a, 'b>(
725             sub: (&'b str, Option<&'b ::structopt::clap::ArgMatches<'a>>)
726         ) -> Option<Self> {
727             match sub {
728                 #( #match_arms, )*
729                 #other => {
730                     #( #child_subcommands )else*;
731                     #external
732                 }
733             }
734         }
735     }
736 }
737 
738 #[cfg(feature = "paw")]
gen_paw_impl(impl_generics: &ImplGenerics, name: &Ident, ty_generics: &TypeGenerics, where_clause: &TokenStream) -> TokenStream739 fn gen_paw_impl(impl_generics: &ImplGenerics, name: &Ident, ty_generics: &TypeGenerics, where_clause: &TokenStream) -> TokenStream {
740     quote! {
741         impl #impl_generics ::structopt::paw::ParseArgs for #name #ty_generics #where_clause {
742             type Error = std::io::Error;
743 
744             fn parse_args() -> std::result::Result<Self, Self::Error> {
745                 Ok(<#name as ::structopt::StructOpt>::from_args())
746             }
747         }
748     }
749 }
750 #[cfg(not(feature = "paw"))]
gen_paw_impl(_: &ImplGenerics, _: &Ident, _: &TypeGenerics, _: &TokenStream) -> TokenStream751 fn gen_paw_impl(_: &ImplGenerics, _: &Ident, _: &TypeGenerics, _: &TokenStream) -> TokenStream {
752     TokenStream::new()
753 }
754 
split_structopt_generics_for_impl(generics: &Generics) -> (ImplGenerics, TypeGenerics, TokenStream)755 fn split_structopt_generics_for_impl(generics: &Generics) -> (ImplGenerics, TypeGenerics, TokenStream) {
756     use syn::{ token::Add, TypeParamBound::Trait };
757 
758     fn path_ends_with(path: &Path, ident: &str) -> bool {
759         path.segments.last().unwrap().ident == ident
760     }
761 
762     fn type_param_bounds_contains(bounds: &Punctuated<TypeParamBound, Add>, ident: &str) -> bool {
763         for bound in bounds {
764             if let Trait(bound) = bound {
765                 if path_ends_with(&bound.path, ident) {
766                     return true;
767                 }
768             }
769         }
770         return false;
771     }
772 
773     struct TraitBoundAmendments{
774         tokens: TokenStream,
775         need_where: bool,
776         need_comma: bool,
777     }
778 
779     impl TraitBoundAmendments {
780         fn new(where_clause: Option<&WhereClause>) -> Self {
781             let tokens = TokenStream::new();
782             let (need_where,need_comma) = if let Some(where_clause) = where_clause {
783                 if where_clause.predicates.trailing_punct() {
784                     (false, false)
785                 } else {
786                     (false, true)
787                 }
788             } else {
789                 (true, false)
790             };
791             Self{tokens, need_where, need_comma}
792         }
793 
794         fn add(&mut self, amendment: TokenStream) {
795             if self.need_where {
796                 self.tokens.extend(quote!{ where });
797                 self.need_where = false;
798             }
799             if self.need_comma {
800                 self.tokens.extend(quote!{ , });
801             }
802             self.tokens.extend(amendment);
803             self.need_comma = true;
804         }
805 
806         fn into_tokens(self) -> TokenStream {
807             self.tokens
808         }
809     }
810 
811     let mut trait_bound_amendments = TraitBoundAmendments::new(generics.where_clause.as_ref());
812 
813     for param in &generics.params {
814         if let GenericParam::Type(param) = param {
815             let param_ident = &param.ident;
816             if type_param_bounds_contains(&param.bounds, "StructOpt") {
817                 trait_bound_amendments.add(quote!{ #param_ident : ::structopt::StructOptInternal });
818             }
819         }
820     }
821 
822     if let Some(where_clause) = &generics.where_clause {
823         for predicate in &where_clause.predicates {
824             if let WherePredicate::Type(predicate) = predicate {
825                 let predicate_bounded_ty = &predicate.bounded_ty;
826                 if type_param_bounds_contains(&predicate.bounds, "StructOpt") {
827                     trait_bound_amendments.add(quote!{ #predicate_bounded_ty : ::structopt::StructOptInternal });
828                 }
829             }
830         }
831     }
832 
833     let trait_bound_amendments = trait_bound_amendments.into_tokens();
834 
835     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
836 
837     let where_clause = quote!{ #where_clause #trait_bound_amendments };
838 
839     (impl_generics, ty_generics, where_clause)
840 }
841 
impl_structopt_for_struct( name: &Ident, fields: &Punctuated<Field, Comma>, attrs: &[Attribute], generics: &Generics, ) -> TokenStream842 fn impl_structopt_for_struct(
843     name: &Ident,
844     fields: &Punctuated<Field, Comma>,
845     attrs: &[Attribute],
846     generics: &Generics,
847 ) -> TokenStream {
848     let (impl_generics, ty_generics, where_clause) = split_structopt_generics_for_impl(&generics);
849 
850     let basic_clap_app_gen = gen_clap_struct(attrs);
851     let augment_clap = gen_augment_clap(fields, &basic_clap_app_gen.attrs);
852     let from_clap = gen_from_clap(name, fields, &basic_clap_app_gen.attrs);
853     let paw_impl = gen_paw_impl(&impl_generics, name, &ty_generics, &where_clause);
854 
855     let clap_tokens = basic_clap_app_gen.tokens;
856     quote! {
857         #[allow(unused_variables)]
858         #[allow(unknown_lints)]
859         #[allow(
860             clippy::style,
861             clippy::complexity,
862             clippy::pedantic,
863             clippy::restriction,
864             clippy::perf,
865             clippy::deprecated,
866             clippy::nursery,
867             clippy::cargo
868         )]
869         #[deny(clippy::correctness)]
870         #[allow(dead_code, unreachable_code)]
871         impl #impl_generics ::structopt::StructOpt for #name #ty_generics #where_clause {
872             #clap_tokens
873             #from_clap
874         }
875 
876         #[allow(unused_variables)]
877         #[allow(unknown_lints)]
878         #[allow(
879             clippy::style,
880             clippy::complexity,
881             clippy::pedantic,
882             clippy::restriction,
883             clippy::perf,
884             clippy::deprecated,
885             clippy::nursery,
886             clippy::cargo
887         )]
888         #[deny(clippy::correctness)]
889         #[allow(dead_code, unreachable_code)]
890         impl #impl_generics ::structopt::StructOptInternal for #name #ty_generics #where_clause {
891             #augment_clap
892             fn is_subcommand() -> bool { false }
893         }
894 
895         #paw_impl
896     }
897 }
898 
impl_structopt_for_enum( name: &Ident, variants: &Punctuated<Variant, Comma>, attrs: &[Attribute], generics: &Generics, ) -> TokenStream899 fn impl_structopt_for_enum(
900     name: &Ident,
901     variants: &Punctuated<Variant, Comma>,
902     attrs: &[Attribute],
903     generics: &Generics,
904 ) -> TokenStream {
905 
906     let (impl_generics, ty_generics, where_clause) = split_structopt_generics_for_impl(&generics);
907 
908     let basic_clap_app_gen = gen_clap_enum(attrs);
909     let clap_tokens = basic_clap_app_gen.tokens;
910     let attrs = basic_clap_app_gen.attrs;
911 
912     let augment_clap = gen_augment_clap_enum(variants, &attrs);
913     let from_clap = gen_from_clap_enum();
914     let from_subcommand = gen_from_subcommand(name, variants, &attrs);
915     let paw_impl = gen_paw_impl(&impl_generics, name, &ty_generics, &where_clause);
916 
917     quote! {
918         #[allow(unknown_lints)]
919         #[allow(unused_variables, dead_code, unreachable_code)]
920         #[allow(
921             clippy::style,
922             clippy::complexity,
923             clippy::pedantic,
924             clippy::restriction,
925             clippy::perf,
926             clippy::deprecated,
927             clippy::nursery,
928             clippy::cargo
929         )]
930         #[deny(clippy::correctness)]
931         impl #impl_generics ::structopt::StructOpt for #name #ty_generics #where_clause {
932             #clap_tokens
933             #from_clap
934         }
935 
936         #[allow(unused_variables)]
937         #[allow(unknown_lints)]
938         #[allow(
939             clippy::style,
940             clippy::complexity,
941             clippy::pedantic,
942             clippy::restriction,
943             clippy::perf,
944             clippy::deprecated,
945             clippy::nursery,
946             clippy::cargo
947         )]
948         #[deny(clippy::correctness)]
949         #[allow(dead_code, unreachable_code)]
950         impl #impl_generics ::structopt::StructOptInternal for #name #ty_generics #where_clause {
951             #augment_clap
952             #from_subcommand
953             fn is_subcommand() -> bool { true }
954         }
955 
956         #paw_impl
957     }
958 }
959 
impl_structopt(input: &DeriveInput) -> TokenStream960 fn impl_structopt(input: &DeriveInput) -> TokenStream {
961     use syn::Data::*;
962 
963     let struct_name = &input.ident;
964 
965     set_dummy(quote! {
966         impl ::structopt::StructOpt for #struct_name {
967             fn clap<'a, 'b>() -> ::structopt::clap::App<'a, 'b> {
968                 unimplemented!()
969             }
970             fn from_clap(_matches: &::structopt::clap::ArgMatches) -> Self {
971                 unimplemented!()
972             }
973         }
974 
975         impl ::structopt::StructOptInternal for #struct_name {}
976     });
977 
978     match input.data {
979         Struct(DataStruct {
980             fields: syn::Fields::Named(ref fields),
981             ..
982         }) => impl_structopt_for_struct(struct_name, &fields.named, &input.attrs, &input.generics),
983         Enum(ref e) => impl_structopt_for_enum(struct_name, &e.variants, &input.attrs, &input.generics),
984         _ => abort_call_site!("structopt only supports non-tuple structs and enums"),
985     }
986 }
987