-
Notifications
You must be signed in to change notification settings - Fork 796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix the interpretation of vararg separator #792
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,139 +52,109 @@ impl PyFunctionAttr { | |
NestedMeta::Lit(ref lit) => { | ||
self.add_literal(item, lit)?; | ||
} | ||
_ => { | ||
return Err(syn::Error::new_spanned(item, "Unknown argument")); | ||
NestedMeta::Meta(syn::Meta::List(ref list)) => { | ||
return Err(syn::Error::new_spanned( | ||
list, | ||
"List is not supported as argument", | ||
)); | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
fn add_literal(&mut self, item: &NestedMeta, lit: &syn::Lit) -> syn::Result<()> { | ||
match lit { | ||
syn::Lit::Str(ref lits) => { | ||
syn::Lit::Str(ref lits) if lits.value() == "*" => { | ||
// "*" | ||
if lits.value() == "*" { | ||
if self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"syntax error, keyword self.arguments is defined", | ||
)); | ||
} | ||
if self.has_varargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"self.arguments already define * (var args)", | ||
)); | ||
} | ||
self.has_varargs = true; | ||
self.arguments.push(Argument::VarArgsSeparator); | ||
} else { | ||
return Err(syn::Error::new_spanned(lits, "Unknown string literal")); | ||
} | ||
} | ||
_ => { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
format!("Only string literal is supported, got: {:?}", lit), | ||
)); | ||
self.vararg_is_ok(item)?; | ||
self.has_varargs = true; | ||
self.arguments.push(Argument::VarArgsSeparator); | ||
Ok(()) | ||
} | ||
}; | ||
Ok(()) | ||
_ => Err(syn::Error::new_spanned( | ||
item, | ||
format!("Only \"*\" is supported here, got: {:?}", lit), | ||
)), | ||
} | ||
} | ||
|
||
fn add_work(&mut self, item: &NestedMeta, path: &Path) -> syn::Result<()> { | ||
// self.arguments in form somename | ||
if self.has_kwargs { | ||
if self.has_kw || self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"syntax error, keyword self.arguments is defined", | ||
"Positional argument or varargs(*) is not allowed after keyword arguments", | ||
)); | ||
} | ||
if self.has_kw { | ||
if self.has_varargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"syntax error, argument is not allowed after keyword argument", | ||
"Positional argument or varargs(*) is not allowed after *", | ||
)); | ||
} | ||
self.arguments.push(Argument::Arg(path.clone(), None)); | ||
Ok(()) | ||
} | ||
|
||
fn vararg_is_ok(&self, item: &NestedMeta) -> syn::Result<()> { | ||
if self.has_kwargs || self.has_varargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"* is not allowed after varargs(*) or kwargs(**)", | ||
)); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn kw_arg_is_ok(&self, item: &NestedMeta) -> syn::Result<()> { | ||
if self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"Keyword argument or kwargs(**) is not allowed after kwargs(**)", | ||
)); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn add_nv_common( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I separated this function for refactoring. if self.has_varargs {
// No check here
...
} else {
self.kw_arg_is_ok(item)?;
...
} ). |
||
&mut self, | ||
item: &NestedMeta, | ||
name: &syn::Path, | ||
value: String, | ||
) -> syn::Result<()> { | ||
self.kw_arg_is_ok(item)?; | ||
if self.has_varargs { | ||
// kw only | ||
self.arguments.push(Argument::Kwarg(name.clone(), value)); | ||
} else { | ||
self.has_kw = true; | ||
self.arguments | ||
.push(Argument::Arg(name.clone(), Some(value))); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn add_name_value(&mut self, item: &NestedMeta, nv: &syn::MetaNameValue) -> syn::Result<()> { | ||
match nv.lit { | ||
syn::Lit::Str(ref litstr) => { | ||
if litstr.value() == "*" { | ||
// args="*" | ||
if self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"* - syntax error, keyword self.arguments is defined", | ||
)); | ||
} | ||
if self.has_varargs { | ||
return Err(syn::Error::new_spanned(item, "*(var args) is defined")); | ||
} | ||
self.vararg_is_ok(item)?; | ||
self.has_varargs = true; | ||
self.arguments.push(Argument::VarArgs(nv.path.clone())); | ||
} else if litstr.value() == "**" { | ||
// kwargs="**" | ||
if self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"self.arguments already define ** (kw args)", | ||
)); | ||
} | ||
self.kw_arg_is_ok(item)?; | ||
self.has_kwargs = true; | ||
self.arguments.push(Argument::KeywordArgs(nv.path.clone())); | ||
} else if self.has_varargs { | ||
self.arguments | ||
.push(Argument::Kwarg(nv.path.clone(), litstr.value())) | ||
} else { | ||
if self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"syntax error, keyword self.arguments is defined", | ||
)); | ||
} | ||
self.has_kw = true; | ||
self.arguments | ||
.push(Argument::Arg(nv.path.clone(), Some(litstr.value()))) | ||
self.add_nv_common(item, &nv.path, litstr.value())?; | ||
} | ||
} | ||
syn::Lit::Int(ref litint) => { | ||
if self.has_varargs { | ||
self.arguments | ||
.push(Argument::Kwarg(nv.path.clone(), format!("{}", litint))); | ||
} else { | ||
if self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"syntax error, keyword self.arguments is defined", | ||
)); | ||
} | ||
self.has_kw = true; | ||
self.arguments | ||
.push(Argument::Arg(nv.path.clone(), Some(format!("{}", litint)))); | ||
} | ||
self.add_nv_common(item, &nv.path, format!("{}", litint))?; | ||
} | ||
syn::Lit::Bool(ref litb) => { | ||
if self.has_varargs { | ||
self.arguments | ||
.push(Argument::Kwarg(nv.path.clone(), format!("{}", litb.value))); | ||
} else { | ||
if self.has_kwargs { | ||
return Err(syn::Error::new_spanned( | ||
item, | ||
"syntax error, keyword self.arguments is defined", | ||
)); | ||
} | ||
self.has_kw = true; | ||
self.arguments.push(Argument::Arg( | ||
nv.path.clone(), | ||
Some(format!("{}", litb.value)), | ||
)); | ||
} | ||
self.add_nv_common(item, &nv.path, format!("{}", litb.value))?; | ||
} | ||
_ => { | ||
return Err(syn::Error::new_spanned( | ||
|
@@ -221,11 +191,8 @@ pub fn parse_name_attribute(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Opti | |
*span, | ||
"Expected string literal for #[name] argument", | ||
)), | ||
// TODO: The below pattern is unstable, so instead we match the wildcard. | ||
// slice_patterns due to be stable soon: https://github.com/rust-lang/rust/issues/62254 | ||
// [(_, span), _, ..] => { | ||
_ => Err(syn::Error::new( | ||
name_attrs[0].1, | ||
[(_, span), ..] => Err(syn::Error::new( | ||
*span, | ||
"#[name] can not be specified multiple times", | ||
)), | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -406,15 +406,6 @@ fn impl_call(_cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { | |
quote! { _slf.#fname(#(#names),*) } | ||
} | ||
|
||
/// Converts a bool to "true" or "false" | ||
fn bool_to_ident(condition: bool) -> syn::Ident { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed this function since just |
||
if condition { | ||
syn::Ident::new("true", Span::call_site()) | ||
} else { | ||
syn::Ident::new("false", Span::call_site()) | ||
} | ||
} | ||
|
||
fn impl_arg_params_(spec: &FnSpec<'_>, body: TokenStream, into_result: TokenStream) -> TokenStream { | ||
if spec.args.is_empty() { | ||
return quote! { | ||
|
@@ -431,8 +422,8 @@ fn impl_arg_params_(spec: &FnSpec<'_>, body: TokenStream, into_result: TokenStre | |
continue; | ||
} | ||
let name = arg.name; | ||
let kwonly = bool_to_ident(spec.is_kw_only(&arg.name)); | ||
let opt = bool_to_ident(arg.optional.is_some() || spec.default_value(&arg.name).is_some()); | ||
let kwonly = spec.is_kw_only(&arg.name); | ||
let opt = arg.optional.is_some() || spec.default_value(&arg.name).is_some(); | ||
|
||
params.push(quote! { | ||
pyo3::derive_utils::ParamDescription { | ||
|
@@ -449,8 +440,16 @@ fn impl_arg_params_(spec: &FnSpec<'_>, body: TokenStream, into_result: TokenStre | |
param_conversion.push(impl_arg_param(&arg, &spec, idx, &mut option_pos)); | ||
} | ||
|
||
let accept_args = bool_to_ident(spec.accept_args()); | ||
let accept_kwargs = bool_to_ident(spec.accept_kwargs()); | ||
let (mut accept_args, mut accept_kwargs) = (false, false); | ||
|
||
for s in spec.attrs.iter() { | ||
use crate::pyfunction::Argument; | ||
match s { | ||
Argument::VarArgs(_) => accept_args = true, | ||
Argument::KeywordArgs(_) => accept_kwargs = true, | ||
_ => continue, | ||
} | ||
} | ||
let num_normal_params = params.len(); | ||
// create array of arguments, and then parse | ||
quote! { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice :)