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| ¶m.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}