1#![allow(nonstandard_style, unused_braces, unused_imports)]
4
5use ::core::{
6 mem,
7 ops::Not as _,
8};
9use ::proc_macro::{
10 TokenStream,
11};
12use ::proc_macro2::{
13 Span,
14 TokenStream as TokenStream2,
15 TokenTree as TT,
16};
17use ::quote::{
18 format_ident,
19 quote,
20 quote_spanned,
21 ToTokens,
22};
23use ::syn::{*,
24 parse::{Parse, Parser, ParseStream},
25 punctuated::Punctuated,
26 spanned::Spanned,
27 Result, };
29
30#[proc_macro_attribute] pub
32fn extension (
33 attrs: TokenStream,
34 input: TokenStream,
35) -> TokenStream
36{
37 extension_impl(attrs.into(), input.into())
38 .unwrap_or_else(|err| {
40 let mut errors =
41 err .into_iter()
42 .map(|err| Error::new(
43 err.span(),
44 format_args!("`#[extension(trait …)]`: {}", err),
45 ))
46 ;
47 let mut err = errors.next().unwrap();
48 errors.for_each(|cur| err.combine(cur));
49 err.to_compile_error()
50 })
51 .into()
52}
53
54struct Attrs {
55 pub_: Visibility,
56 trait_: Token![trait],
57 TraitName: Ident,
58}
59
60impl Parse for Attrs {
61 fn parse (input: ParseStream<'_>)
62 -> Result<Attrs>
63 {
64 Ok(Self {
65 pub_: input.parse()?,
66 trait_: input.parse()?,
67 TraitName: input.parse()?,
68 })
69 }
70}
71
72#[cfg(any())]
74const _: () = {
75 use ::ext_trait::extension;
76
77 #[extension(trait disregard_err)]
78 impl<T, E> Result<T, E> {
79 fn disregard_err(self) -> Option<T> { self }
80 }
81};
82
83fn extension_impl (
84 attrs: TokenStream2,
85 input: TokenStream2,
86) -> Result<TokenStream2>
87{
88 let trait_def_span = attrs.span();
89 let Attrs { pub_, trait_, TraitName } = parse2(attrs)?;
90 let ref mut item_impl: ItemImpl = parse2(input)?;
91 let attrs = mem::take(&mut item_impl.attrs);
92 item_impl.attrs = attrs_to_forward_to_impl_block(&attrs);
93 let (intro_generics, fwd_generics, where_clause) = item_impl.generics.split_for_impl();
94 match Option::replace(
95 &mut item_impl.trait_,
96 (None, parse_quote!( #TraitName #fwd_generics ), <_>::default()),
97 )
98 {
99 | Some((_, _, extraneous_for)) => return Err(Error::new_spanned(
100 extraneous_for,
101 "expected inherent `impl<…> Type<…>` syntax",
102 )),
103 | _ => {},
104 }
105 let ref item_impl = item_impl;
106 let each_entry = item_impl.items.iter().map(|it| Ok(match it {
107 | ImplItem::Const(ImplItemConst {
112 vis: pub_,
113 defaultness: default_,
114 const_token: const_,
115 ident: CONST_NAME @ _,
116 ty: Ty @ _,
117 attrs,
118 ..
119 }) => {
120 let attrs = attrs_to_forward_to_trait_items(attrs);
121 quote!(
122 #(#attrs)*
123 #pub_
124 #default_
125 #const_ #CONST_NAME: #Ty;
126 )
127 }
128
129 | ImplItem::Method(ImplItemMethod {
130 vis: pub_,
131 defaultness: default_,
132 sig,
133 attrs,
134 ..
135 }) => {
136 let attrs = attrs_to_forward_to_trait_items(attrs);
137 let mut sig = sig.clone();
138 sig.inputs.iter_mut().for_each(|fn_arg| match fn_arg {
139 | FnArg::Receiver(Receiver { reference, mutability, .. }) => {
140 if reference.is_none() {
141 *mutability = None;
142 }
143 },
144 | FnArg::Typed(PatType { pat, .. }) => {
145 *pat = parse_quote!( _ );
146 },
147 });
148 quote!(
149 #(#attrs)*
150 #pub_
151 #default_
152 #sig;
153 )
154 },
155
156 | ImplItem::Type(ImplItemType {
157 vis: pub_,
158 defaultness: default_,
159 type_token: type_,
160 ident: TypeName @ _,
161 generics,
162 semi_token: SEMICOLON @ _,
163 attrs,
164 ..
165 }) => {
166 let attrs = attrs_to_forward_to_trait_items(attrs);
167 quote! (
168 #(#attrs)*
169 #pub_
170 #default_
171 #type_ #TypeName #generics
172 :
173 ?::ext_trait::__::core::marker::Sized
174 #SEMICOLON
175 )
176 },
177
178 | _ => return Err(Error::new_spanned(it, "unsupported `impl` entry")),
179 })).collect::<Result<Vec<_>>>()?;
180 let ItemImpl { self_ty: ref Receiver, .. } = *item_impl;
181 let docs_addendum = format!(r#"
182
183This is an extension trait for the following impl:
184```rust ,ignore
185#[extension(pub trait {TraitName})]
186impl{intro_generics} for {Receiver}
187{maybe_where}{where_clauses}
188```"#,
189 intro_generics = intro_generics.to_token_stream(),
190 Receiver = Receiver.to_token_stream(),
191 maybe_where = if where_clause.is_some() { "where" } else { "" },
192 where_clauses =
193 where_clause
194 .iter()
195 .flat_map(|w| w.predicates.iter().map(|p| format!("\n {}", p.to_token_stream())))
196 .collect::<String>()
197 ,
198 );
199 Ok(quote_spanned!(trait_def_span=>
200 #(#attrs)*
201 #[doc = #docs_addendum]
202 #[allow(nonstandard_style)]
203 #pub_
204 #trait_ #TraitName #intro_generics
205 #where_clause
206 {
207 #(#each_entry)*
208 }
209
210 #item_impl
211 ))
212}
213
214fn attrs_to_forward_to_impl_block(
215 trait_attrs: &[Attribute],
216) -> Vec<Attribute>
217{
218 const IMPL_BLOCK_ATTRS_ALLOW_LIST: &[&str] = &[
219 "doc",
220 "allow",
221 "warn",
222 "deny",
223 "forbid",
224 "async_trait",
225 ];
226
227 trait_attrs.iter().filter(|attr| IMPL_BLOCK_ATTRS_ALLOW_LIST.iter().any(|ident| {
228 attr.path.is_ident(ident)
229 })).cloned().collect()
230}
231
232fn attrs_to_forward_to_trait_items(
233 impl_block_assoc_item_attrs: &[Attribute],
234) -> Vec<&Attribute>
235{
236 const TRAIT_ITEMS_ATTRS_DENY_LIST: &[&str] = &[
237 "inline",
238 ];
239
240 impl_block_assoc_item_attrs.iter().filter(|attr| TRAIT_ITEMS_ATTRS_DENY_LIST.iter().all(|ident| {
241 attr.path.is_ident(ident).not()
242 })).collect()
243}