Remove proc-macro panics.

This commit is contained in:
Sebastian Puetz 2020-09-01 20:23:14 +02:00
parent 4a05f2782c
commit 4e7fe6e638
4 changed files with 86 additions and 61 deletions

View file

@ -224,7 +224,7 @@ impl<'a> FnSpec<'a> {
ty,
optional: opt,
py,
reference: is_ref(name, ty),
reference: is_ref(name, ty)?,
});
}
}
@ -323,30 +323,32 @@ impl<'a> FnSpec<'a> {
}
}
pub fn is_ref(name: &syn::Ident, ty: &syn::Type) -> bool {
pub fn is_ref(name: &syn::Ident, ty: &syn::Type) -> syn::Result<bool> {
match ty {
syn::Type::Reference(_) => return true,
syn::Type::Reference(_) => return Ok(true),
syn::Type::Path(syn::TypePath { ref path, .. }) => {
if let Some(segment) = path.segments.last() {
if "Option" == segment.ident.to_string().as_str() {
match segment.arguments {
syn::PathArguments::AngleBracketed(ref params) => {
if params.args.len() != 1 {
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
let msg = format!("argument type is not supported by python method: {:?} ({:?}) {:?}",
name,
ty,
path);
syn::Error::new_spanned(segment, msg);
}
let last = &params.args[params.args.len() - 1];
if let syn::GenericArgument::Type(syn::Type::Reference(_)) = last {
return true;
return Ok(true);
}
}
_ => {
panic!(
let msg = format!(
"argument type is not supported by python method: {:?} ({:?}) {:?}",
name, ty, path
);
syn::Error::new_spanned(segment, msg);
}
}
}
@ -354,7 +356,7 @@ pub fn is_ref(name: &syn::Ident, ty: &syn::Type) -> bool {
}
_ => (),
}
false
Ok(false)
}
pub(crate) fn check_ty_optional(ty: &syn::Type) -> Option<&syn::Type> {

View file

@ -40,7 +40,7 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) -> syn::Result<()> {
for stmt in func.block.stmts.iter_mut() {
if let syn::Stmt::Item(syn::Item::Fn(ref mut func)) = stmt {
if let Some((module_name, python_name, pyfn_attrs)) =
extract_pyfn_attrs(&mut func.attrs)
extract_pyfn_attrs(&mut func.attrs)?
{
let function_to_python = add_fn_to_module(func, python_name, pyfn_attrs)?;
let function_wrapper_ident = function_wrapper_ident(&func.sig.ident);
@ -76,14 +76,14 @@ fn wrap_fn_argument<'a>(cap: &'a syn::PatType, name: &'a Ident) -> syn::Result<m
ty: &cap.ty,
optional: opt,
py,
reference: method::is_ref(&name, &cap.ty),
reference: method::is_ref(&name, &cap.ty)?,
})
}
/// Extracts the data from the #[pyfn(...)] attribute of a function
fn extract_pyfn_attrs(
attrs: &mut Vec<syn::Attribute>,
) -> Option<(syn::Path, Ident, Vec<pyfunction::Argument>)> {
) -> syn::Result<Option<(syn::Path, Ident, Vec<pyfunction::Argument>)>> {
let mut new_attrs = Vec::new();
let mut fnname = None;
let mut modname = None;
@ -99,14 +99,24 @@ fn extract_pyfn_attrs(
syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
modname = Some(path.clone())
}
_ => panic!("The first parameter of pyfn must be a MetaItem"),
_ => {
return Err(syn::Error::new_spanned(
&meta[0],
"The first parameter of pyfn must be a MetaItem",
))
}
}
// read Python function name
match meta[1] {
syn::NestedMeta::Lit(syn::Lit::Str(ref lits)) => {
fnname = Some(syn::Ident::new(&lits.value(), lits.span()));
}
_ => panic!("The second parameter of pyfn must be a Literal"),
_ => {
return Err(syn::Error::new_spanned(
&meta[1],
"The second parameter of pyfn must be a Literal",
))
}
}
// Read additional arguments
if list.nested.len() >= 3 {
@ -115,7 +125,10 @@ fn extract_pyfn_attrs(
.arguments;
}
} else {
panic!("can not parse 'pyfn' params {:?}", attr);
return Err(syn::Error::new_spanned(
attr,
format!("can not parse 'pyfn' params {:?}", attr),
));
}
}
_ => new_attrs.push(attr.clone()),
@ -123,7 +136,10 @@ fn extract_pyfn_attrs(
}
*attrs = new_attrs;
Some((modname?, fnname?, fn_attrs))
match (modname, fnname) {
(Some(modname), Some(fnname)) => Ok(Some((modname, fnname, fn_attrs))),
_ => Ok(None),
}
}
/// Coordinates the naming of a the add-function-to-python-module function

View file

@ -73,7 +73,7 @@ pub(crate) fn impl_method_proto(
cls: &syn::Type,
sig: &mut syn::Signature,
meth: &MethodProto,
) -> TokenStream {
) -> syn::Result<TokenStream> {
let ret_ty = match &sig.output {
syn::ReturnType::Default => quote! { () },
syn::ReturnType::Type(_, ty) => {
@ -83,7 +83,7 @@ pub(crate) fn impl_method_proto(
}
};
match *meth {
let toks = match *meth {
MethodProto::Free { proto, .. } => {
let p: syn::Path = syn::parse_str(proto).unwrap();
quote! {
@ -109,7 +109,7 @@ pub(crate) fn impl_method_proto(
let p: syn::Path = syn::parse_str(proto).unwrap();
let slf_name = syn::Ident::new(arg, Span::call_site());
let slf_ty = get_arg_ty(sig, 0);
let slf_ty = get_arg_ty(sig, 0)?;
let tmp: syn::ItemFn = syn::parse_quote! {
fn test(&self) -> <#cls as #p<'p>>::Result {}
};
@ -132,12 +132,12 @@ pub(crate) fn impl_method_proto(
MethodProto::Binary { name, arg, proto } => {
if sig.inputs.len() <= 1 {
println!("Not enough arguments for {}", name);
return TokenStream::new();
return Ok(TokenStream::new());
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg_name = syn::Ident::new(arg, Span::call_site());
let arg_ty = get_arg_ty(sig, 1);
let arg_ty = get_arg_ty(sig, 1)?;
let tmp = extract_decl(syn::parse_quote! {
fn test(&self,arg: <#cls as #p<'p>>::#arg_name)-> <#cls as #p<'p>>::Result {}
@ -147,7 +147,7 @@ pub(crate) fn impl_method_proto(
fn test(&self, arg: Option<<#cls as #p<'p>>::#arg_name>) -> <#cls as #p<'p>>::Result {}
});
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2)?;
modify_self_ty(sig);
quote! {
@ -165,13 +165,13 @@ pub(crate) fn impl_method_proto(
} => {
if sig.inputs.len() <= 1 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
return Ok(TokenStream::new());
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 0);
let arg1_ty = get_arg_ty(sig, 0)?;
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 1);
let arg2_ty = get_arg_ty(sig, 1)?;
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
@ -182,8 +182,8 @@ pub(crate) fn impl_method_proto(
arg1: Option<<#cls as #p<'p>>::#arg1_name>,
arg2: Option<<#cls as #p<'p>>::#arg2_name>)
-> <#cls as #p<'p>>::Result {}});
modify_arg_ty(sig, 0, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 0, &tmp, &tmp2)?;
modify_arg_ty(sig, 1, &tmp, &tmp2)?;
quote! {
impl<'p> #p<'p> for #cls {
@ -201,13 +201,13 @@ pub(crate) fn impl_method_proto(
} => {
if sig.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
return Ok(TokenStream::new());
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 1);
let arg1_ty = get_arg_ty(sig, 1)?;
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 2);
let arg2_ty = get_arg_ty(sig, 2)?;
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
@ -220,8 +220,8 @@ pub(crate) fn impl_method_proto(
arg1: Option<<#cls as #p<'p>>::#arg1_name>,
arg2: Option<<#cls as #p<'p>>::#arg2_name>)
-> <#cls as #p<'p>>::Result {}});
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2)?;
modify_arg_ty(sig, 2, &tmp, &tmp2)?;
modify_self_ty(sig);
quote! {
@ -241,15 +241,15 @@ pub(crate) fn impl_method_proto(
} => {
if sig.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
return Ok(TokenStream::new());
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 0);
let arg1_ty = get_arg_ty(sig, 0)?;
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 1);
let arg2_ty = get_arg_ty(sig, 1)?;
let arg3_name = syn::Ident::new(arg3, Span::call_site());
let arg3_ty = get_arg_ty(sig, 2);
let arg3_ty = get_arg_ty(sig, 2)?;
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
@ -262,9 +262,9 @@ pub(crate) fn impl_method_proto(
arg2: Option<<#cls as #p<'p>>::#arg2_name>,
arg3: Option<<#cls as #p<'p>>::#arg3_name>)
-> <#cls as #p<'p>>::Result {}});
modify_arg_ty(sig, 0, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
modify_arg_ty(sig, 0, &tmp, &tmp2)?;
modify_arg_ty(sig, 1, &tmp, &tmp2)?;
modify_arg_ty(sig, 2, &tmp, &tmp2)?;
quote! {
impl<'p> #p<'p> for #cls {
@ -284,15 +284,15 @@ pub(crate) fn impl_method_proto(
} => {
if sig.inputs.len() <= 3 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
return Ok(TokenStream::new());
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 1);
let arg1_ty = get_arg_ty(sig, 1)?;
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 2);
let arg2_ty = get_arg_ty(sig, 2)?;
let arg3_name = syn::Ident::new(arg3, Span::call_site());
let arg3_ty = get_arg_ty(sig, 3);
let arg3_ty = get_arg_ty(sig, 3)?;
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
@ -307,9 +307,9 @@ pub(crate) fn impl_method_proto(
arg2: Option<<#cls as #p<'p>>::#arg2_name>,
arg3: Option<<#cls as #p<'p>>::#arg3_name>)
-> <#cls as #p<'p>>::Result {}});
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
modify_arg_ty(sig, 3, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2)?;
modify_arg_ty(sig, 2, &tmp, &tmp2)?;
modify_arg_ty(sig, 3, &tmp, &tmp2)?;
modify_self_ty(sig);
quote! {
@ -321,11 +321,12 @@ pub(crate) fn impl_method_proto(
}
}
}
}
};
Ok(toks)
}
/// Some hacks for arguments: get `T` from `Option<T>` and insert lifetime
fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Type {
fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Result<syn::Type> {
fn get_option_ty(path: &syn::Path) -> Option<syn::Type> {
let seg = path.segments.last()?;
if seg.ident == "Option" {
@ -344,10 +345,15 @@ fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Type {
syn::Type::Path(ref ty) => get_option_ty(&ty.path).unwrap_or_else(|| *cap.ty.clone()),
_ => *cap.ty.clone(),
},
ty => panic!("Unsupported argument type: {:?}", ty),
ty => {
return Err(syn::Error::new_spanned(
ty,
format!("Unsupported argument type: {:?}", ty),
))
}
};
insert_lifetime(&mut ty);
ty
Ok(ty)
}
/// Insert lifetime `'p` to `PyRef<Self>` or references (e.g., `&PyType`).
@ -395,26 +401,27 @@ fn modify_arg_ty(
idx: usize,
decl1: &syn::Signature,
decl2: &syn::Signature,
) {
) -> syn::Result<()> {
let arg = sig.inputs[idx].clone();
match arg {
syn::FnArg::Typed(ref cap) => match *cap.ty {
syn::Type::Path(ref typath) => {
let seg = typath.path.segments.last().unwrap().clone();
if seg.ident == "Option" {
sig.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx]);
sig.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx])?;
} else {
sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx])?;
}
}
_ => {
sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx])?;
}
},
_ => panic!("not supported"),
_ => return Err(syn::Error::new_spanned(arg, "not supported")),
}
sig.output = decl1.output.clone();
Ok(())
}
fn modify_self_ty(sig: &mut syn::Signature) {
@ -426,15 +433,15 @@ fn modify_self_ty(sig: &mut syn::Signature) {
}
}
fn fix_name(pat: &syn::Pat, arg: &syn::FnArg) -> syn::FnArg {
fn fix_name(pat: &syn::Pat, arg: &syn::FnArg) -> syn::Result<syn::FnArg> {
if let syn::FnArg::Typed(ref cap) = arg {
syn::FnArg::Typed(syn::PatType {
Ok(syn::FnArg::Typed(syn::PatType {
attrs: cap.attrs.clone(),
pat: Box::new(pat.clone()),
colon_token: cap.colon_token,
ty: cap.ty.clone(),
})
}))
} else {
panic!("func.rs::296")
Err(syn::Error::new_spanned(arg, "Expected a typed argument"))
}
}

View file

@ -67,7 +67,7 @@ fn impl_proto_impl(
if let syn::ImplItem::Method(ref mut met) = iimpl {
// impl Py~Protocol<'p> { type = ... }
if let Some(m) = proto.get_proto(&met.sig.ident) {
impl_method_proto(ty, &mut met.sig, m).to_tokens(&mut trait_impls);
impl_method_proto(ty, &mut met.sig, m)?.to_tokens(&mut trait_impls);
// Insert the method to the HashSet
method_names.insert(met.sig.ident.to_string());
}