pyo3_stub_gen_derive/gen_stub/
method.rs1use 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, Clone, Copy, PartialEq)]
13pub enum MethodType {
14 Instance,
15 Static,
16 Class,
17 New,
18}
19
20#[derive(Debug)]
21pub struct MethodInfo {
22 name: String,
23 args: Vec<ArgInfo>,
24 sig: Option<Signature>,
25 r#return: Option<Type>,
26 doc: String,
27 r#type: MethodType,
28}
29
30fn replace_inner(ty: &mut Type, self_: &Type) {
31 match ty {
32 Type::Path(TypePath { path, .. }) => {
33 if let Some(last) = path.segments.last_mut() {
34 if let PathArguments::AngleBracketed(arg) = &mut last.arguments {
35 for arg in &mut arg.args {
36 if let GenericArgument::Type(ty) = arg {
37 replace_inner(ty, self_);
38 }
39 }
40 }
41 if last.ident == "Self" {
42 *ty = self_.clone();
43 }
44 }
45 }
46 Type::Reference(TypeReference { elem, .. }) => {
47 replace_inner(elem, self_);
48 }
49 _ => {}
50 }
51}
52
53impl MethodInfo {
54 pub fn replace_self(&mut self, self_: &Type) {
55 for arg in &mut self.args {
56 replace_inner(&mut arg.r#type, self_);
57 }
58 if let Some(ret) = self.r#return.as_mut() {
59 replace_inner(ret, self_);
60 }
61 }
62}
63
64impl TryFrom<ImplItemFn> for MethodInfo {
65 type Error = Error;
66 fn try_from(item: ImplItemFn) -> Result<Self> {
67 let ImplItemFn { attrs, sig, .. } = item;
68 let doc = extract_documents(&attrs).join("\n");
69 let attrs = parse_pyo3_attrs(&attrs)?;
70 let mut method_name = None;
71 let mut text_sig = Signature::overriding_operator(&sig);
72 let mut method_type = MethodType::Instance;
73 for attr in attrs {
74 match attr {
75 Attr::Name(name) => method_name = Some(name),
76 Attr::Signature(text_sig_) => text_sig = Some(text_sig_),
77 Attr::StaticMethod => method_type = MethodType::Static,
78 Attr::ClassMethod => method_type = MethodType::Class,
79 Attr::New => method_type = MethodType::New,
80 _ => {}
81 }
82 }
83 let name = if method_type == MethodType::New {
84 "__new__".to_string()
85 } else {
86 method_name.unwrap_or(sig.ident.to_string())
87 };
88 let r#return = escape_return_type(&sig.output);
89 Ok(MethodInfo {
90 name,
91 sig: text_sig,
92 args: parse_args(sig.inputs)?,
93 r#return,
94 doc,
95 r#type: method_type,
96 })
97 }
98}
99
100impl ToTokens for MethodInfo {
101 fn to_tokens(&self, tokens: &mut TokenStream2) {
102 let Self {
103 name,
104 r#return: ret,
105 args,
106 sig,
107 doc,
108 r#type,
109 } = self;
110 let args_with_sig = ArgsWithSignature { args, sig };
111 let ret_tt = if let Some(ret) = ret {
112 quote! { <#ret as pyo3_stub_gen::PyStubType>::type_output }
113 } else {
114 quote! { ::pyo3_stub_gen::type_info::no_return_type_output }
115 };
116 let type_tt = match r#type {
117 MethodType::Instance => quote! { ::pyo3_stub_gen::type_info::MethodType::Instance },
118 MethodType::Static => quote! { ::pyo3_stub_gen::type_info::MethodType::Static },
119 MethodType::Class => quote! { ::pyo3_stub_gen::type_info::MethodType::Class },
120 MethodType::New => quote! { ::pyo3_stub_gen::type_info::MethodType::New },
121 };
122 tokens.append_all(quote! {
123 ::pyo3_stub_gen::type_info::MethodInfo {
124 name: #name,
125 args: #args_with_sig,
126 r#return: #ret_tt,
127 doc: #doc,
128 r#type: #type_tt
129 }
130 })
131 }
132}