ext_trait_proc_macros/
mod.rs

1//! Crate not intended for direct use.
2//! Use https:://docs.rs/ext-trait instead.
3#![allow(nonstandard_style, unused_imports)]
4
5use ::core::{
6    ops::Not as _,
7};
8use ::proc_macro::{
9    TokenStream,
10};
11use ::proc_macro2::{
12    Span,
13    TokenStream as TokenStream2,
14    TokenTree as TT,
15};
16use ::quote::{
17    format_ident,
18    quote,
19    quote_spanned,
20    ToTokens,
21};
22use ::syn::{*,
23    parse::{Parse, Parser, ParseStream},
24    punctuated::Punctuated,
25    spanned::Spanned,
26    Result, // Explicitly shadow it
27};
28
29///
30#[proc_macro_attribute] pub
31fn extension (
32    attrs: TokenStream,
33    input: TokenStream,
34) -> TokenStream
35{
36    extension_impl(attrs.into(), input.into())
37    //  .map(|ret| { println!("{}", ret); ret })
38        .unwrap_or_else(|err| {
39            let mut errors =
40                err .into_iter()
41                    .map(|err| Error::new(
42                        err.span(),
43                        format_args!("`#[extension(trait …)]`: {}", err),
44                    ))
45            ;
46            let mut err = errors.next().unwrap();
47            errors.for_each(|cur| err.combine(cur));
48            err.to_compile_error()
49        })
50        .into()
51}
52
53struct Attrs {
54    pub_: Visibility,
55    trait_: Token![trait],
56    TraitName: Ident,
57}
58
59impl Parse for Attrs {
60    fn parse (input: ParseStream<'_>)
61      -> Result<Attrs>
62    {
63        Ok(Self {
64            pub_: input.parse()?,
65            trait_: input.parse()?,
66            TraitName: input.parse()?,
67        })
68    }
69}
70
71/// Example
72#[cfg(any())]
73const _: () = {
74    use ::ext_trait::extension;
75
76    #[extension(trait disregard_err)]
77    impl<T, E> Result<T, E> {
78        fn disregard_err(self) -> Option<T> { self }
79    }
80};
81
82fn extension_impl (
83    attrs: TokenStream2,
84    input: TokenStream2,
85) -> Result<TokenStream2>
86{
87    let trait_def_span = attrs.span();
88    let Attrs { pub_, trait_, TraitName } = parse2(attrs)?;
89    let ref mut item_impl: ItemImpl = parse2(input)?;
90    let (intro_generics, fwd_generics, where_clause) = item_impl.generics.split_for_impl();
91    match Option::replace(
92        &mut item_impl.trait_,
93        (None, parse_quote!( #TraitName #fwd_generics ), <_>::default()),
94    )
95    {
96        | Some((_, _, extraneous_for)) => return Err(Error::new_spanned(
97            extraneous_for,
98            "expected inherent `impl<…> Type<…>` syntax",
99        )),
100        | _ => {},
101    }
102    let ref item_impl = item_impl;
103    let each_entry = item_impl.items.iter().map(|it| Ok(match it {
104        // We don't deny `pub_` and `default_` annotations *directly*:
105        // instead, we forward their extraneous presence to the `trait`
106        // definition, so as to trigger an grammar error from the following
107        // rust parser pass, which ought to yield a way nicer error message.
108        | ImplItem::Const(ImplItemConst {
109            vis: pub_,
110            defaultness: default_,
111            const_token: const_,
112            ident: CONST_NAME @ _,
113            ty: Ty @ _,
114            ..
115        }) => quote!(
116            #pub_
117            #default_
118            #const_ #CONST_NAME: #Ty;
119        ),
120
121        | ImplItem::Method(ImplItemMethod {
122            vis: pub_,
123            defaultness: default_,
124            sig,
125            ..
126        }) => {
127            let mut sig = sig.clone();
128            sig.inputs.iter_mut().for_each(|fn_arg| match fn_arg {
129                | FnArg::Receiver(Receiver { reference, mutability, .. }) => {
130                    if reference.is_none() {
131                        *mutability = None;
132                    }
133                },
134                | FnArg::Typed(PatType { pat, .. }) => {
135                    *pat = parse_quote!( _ );
136                },
137            });
138            quote!(
139                #pub_
140                #default_
141                #sig;
142            )
143        },
144
145        | ImplItem::Type(ImplItemType {
146            vis: pub_,
147            defaultness: default_,
148            type_token: type_,
149            ident: TypeName @ _,
150            generics,
151            semi_token: SEMICOLON @ _,
152            ..
153        }) => quote! (
154            #pub_
155            #default_
156            #type_ #TypeName #generics
157            :
158                ?::ext_trait::__::core::marker::Sized
159            #SEMICOLON
160        ),
161
162        | _ => return Err(Error::new_spanned(it, "unsupported `impl` entry")),
163    })).collect::<Result<Vec<_>>>()?;
164    Ok(quote_spanned!(trait_def_span=>
165        #[allow(nonstandard_style)]
166        #pub_
167        #trait_ #TraitName #intro_generics
168        #where_clause
169        {
170            #(#each_entry)*
171        }
172
173        #item_impl
174    ))
175}