inherent/
expand.rs

1use crate::parse::TraitImpl;
2use crate::verbatim::VerbatimFn;
3use proc_macro2::{Span, TokenStream};
4use quote::{quote, quote_spanned};
5use syn::{Attribute, FnArg, Ident, ImplItem, Pat, Path, Signature, Visibility};
6
7pub fn inherent(mut input: TraitImpl) -> TokenStream {
8    let impl_token = &input.impl_token;
9    let generics = &input.generics;
10    let where_clause = &input.generics.where_clause;
11    let trait_ = &input.trait_;
12    let ty = &input.self_ty;
13
14    let fwd_methods: Vec<_> = input
15        .items
16        .iter()
17        .filter_map(|item| match item {
18            ImplItem::Fn(method) => Some(fwd_method(
19                trait_,
20                &method.attrs,
21                &method.vis,
22                &method.sig,
23                method.block.brace_token.span.join(),
24            )),
25            ImplItem::Verbatim(tokens) => {
26                if let Ok(method) = syn::parse2::<VerbatimFn>(tokens.clone()) {
27                    Some(fwd_method(
28                        trait_,
29                        &method.attrs,
30                        &method.vis,
31                        &method.sig,
32                        method.semi_token.span,
33                    ))
34                } else {
35                    None
36                }
37            }
38            _ => None,
39        })
40        .collect();
41
42    input.items = input
43        .items
44        .into_iter()
45        .filter_map(|item| match item {
46            ImplItem::Fn(mut method) => {
47                method.vis = Visibility::Inherited;
48                Some(ImplItem::Fn(method))
49            }
50            ImplItem::Verbatim(tokens) => {
51                if syn::parse2::<VerbatimFn>(tokens.clone()).is_ok() {
52                    None
53                } else {
54                    Some(ImplItem::Verbatim(tokens))
55                }
56            }
57            item => Some(item),
58        })
59        .collect();
60
61    let body = quote_spanned!(input.brace_token.span=> { #(#fwd_methods)* });
62
63    quote! {
64        #impl_token #generics #ty #where_clause #body
65
66        #input
67    }
68}
69
70fn fwd_method(
71    trait_: &Path,
72    attrs: &[Attribute],
73    vis: &Visibility,
74    sig: &Signature,
75    body_span: Span,
76) -> TokenStream {
77    let constness = &sig.constness;
78    let asyncness = &sig.asyncness;
79    let unsafety = &sig.unsafety;
80    let abi = &sig.abi;
81    let fn_token = sig.fn_token;
82    let ident = &sig.ident;
83    let generics = &sig.generics;
84    let output = &sig.output;
85    let where_clause = &sig.generics.where_clause;
86
87    let (arg_pat, arg_val): (Vec<_>, Vec<_>) = sig
88        .inputs
89        .pairs()
90        .enumerate()
91        .map(|(i, pair)| {
92            let (input, comma_token) = pair.into_tuple();
93            match input {
94                FnArg::Receiver(receiver) => {
95                    let self_token = receiver.self_token;
96                    if receiver.reference.is_some() {
97                        (quote!(#receiver #comma_token), quote!(#self_token))
98                    } else {
99                        (quote!(#self_token #comma_token), quote!(#self_token))
100                    }
101                }
102                FnArg::Typed(arg) => {
103                    let var = match arg.pat.as_ref() {
104                        Pat::Ident(pat) => pat.ident.clone(),
105                        _ => Ident::new(&format!("__arg{}", i), Span::call_site()),
106                    };
107                    let colon_token = arg.colon_token;
108                    let ty = &arg.ty;
109                    (quote!(#var #colon_token #ty #comma_token), quote!(#var))
110                }
111            }
112        })
113        .unzip();
114
115    let types = generics.type_params().map(|param| &param.ident);
116    let wait = asyncness.map(|_| quote!(.await));
117    let body = quote!(<Self as #trait_>::#ident::<#(#types,)*>(#(#arg_val,)*) #wait);
118    let block = quote_spanned!(body_span=> { #body });
119    let args = quote_spanned!(sig.paren_token.span=> (#(#arg_pat)*));
120
121    let has_doc = attrs.iter().any(|attr| attr.path().is_ident("doc"));
122    let default_doc = if has_doc {
123        None
124    } else {
125        let mut link = String::new();
126        for segment in &trait_.segments {
127            link += &segment.ident.to_string();
128            link += "::";
129        }
130        let msg = format!("See [`{}{}`]", link, ident);
131        Some(quote!(#[doc = #msg]))
132    };
133
134    quote! {
135        #(#attrs)*
136        #default_doc
137        #vis #constness #asyncness #unsafety #abi #fn_token #ident #generics #args #output #where_clause #block
138    }
139}