use crate::parse::TraitImpl; use crate::verbatim::VerbatimFn; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned}; use syn::{Attribute, FnArg, Ident, ImplItem, Pat, Path, Signature, Visibility}; pub fn inherent(mut input: TraitImpl) -> TokenStream { let impl_token = &input.impl_token; let generics = &input.generics; let where_clause = &input.generics.where_clause; let trait_ = &input.trait_; let ty = &input.self_ty; let fwd_methods: Vec<_> = input .items .iter() .filter_map(|item| match item { ImplItem::Fn(method) => Some(fwd_method( trait_, &method.attrs, &method.vis, &method.sig, method.block.brace_token.span.join(), )), ImplItem::Verbatim(tokens) => { if let Ok(method) = syn::parse2::(tokens.clone()) { Some(fwd_method( trait_, &method.attrs, &method.vis, &method.sig, method.semi_token.span, )) } else { None } } _ => None, }) .collect(); input.items = input .items .into_iter() .filter_map(|item| match item { ImplItem::Fn(mut method) => { method.vis = Visibility::Inherited; Some(ImplItem::Fn(method)) } ImplItem::Verbatim(tokens) => { if syn::parse2::(tokens.clone()).is_ok() { None } else { Some(ImplItem::Verbatim(tokens)) } } item => Some(item), }) .collect(); let body = quote_spanned!(input.brace_token.span=> { #(#fwd_methods)* }); quote! { #impl_token #generics #ty #where_clause #body #input } } fn fwd_method( trait_: &Path, attrs: &[Attribute], vis: &Visibility, sig: &Signature, body_span: Span, ) -> TokenStream { let constness = &sig.constness; let asyncness = &sig.asyncness; let unsafety = &sig.unsafety; let abi = &sig.abi; let fn_token = sig.fn_token; let ident = &sig.ident; let generics = &sig.generics; let output = &sig.output; let where_clause = &sig.generics.where_clause; let (arg_pat, arg_val): (Vec<_>, Vec<_>) = sig .inputs .pairs() .enumerate() .map(|(i, pair)| { let (input, comma_token) = pair.into_tuple(); match input { FnArg::Receiver(receiver) => { let self_token = receiver.self_token; if receiver.reference.is_some() { (quote!(#receiver #comma_token), quote!(#self_token)) } else { (quote!(#self_token #comma_token), quote!(#self_token)) } } FnArg::Typed(arg) => { let var = match arg.pat.as_ref() { Pat::Ident(pat) => pat.ident.clone(), _ => Ident::new(&format!("__arg{}", i), Span::call_site()), }; let colon_token = arg.colon_token; let ty = &arg.ty; (quote!(#var #colon_token #ty #comma_token), quote!(#var)) } } }) .unzip(); let types = generics.type_params().map(|param| ¶m.ident); let body = quote!(::#ident::<#(#types,)*>(#(#arg_val,)*)); let block = quote_spanned!(body_span=> { #body }); let args = quote_spanned!(sig.paren_token.span=> (#(#arg_pat)*)); let has_doc = attrs.iter().any(|attr| attr.path().is_ident("doc")); let default_doc = if has_doc { None } else { let mut link = String::new(); for segment in &trait_.segments { link += &segment.ident.to_string(); link += "::"; } let msg = format!("See [`{}{}`]", link, ident); Some(quote!(#[doc = #msg])) }; quote! { #(#attrs)* #default_doc #vis #constness #asyncness #unsafety #abi #fn_token #ident #generics #args #output #where_clause #block } }