1use super::{
2 arg::parse_args, escape_return_type, extract_documents, parse_pyo3_attrs, ArgInfo,
3 ArgsWithSignature, Attr, Signature,
4};
5
6use proc_macro2::TokenStream as TokenStream2;
7use quote::{quote, ToTokens, TokenStreamExt};
8use syn::{
9 Error, GenericArgument, ImplItemFn, PathArguments, Result, Type, TypePath, TypeReference,
10};
11
12#[derive(Debug)]
13pub struct MethodInfo {
14 name: String,
15 args: Vec<ArgInfo>,
16 sig: Option<Signature>,
17 r#return: Option<Type>,
18 doc: String,
19 is_static: bool,
20 is_class: bool,
21}
22
23fn replace_inner(ty: &mut Type, self_: &Type) {
24 match ty {
25 Type::Path(TypePath { path: &mut Path, .. }) => {
26 if let Some(last: &mut PathSegment) = path.segments.iter_mut().last() {
27 if let PathArguments::AngleBracketed(arg: &mut AngleBracketedGenericArguments) = &mut last.arguments {
28 for arg: &mut GenericArgument in &mut arg.args {
29 if let GenericArgument::Type(ty: &mut Type) = arg {
30 replace_inner(ty, self_);
31 }
32 }
33 }
34 if last.ident == "Self" {
35 *ty = self_.clone();
36 }
37 }
38 }
39 Type::Reference(TypeReference { elem: &mut Box, .. }) => {
40 replace_inner(ty:elem, self_);
41 }
42 _ => {}
43 }
44}
45
46impl MethodInfo {
47 pub fn replace_self(&mut self, self_: &Type) {
48 for arg: &mut ArgInfo in &mut self.args {
49 replace_inner(&mut arg.r#type, self_);
50 }
51 if let Some(ret: &mut Type) = self.r#return.as_mut() {
52 replace_inner(ty:ret, self_);
53 }
54 }
55}
56
57impl TryFrom<ImplItemFn> for MethodInfo {
58 type Error = Error;
59 fn try_from(item: ImplItemFn) -> Result<Self> {
60 let ImplItemFn { attrs, sig, .. } = item;
61 let doc = extract_documents(&attrs).join("\n");
62 let attrs = parse_pyo3_attrs(&attrs)?;
63 let mut method_name = None;
64 let mut text_sig = Signature::overriding_operator(&sig);
65 let mut is_static = false;
66 let mut is_class = false;
67 for attr in attrs {
68 match attr {
69 Attr::Name(name) => method_name = Some(name),
70 Attr::Signature(text_sig_) => text_sig = Some(text_sig_),
71 Attr::StaticMethod => is_static = true,
72 Attr::ClassMethod => is_class = true,
73 _ => {}
74 }
75 }
76 let name = method_name.unwrap_or(sig.ident.to_string());
77 let r#return = escape_return_type(&sig.output);
78 Ok(MethodInfo {
79 name,
80 sig: text_sig,
81 args: parse_args(sig.inputs)?,
82 r#return,
83 doc,
84 is_static,
85 is_class,
86 })
87 }
88}
89
90impl ToTokens for MethodInfo {
91 fn to_tokens(&self, tokens: &mut TokenStream2) {
92 let Self {
93 name,
94 r#return: ret,
95 args,
96 sig,
97 doc,
98 is_class,
99 is_static,
100 } = self;
101 let args_with_sig = ArgsWithSignature { args, sig };
102 let ret_tt = if let Some(ret) = ret {
103 quote! { <#ret as pyo3_stub_gen::PyStubType>::type_output }
104 } else {
105 quote! { ::pyo3_stub_gen::type_info::no_return_type_output }
106 };
107 tokens.append_all(quote! {
108 ::pyo3_stub_gen::type_info::MethodInfo {
109 name: #name,
110 args: #args_with_sig,
111 r#return: #ret_tt,
112 doc: #doc,
113 is_static: #is_static,
114 is_class: #is_class,
115 }
116 })
117 }
118}
119