From a4f984608c27b9883f7b3f380e364c59cf34ef75 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 11 Jun 2020 20:02:44 -0400 Subject: [PATCH] Use proper span when generating `matches` token In `struct-opt-derive`, a function is generated with a parameter named `matches`. Since `quote!` is used to generate the function, the `matches` token will be resolved using `Span::call_site`. However, the literal identifier `matches` is also used inside several `quote_spanned!` expressions. Such a `matches` identifier will be resolved using the `Span` passed to `quote_spanned!`, which may not be the same as `Span::call_site`. Currently, this is difficult to observe in practice, due to rust-lang/rust#43081 . However, once PR rust-lang/rust#73084 is merged, proc macros will see properly spanned tokens in more cases, which will cause these incorrect uses of `quote_spanned!` to break. This PR uses `quote! { matches }` to generate a correctly spanned `matches` token, which is then include in the `quote_spanned!` expressions using `#matches`. --- structopt-derive/src/lib.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/structopt-derive/src/lib.rs b/structopt-derive/src/lib.rs index 5e494681..4817277a 100644 --- a/structopt-derive/src/lib.rs +++ b/structopt-derive/src/lib.rs @@ -248,6 +248,7 @@ fn gen_constructor(fields: &Punctuated, parent_attribute: &Attrs) ); let field_name = field.ident.as_ref().unwrap(); let kind = attrs.kind(); + let matches = quote! { matches }; match &*kind { Kind::ExternalSubcommand => abort!( kind.span(), @@ -265,13 +266,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 +319,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 +344,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() },