157 lines
4.8 KiB
Rust
157 lines
4.8 KiB
Rust
use proc_macro2::Ident;
|
|
use quote::quote;
|
|
use syn::parse::{Parse, ParseStream};
|
|
use syn::punctuated::Punctuated;
|
|
use syn::spanned::Spanned;
|
|
use syn::{
|
|
Attribute, Generics, ImplItem, Pat, PatIdent, Path, Signature, Token, TraitItem,
|
|
TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, Type, Visibility, WhereClause,
|
|
braced, parse_macro_input,
|
|
};
|
|
|
|
pub(crate) fn extension(
|
|
attr: proc_macro::TokenStream,
|
|
input: proc_macro::TokenStream,
|
|
) -> proc_macro::TokenStream {
|
|
let ExtensionAttr { vis, trait_ } = parse_macro_input!(attr as ExtensionAttr);
|
|
let Impl { attrs, generics, self_ty, items, wc } = parse_macro_input!(input as Impl);
|
|
let headers: Vec<_> = items
|
|
.iter()
|
|
.map(|item| match item {
|
|
ImplItem::Fn(f) => TraitItem::Fn(TraitItemFn {
|
|
attrs: scrub_attrs(&f.attrs),
|
|
sig: scrub_header(f.sig.clone()),
|
|
default: None,
|
|
semi_token: Some(Token)),
|
|
}),
|
|
ImplItem::Const(ct) => TraitItem::Const(TraitItemConst {
|
|
attrs: scrub_attrs(&ct.attrs),
|
|
const_token: ct.const_token,
|
|
ident: ct.ident.clone(),
|
|
generics: ct.generics.clone(),
|
|
colon_token: ct.colon_token,
|
|
ty: ct.ty.clone(),
|
|
default: None,
|
|
semi_token: ct.semi_token,
|
|
}),
|
|
ImplItem::Type(ty) => TraitItem::Type(TraitItemType {
|
|
attrs: scrub_attrs(&ty.attrs),
|
|
type_token: ty.type_token,
|
|
ident: ty.ident.clone(),
|
|
generics: ty.generics.clone(),
|
|
colon_token: None,
|
|
bounds: Punctuated::new(),
|
|
default: None,
|
|
semi_token: ty.semi_token,
|
|
}),
|
|
ImplItem::Macro(mac) => TraitItem::Macro(TraitItemMacro {
|
|
attrs: scrub_attrs(&mac.attrs),
|
|
mac: mac.mac.clone(),
|
|
semi_token: mac.semi_token,
|
|
}),
|
|
ImplItem::Verbatim(stream) => TraitItem::Verbatim(stream.clone()),
|
|
_ => unimplemented!(),
|
|
})
|
|
.collect();
|
|
|
|
quote! {
|
|
#(#attrs)*
|
|
#vis trait #trait_ {
|
|
#(#headers)*
|
|
}
|
|
|
|
impl #generics #trait_ for #self_ty #wc {
|
|
#(#items)*
|
|
}
|
|
}
|
|
.into()
|
|
}
|
|
|
|
/// Only keep `#[doc]` attrs.
|
|
fn scrub_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
|
|
attrs
|
|
.into_iter()
|
|
.cloned()
|
|
.filter(|attr| {
|
|
let ident = &attr.path().segments[0].ident;
|
|
ident == "doc" || ident == "must_use"
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Scrub arguments so that they're valid for trait signatures.
|
|
fn scrub_header(mut sig: Signature) -> Signature {
|
|
for (idx, input) in sig.inputs.iter_mut().enumerate() {
|
|
match input {
|
|
syn::FnArg::Receiver(rcvr) => {
|
|
// `mut self` -> `self`
|
|
if rcvr.reference.is_none() {
|
|
rcvr.mutability.take();
|
|
}
|
|
}
|
|
syn::FnArg::Typed(arg) => match &mut *arg.pat {
|
|
Pat::Ident(arg) => {
|
|
// `ref mut ident @ pat` -> `ident`
|
|
arg.by_ref.take();
|
|
arg.mutability.take();
|
|
arg.subpat.take();
|
|
}
|
|
_ => {
|
|
// `pat` -> `__arg0`
|
|
arg.pat = Box::new(
|
|
PatIdent {
|
|
attrs: vec![],
|
|
by_ref: None,
|
|
mutability: None,
|
|
ident: Ident::new(&format!("__arg{idx}"), arg.pat.span()),
|
|
subpat: None,
|
|
}
|
|
.into(),
|
|
)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
sig
|
|
}
|
|
|
|
struct ExtensionAttr {
|
|
vis: Visibility,
|
|
trait_: Path,
|
|
}
|
|
|
|
impl Parse for ExtensionAttr {
|
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
|
let vis = input.parse()?;
|
|
let _: Token![trait] = input.parse()?;
|
|
let trait_ = input.parse()?;
|
|
Ok(ExtensionAttr { vis, trait_ })
|
|
}
|
|
}
|
|
|
|
struct Impl {
|
|
attrs: Vec<Attribute>,
|
|
generics: Generics,
|
|
self_ty: Type,
|
|
items: Vec<ImplItem>,
|
|
wc: Option<WhereClause>,
|
|
}
|
|
|
|
impl Parse for Impl {
|
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
|
let attrs = input.call(Attribute::parse_outer)?;
|
|
let _: Token![impl] = input.parse()?;
|
|
let generics = input.parse()?;
|
|
let self_ty = input.parse()?;
|
|
let wc = input.parse()?;
|
|
|
|
let content;
|
|
let _brace_token = braced!(content in input);
|
|
let mut items = Vec::new();
|
|
while !content.is_empty() {
|
|
items.push(content.parse()?);
|
|
}
|
|
|
|
Ok(Impl { attrs, generics, self_ty, items, wc })
|
|
}
|
|
}
|