diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 820a4c8e..a5cd7c26 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -28,7 +28,7 @@ use crate::{ use proc_macro2::{Span, TokenStream}; use proc_macro_error::{abort, abort_call_site, proc_macro_error, set_dummy}; -use quote::{quote, quote_spanned}; +use quote::{format_ident, quote, quote_spanned}; use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, *}; /// Default casing style for generated arguments. @@ -239,6 +239,16 @@ fn gen_augmentation( } fn gen_constructor(fields: &Punctuated, parent_attribute: &Attrs) -> TokenStream { + // This ident is used in several match branches below, + // and the `quote[_spanned]` invocations have different spans. + // + // Given that this ident is used in several places and + // that the branches are located inside of a loop, it is possible that + // this ident will be given _different_ spans in different places, and + // thus will not be the _same_ ident anymore. To make sure the `matches` + // is always the same, we factor it out. + let matches = format_ident!("matches"); + let fields = fields.iter().map(|field| { let attrs = Attrs::from_field( field, @@ -265,13 +275,13 @@ fn gen_constructor(fields: &Punctuated, parent_attribute: &Attrs) }; quote_spanned! { kind.span()=> #field_name: <#subcmd_type as ::structopt::StructOptInternal>::from_subcommand( - matches.subcommand()) + #matches.subcommand()) #unwrapper } } Kind::Flatten => quote_spanned! { kind.span()=> - #field_name: ::structopt::StructOpt::from_clap(matches) + #field_name: ::structopt::StructOpt::from_clap(#matches) }, Kind::Skip(val) => match val { @@ -318,24 +328,24 @@ fn gen_constructor(fields: &Punctuated, parent_attribute: &Attrs) let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences; let name = attrs.cased_name(); let field_value = match **ty { - Ty::Bool => quote_spanned!(ty.span()=> matches.is_present(#name)), + Ty::Bool => quote_spanned!(ty.span()=> #matches.is_present(#name)), Ty::Option => quote_spanned! { ty.span()=> - matches.#value_of(#name) + #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 #matches.is_present(#name) { + Some(#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 #matches.is_present(#name) { + Some(#matches.#values_of(#name) .map_or_else(Vec::new, |v| v.map(#parse).collect())) } else { None @@ -343,20 +353,20 @@ fn gen_constructor(fields: &Punctuated, parent_attribute: &Attrs) }, Ty::Vec => quote_spanned! { ty.span()=> - matches.#values_of(#name) + #matches.#values_of(#name) .map_or_else(Vec::new, |v| v.map(#parse).collect()) }, Ty::Other if occurrences => quote_spanned! { ty.span()=> - #parse(matches.#value_of(#name)) + #parse(#matches.#value_of(#name)) }, Ty::Other if flag => quote_spanned! { ty.span()=> - #parse(matches.is_present(#name)) + #parse(#matches.is_present(#name)) }, Ty::Other => quote_spanned! { ty.span()=> - matches.#value_of(#name) + #matches.#value_of(#name) .map(#parse) .unwrap() },