Revision control
Copy as Markdown
Other Tools
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::<VerbatimFn>(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::<VerbatimFn>(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!(<Self as #trait_>::#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
}
}