mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 06:42:33 +00:00
Fix hygiene of arg_matches
parameter
In the `Clap` derive macro, a function parameter named `arg_matches` is generated using `quote!` - as a result, this parameter ends up with call-site hygiene. However, `arg_matches` is written literally within several `quote_spanned!` blocks, which generate an `arg_matches` token with the hygiene of whatever span was passed to `quote_spanned!`. If these two hygienes are different (for example, if the user invokes the derive macro from a `macro_rules!` macro), then a usage of `arg_matches` may not resolve to the `arg_matches` parameter definition. This commit changes the generation of `arg_matches` identifiers to always use `quote!`, ensuring that they will always be considered the 'same' identifier by Rust.
This commit is contained in:
parent
3c93f276b5
commit
6dc8353fe2
1 changed files with 22 additions and 16 deletions
|
@ -131,16 +131,20 @@ fn gen_parsers(
|
||||||
let flag = *attrs.parser().kind == ParserKind::FromFlag;
|
let flag = *attrs.parser().kind == ParserKind::FromFlag;
|
||||||
let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
|
let occurrences = *attrs.parser().kind == ParserKind::FromOccurrences;
|
||||||
let name = attrs.cased_name();
|
let name = attrs.cased_name();
|
||||||
|
// Use `quote!` to give this identifier the same hygiene
|
||||||
|
// as the `arg_matches` parameter definition. This
|
||||||
|
// allows us to refer to `arg_matches` within a `quote_spanned` block
|
||||||
|
let arg_matches = quote! { arg_matches };
|
||||||
|
|
||||||
let field_value = match **ty {
|
let field_value = match **ty {
|
||||||
Ty::Bool => {
|
Ty::Bool => {
|
||||||
if update.is_some() {
|
if update.is_some() {
|
||||||
quote_spanned! { ty.span()=>
|
quote_spanned! { ty.span()=>
|
||||||
*#field_name || arg_matches.is_present(#name)
|
*#field_name || #arg_matches.is_present(#name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote_spanned! { ty.span()=>
|
quote_spanned! { ty.span()=>
|
||||||
arg_matches.is_present(#name)
|
#arg_matches.is_present(#name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,22 +157,22 @@ fn gen_parsers(
|
||||||
}
|
}
|
||||||
|
|
||||||
quote_spanned! { ty.span()=>
|
quote_spanned! { ty.span()=>
|
||||||
arg_matches.#value_of(#name)
|
#arg_matches.#value_of(#name)
|
||||||
.map(#parse)
|
.map(#parse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ty::OptionOption => quote_spanned! { ty.span()=>
|
Ty::OptionOption => quote_spanned! { ty.span()=>
|
||||||
if arg_matches.is_present(#name) {
|
if #arg_matches.is_present(#name) {
|
||||||
Some(arg_matches.#value_of(#name).map(#parse))
|
Some(#arg_matches.#value_of(#name).map(#parse))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Ty::OptionVec => quote_spanned! { ty.span()=>
|
Ty::OptionVec => quote_spanned! { ty.span()=>
|
||||||
if arg_matches.is_present(#name) {
|
if #arg_matches.is_present(#name) {
|
||||||
Some(arg_matches.#values_of(#name)
|
Some(#arg_matches.#values_of(#name)
|
||||||
.map(|v| v.map(#parse).collect())
|
.map(|v| v.map(#parse).collect())
|
||||||
.unwrap_or_else(Vec::new))
|
.unwrap_or_else(Vec::new))
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,18 +188,18 @@ fn gen_parsers(
|
||||||
}
|
}
|
||||||
|
|
||||||
quote_spanned! { ty.span()=>
|
quote_spanned! { ty.span()=>
|
||||||
arg_matches.#values_of(#name)
|
#arg_matches.#values_of(#name)
|
||||||
.map(|v| v.map(#parse).collect())
|
.map(|v| v.map(#parse).collect())
|
||||||
.unwrap_or_else(Vec::new)
|
.unwrap_or_else(Vec::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ty::Other if occurrences => quote_spanned! { ty.span()=>
|
Ty::Other if occurrences => quote_spanned! { ty.span()=>
|
||||||
#parse(arg_matches.#value_of(#name))
|
#parse(#arg_matches.#value_of(#name))
|
||||||
},
|
},
|
||||||
|
|
||||||
Ty::Other if flag => quote_spanned! { ty.span()=>
|
Ty::Other if flag => quote_spanned! { ty.span()=>
|
||||||
#parse(arg_matches.is_present(#name))
|
#parse(#arg_matches.is_present(#name))
|
||||||
},
|
},
|
||||||
|
|
||||||
Ty::Other => {
|
Ty::Other => {
|
||||||
|
@ -204,7 +208,7 @@ fn gen_parsers(
|
||||||
}
|
}
|
||||||
|
|
||||||
quote_spanned! { ty.span()=>
|
quote_spanned! { ty.span()=>
|
||||||
arg_matches.#value_of(#name)
|
#arg_matches.#value_of(#name)
|
||||||
.map(#parse)
|
.map(#parse)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -213,7 +217,7 @@ fn gen_parsers(
|
||||||
|
|
||||||
if let Some(access) = update {
|
if let Some(access) = update {
|
||||||
quote_spanned! { field.span()=>
|
quote_spanned! { field.span()=>
|
||||||
if arg_matches.is_present(#name) {
|
if #arg_matches.is_present(#name) {
|
||||||
#access
|
#access
|
||||||
*#field_name = #field_value
|
*#field_name = #field_value
|
||||||
}
|
}
|
||||||
|
@ -232,6 +236,7 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att
|
||||||
);
|
);
|
||||||
let field_name = field.ident.as_ref().unwrap();
|
let field_name = field.ident.as_ref().unwrap();
|
||||||
let kind = attrs.kind();
|
let kind = attrs.kind();
|
||||||
|
let arg_matches = quote! { arg_matches };
|
||||||
match &*kind {
|
match &*kind {
|
||||||
Kind::ExternalSubcommand => {
|
Kind::ExternalSubcommand => {
|
||||||
abort! { kind.span(),
|
abort! { kind.span(),
|
||||||
|
@ -249,14 +254,14 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Att
|
||||||
};
|
};
|
||||||
quote_spanned! { kind.span()=>
|
quote_spanned! { kind.span()=>
|
||||||
#field_name: {
|
#field_name: {
|
||||||
<#subcmd_type as ::clap::Subcommand>::from_subcommand(arg_matches.subcommand())
|
<#subcmd_type as ::clap::Subcommand>::from_subcommand(#arg_matches.subcommand())
|
||||||
#unwrapper
|
#unwrapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Kind::Flatten => quote_spanned! { kind.span()=>
|
Kind::Flatten => quote_spanned! { kind.span()=>
|
||||||
#field_name: ::clap::FromArgMatches::from_arg_matches(arg_matches)
|
#field_name: ::clap::FromArgMatches::from_arg_matches(#arg_matches)
|
||||||
},
|
},
|
||||||
|
|
||||||
Kind::Skip(val) => match val {
|
Kind::Skip(val) => match val {
|
||||||
|
@ -295,6 +300,7 @@ pub fn gen_updater(
|
||||||
} else {
|
} else {
|
||||||
quote!()
|
quote!()
|
||||||
};
|
};
|
||||||
|
let arg_matches = quote! { arg_matches };
|
||||||
|
|
||||||
match &*kind {
|
match &*kind {
|
||||||
Kind::ExternalSubcommand => {
|
Kind::ExternalSubcommand => {
|
||||||
|
@ -329,7 +335,7 @@ pub fn gen_updater(
|
||||||
|
|
||||||
quote_spanned! { kind.span()=>
|
quote_spanned! { kind.span()=>
|
||||||
{
|
{
|
||||||
let subcmd = arg_matches.subcommand();
|
let subcmd = #arg_matches.subcommand();
|
||||||
#access
|
#access
|
||||||
#updater
|
#updater
|
||||||
}
|
}
|
||||||
|
@ -338,7 +344,7 @@ pub fn gen_updater(
|
||||||
|
|
||||||
Kind::Flatten => quote_spanned! { kind.span()=> {
|
Kind::Flatten => quote_spanned! { kind.span()=> {
|
||||||
#access
|
#access
|
||||||
::clap::FromArgMatches::update_from_arg_matches(#field_name, arg_matches);
|
::clap::FromArgMatches::update_from_arg_matches(#field_name, #arg_matches);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue