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::Fn(ImplItemFn {
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 if let Pat::Ident(PatIdent { ident: arg_name, .. }) = &mut **pat {
148 **pat = parse_quote!(
152 #arg_name
153 );
154 } else {
155 **pat = parse_quote_spanned!(pat.span()=>
156 _
157 );
158 }
159 },
160 });
161 quote!(
162 #(#attrs)*
163 #pub_
164 #default_
165 #sig;
166 )
167 },
168
169 | ImplItem::Type(ImplItemType {
170 vis: pub_,
171 defaultness: default_,
172 type_token: type_,
173 ident: TypeName @ _,
174 generics,
175 semi_token: SEMICOLON @ _,
176 attrs,
177 ..
178 }) => {
179 let attrs = attrs_to_forward_to_trait_items(attrs);
180 quote! (
181 #(#attrs)*
182 #pub_
183 #default_
184 #type_ #TypeName #generics
185 :
186 ?::ext_trait::__::core::marker::Sized
187 #SEMICOLON
188 )
189 },
190
191 | _ => return Err(Error::new_spanned(it, "unsupported `impl` entry")),
192 })).collect::<Result<Vec<_>>>()?;
193 let ItemImpl { self_ty: ref Receiver, .. } = *item_impl;
194 let docs_addendum = format!(r#"
195
196This is an extension trait for the following impl:
197```rust ,ignore
198#[extension(pub trait {TraitName})]
199impl{intro_generics} for {Receiver}
200{maybe_where}{where_clauses}
201```"#,
202 intro_generics = intro_generics.to_token_stream(),
203 Receiver = Receiver.to_token_stream(),
204 maybe_where = if where_clause.is_some() { "where" } else { "" },
205 where_clauses =
206 where_clause
207 .iter()
208 .flat_map(|w| w.predicates.iter().map(|p| format!("\n {}", p.to_token_stream())))
209 .collect::<String>()
210 ,
211 );
212 Ok(quote_spanned!(trait_def_span=>
213 #(#attrs)*
214 #[doc = #docs_addendum]
215 #[allow(nonstandard_style)]
216 #pub_
217 #trait_ #TraitName #intro_generics
218 #where_clause
219 {
220 #(#each_entry)*
221 }
222
223 #item_impl
224 ))
225}
226
227fn attrs_to_forward_to_impl_block(
228 trait_attrs: &[Attribute],
229) -> Vec<Attribute>
230{
231 const IMPL_BLOCK_ATTRS_ALLOW_LIST: &[&str] = &[
232 "doc",
233 "allow",
234 "warn",
235 "deny",
236 "forbid",
237 "async_trait",
238 ];
239
240 trait_attrs.iter().filter(|attr| IMPL_BLOCK_ATTRS_ALLOW_LIST.iter().any(|ident| {
241 attr.path().is_ident(ident)
242 })).cloned().collect()
243}
244
245fn attrs_to_forward_to_trait_items(
246 impl_block_assoc_item_attrs: &[Attribute],
247) -> Vec<&Attribute>
248{
249 const TRAIT_ITEMS_ATTRS_DENY_LIST: &[&str] = &[
250 "inline",
251 ];
252
253 impl_block_assoc_item_attrs.iter().filter(|attr| TRAIT_ITEMS_ATTRS_DENY_LIST.iter().all(|ident| {
254 attr.path().is_ident(ident).not()
255 })).collect()
256}