From c063fc3d64422248888d4ec66d1b09adcb779132 Mon Sep 17 00:00:00 2001 From: jfh Date: Mon, 13 Sep 2021 22:10:02 +0300 Subject: [PATCH] Try and generate a text signature template for both methods and functions. --- derive/src/pyclass.rs | 46 ++++++++---------------------------------- derive/src/pymodule.rs | 21 +++++++++++++------ derive/src/util.rs | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 44 deletions(-) diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 21da59ba4..3f5cade48 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -1,7 +1,7 @@ use super::Diagnostic; use crate::util::{ - path_eq, pyclass_ident_and_attrs, ClassItemMeta, ContentItem, ContentItemInner, ErrorVec, - ItemMeta, ItemMetaInner, ItemNursery, SimpleItemMeta, ALL_ALLOWED_NAMES, + get_func_sig, path_eq, pyclass_ident_and_attrs, ClassItemMeta, ContentItem, ContentItemInner, + ErrorVec, ItemMeta, ItemMetaInner, ItemNursery, SimpleItemMeta, ALL_ALLOWED_NAMES, }; use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; @@ -367,43 +367,13 @@ where let item_attr = args.attrs.remove(self.index()); let item_meta = MethodItemMeta::from_attr(ident.clone(), &item_attr)?; - let py_name = item_meta.method_name()?; - let sig_doc = args.item.function_or_method_impl().ok().map(|item| { - let sig = item.sig(); - let args: Vec<_> = sig - .inputs - .iter() - .filter_map(|arg| { - use syn::FnArg::*; - let arg = match arg { - Receiver(_) => return Some("$self".to_owned()), - Typed(typed) => typed, - }; - let ty = arg.ty.as_ref(); - let ty = quote!(#ty).to_string(); - if ty == "FuncArgs" { - return Some("*args, **kwargs".to_owned()); - } - if ty == "& VirtualMachine" { - return None; - } - let ident = match arg.pat.as_ref() { - syn::Pat::Ident(p) => p.ident.to_string(), - // FIXME: other => unreachable!("function arg pattern must be ident but found `{}`", quote!(fn #ident(.. #other ..))), - other => quote!(#other).to_string(), - }; - if ident == "zelf" { - return Some("$self".to_owned()); - } - if ident == "vm" { - unreachable!("type &VirtualMachine(`{}`) must be filtered already", ty); - } - Some(ident) - }) - .collect(); - format!("{}({})", py_name, args.join(", ")) - }); + let py_name = item_meta.method_name()?; + let sig_doc = args + .item + .function_or_method_impl() + .ok() + .map(|f| get_func_sig(f.sig(), &py_name)); let tokens = { let doc = args.attrs.doc().map_or_else(TokenStream::new, |mut doc| { diff --git a/derive/src/pymodule.rs b/derive/src/pymodule.rs index ff2edff1e..cbc38304a 100644 --- a/derive/src/pymodule.rs +++ b/derive/src/pymodule.rs @@ -1,7 +1,8 @@ use crate::error::Diagnostic; use crate::util::{ - iter_use_idents, pyclass_ident_and_attrs, AttributeExt, ClassItemMeta, ContentItem, - ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta, ALL_ALLOWED_NAMES, + get_func_sig, iter_use_idents, pyclass_ident_and_attrs, AttributeExt, ClassItemMeta, + ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta, + ALL_ALLOWED_NAMES, }; use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; @@ -257,11 +258,19 @@ impl ModuleItem for FunctionItem { let item_meta = SimpleItemMeta::from_attr(ident.clone(), &item_attr)?; let py_name = item_meta.simple_name()?; + let sig_doc = args + .item + .function_or_method_impl() + .ok() + .map(|f| get_func_sig(f.sig(), &py_name)); + let item = { - let doc = args.attrs.doc().map_or_else( - TokenStream::new, - |doc| quote!(.with_doc(#doc.to_owned(), &vm.ctx)), - ); + let doc = args.attrs.doc().map_or_else(TokenStream::new, |mut doc| { + if let Some(sig_doc) = sig_doc { + doc = format!("{}\n--\n\n{}", sig_doc, doc); + } + quote!(.with_doc(#doc.to_owned(), &vm.ctx)) + }); let module = args.module_name(); let new_func = quote_spanned!(ident.span()=> vm.ctx.make_funcdef(#py_name, #ident) diff --git a/derive/src/util.rs b/derive/src/util.rs index ac564b53a..37f3f3ada 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -2,6 +2,7 @@ use indexmap::map::IndexMap; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use std::collections::HashMap; +use syn::Signature; use syn::{spanned::Spanned, Attribute, Ident, Meta, MetaList, NestedMeta, Path, Result, UseTree}; use syn_ext::ext::{AttributeExt as SynAttributeExt, *}; use syn_ext::types::PunctuatedNestedMeta; @@ -503,3 +504,40 @@ where } Ok(()) } + +// Best effort attempt to create a template from which a +// __text_signature__ can be created. +pub(crate) fn get_func_sig(sig: &Signature, name: &str) -> String { + let args: Vec<_> = sig + .inputs + .iter() + .filter_map(|arg| { + use syn::FnArg::*; + let arg = match arg { + Receiver(_) => return Some("$self".to_owned()), + Typed(typed) => typed, + }; + let ty = arg.ty.as_ref(); + let ty = quote!(#ty).to_string(); + if ty == "FuncArgs" { + return Some("*args, **kwargs".to_owned()); + } + if ty == "& VirtualMachine" { + return None; + } + let ident = match arg.pat.as_ref() { + syn::Pat::Ident(p) => p.ident.to_string(), + // FIXME: other => unreachable!("function arg pattern must be ident but found `{}`", quote!(fn #ident(.. #other ..))), + other => quote!(#other).to_string(), + }; + if ident == "zelf" { + return Some("$self".to_owned()); + } + if ident == "vm" { + unreachable!("type &VirtualMachine(`{}`) must be filtered already", ty); + } + Some(ident) + }) + .collect(); + format!("{}({})", name, args.join(", ")) +}