Skip to main content

impl_tools_lib/autoimpl/
for_deref.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Implementation of the `#[autoimpl]` attribute
7
8use crate::generics::{GenericParam, Generics, TypeParamBound, WherePredicate};
9use crate::utils::propagate_attr_to_impl;
10use proc_macro_error3::{emit_call_site_error, emit_call_site_warning, emit_error};
11use proc_macro2::{Span, TokenStream};
12use quote::{ToTokens, TokenStreamExt, quote};
13use syn::parse_quote;
14use syn::punctuated::Punctuated;
15use syn::spanned::Spanned;
16use syn::token::{Comma, Eq, PathSep};
17use syn::{FnArg, Ident, Item, Member, Pat, ReceiverKind, Token, TraitItem, Type, TypePath};
18
19mod kw {
20    syn::custom_keyword!(using);
21}
22
23/// Autoimpl for types supporting `Deref`
24#[derive(Debug)]
25pub struct ForDeref {
26    generics: Generics,
27    definitive: Option<Ident>,
28    targets: Punctuated<Type, Comma>,
29    using: Option<Member>,
30}
31
32mod parsing {
33    use super::*;
34    use syn::parse::{Parse, ParseStream, Result};
35
36    impl Parse for ForDeref {
37        fn parse(input: ParseStream) -> Result<Self> {
38            let _ = input.parse::<Token![for]>()?;
39            let mut generics: Generics = input.parse()?;
40
41            let targets = Punctuated::parse_separated_nonempty(input)?;
42
43            let mut using = None;
44            if input.peek(kw::using) {
45                let _: kw::using = input.parse()?;
46                let _: Token![self] = input.parse()?;
47                let _: Token![.] = input.parse()?;
48                using = Some(input.parse()?);
49            }
50
51            if input.peek(Token![where]) {
52                generics.where_clause = Some(input.parse()?);
53            }
54
55            let mut definitive: Option<Ident> = None;
56            for param in &generics.params {
57                if let GenericParam::Type(param) = param {
58                    for bound in &param.bounds {
59                        if matches!(bound, TypeParamBound::TraitSubst(_)) {
60                            definitive = Some(param.ident.clone());
61                            break;
62                        }
63                    }
64                }
65            }
66            if definitive.is_none() {
67                if let Some(clause) = generics.where_clause.as_ref() {
68                    for pred in &clause.predicates {
69                        if let WherePredicate::Type(pred) = pred {
70                            for bound in &pred.bounds {
71                                if matches!(bound, TypeParamBound::TraitSubst(_)) {
72                                    if let Type::Path(TypePath {
73                                        attrs: _,
74                                        qself: None,
75                                        path,
76                                    }) = &pred.bounded_ty
77                                    {
78                                        if let Some(ident) = path.get_ident() {
79                                            definitive = Some(ident.clone());
80                                            break;
81                                        }
82                                    }
83                                }
84                            }
85                        }
86                    }
87                }
88            }
89
90            Ok(ForDeref {
91                generics,
92                definitive,
93                targets,
94                using,
95            })
96        }
97    }
98}
99
100fn has_bound_on_self(generics: &syn::Generics) -> bool {
101    if let Some(ref clause) = generics.where_clause {
102        for pred in clause.predicates.iter() {
103            if let syn::WherePredicate::Type(ty) = pred {
104                if let Type::Path(ref bounded) = ty.bounded_ty {
105                    if bounded.qself.is_none() && bounded.path.is_ident("Self") {
106                        if ty
107                            .bounds
108                            .iter()
109                            .any(|bound| matches!(bound, syn::TypeParamBound::Trait(_)))
110                        {
111                            return true;
112                        }
113                    }
114                }
115            }
116            // Note: we ignore lifetime bounds, since Self: 'a implies that 'a
117            // is a parameter with lifetime shorter than Self (thus is more a
118            // bound on 'a than it is on Self), while 'a: Self is not supported.
119        }
120    }
121
122    false
123}
124
125impl ForDeref {
126    /// Expand over the given `item`
127    ///
128    /// This attribute does not modify the item.
129    /// The caller should append the result to `item` tokens.
130    pub fn expand(self, item: TokenStream) -> TokenStream {
131        let trait_def = match syn::parse2::<Item>(item) {
132            Ok(Item::Trait(item)) => item,
133            Ok(item) => {
134                emit_error!(item, "expected trait");
135                return TokenStream::new();
136            }
137            Err(err) => return err.into_compile_error(),
138        };
139
140        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
141        enum Bound {
142            None,
143            Deref(bool), // true if DerefMut
144            ErrorEmitted,
145        }
146        let mut bound = Bound::None;
147
148        let trait_ident = &trait_def.ident;
149        let (_, trait_generics, _) = trait_def.generics.split_for_impl();
150        let trait_ty = quote! { #trait_ident #trait_generics };
151        let ty_generics = self.generics.ty_generics(&trait_def.generics);
152        let (impl_generics, where_clause) =
153            self.generics.impl_generics(&trait_def.generics, &trait_ty);
154
155        let opt_definitive = self
156            .definitive
157            .as_ref()
158            .map(|ty| quote! { < #ty as #trait_ty > });
159        let fn_definitive = opt_definitive
160            .clone()
161            .unwrap_or_else(|| quote! { #trait_ty });
162
163        // Tokenize, like ToTokens impls for syn::TraitItem*, but for definition
164        let mut impl_items = TokenStream::new();
165        let tokens = &mut impl_items;
166        for item in trait_def.items.into_iter() {
167            match item {
168                TraitItem::Const(item) => {
169                    let Some(definitive) = opt_definitive.as_ref() else {
170                        emit_error!(
171                            item,
172                            "cannot autoimpl an associated constant without a definitive type (e.g. `T: trait`)"
173                        );
174                        continue;
175                    };
176
177                    for attr in item.attrs.iter() {
178                        if *attr.path() == parse_quote! { cfg } {
179                            attr.to_tokens(tokens);
180                        }
181                    }
182
183                    item.const_token.to_tokens(tokens);
184                    item.ident.to_tokens(tokens);
185                    item.colon_token.to_tokens(tokens);
186                    item.ty.to_tokens(tokens);
187
188                    Eq::default().to_tokens(tokens);
189                    definitive.to_tokens(tokens);
190                    PathSep::default().to_tokens(tokens);
191                    item.ident.to_tokens(tokens);
192
193                    item.semi_token.to_tokens(tokens);
194                }
195                TraitItem::Fn(mut item) => {
196                    for attr in item.attrs.iter() {
197                        if propagate_attr_to_impl(attr) {
198                            attr.to_tokens(tokens);
199                        }
200                    }
201
202                    if has_bound_on_self(&item.sig.generics) {
203                        // If the method has a bound on Self, we cannot use a dereferencing
204                        // implementation since the definitive type is not guaranteed to match
205                        // the bound (we also cannot add a bound).
206
207                        if item.default.is_none() {
208                            emit_call_site_error!(
209                                "cannot autoimpl trait with Deref";
210                                note = item.span() => "method has a bound on Self and no default implementation";
211                            );
212                        } else if !cfg!(feature = "allow-trait-autoimpl-with-sized-fn-bound") {
213                            // TODO(rust proc_macro_lint): this should be a configurable lint
214                            emit_call_site_warning!(
215                                "autoimpl on trait that has a method with Self: Sized bound";
216                                note = item.span() => "method impl uses default implementation, not deref";
217                            );
218                        }
219
220                        continue;
221                    }
222
223                    for (i, arg) in item.sig.inputs.iter_mut().enumerate() {
224                        if let FnArg::Typed(ty) = arg {
225                            if let Pat::Ident(pat) = &mut *ty.pat {
226                                // We can keep the ident but must not use `ref` / `mut` modifiers
227                                pat.by_ref = None;
228                                pat.mutability = None;
229                                assert_eq!(pat.subpat, None);
230                            } else {
231                                // Substitute a fresh ident
232                                let name = format!("arg{i}");
233                                let ident = Ident::new(&name, Span::call_site());
234                                *ty.pat = Pat::Ident(syn::PatIdent {
235                                    attrs: vec![],
236                                    by_ref: None,
237                                    mutability: None,
238                                    ident,
239                                    subpat: None,
240                                });
241                            }
242                        }
243                    }
244                    item.sig.to_tokens(tokens);
245
246                    if self.using.is_none() {
247                        bound = bound.max(match item.sig.inputs.first() {
248                            Some(FnArg::Receiver(rec)) => {
249                                if matches!(rec.kind, ReceiverKind::Reference(_, _, _)) {
250                                    Bound::Deref(rec.mutability.is_some())
251                                } else {
252                                    emit_call_site_error!(
253                                        "cannot autoimpl trait with Deref";
254                                        note = rec.span() => "deref cannot yield `self` by value";
255                                    );
256                                    Bound::ErrorEmitted
257                                }
258                            }
259                            Some(FnArg::Typed(pat)) => match &*pat.ty {
260                                Type::Reference(rf) if rf.elem == parse_quote! { Self } => {
261                                    Bound::Deref(rf.mutability.is_some())
262                                }
263                                _ => Bound::None,
264                            },
265                            _ => Bound::None,
266                        });
267                    }
268
269                    let ident = &item.sig.ident;
270                    let params = item.sig.inputs.iter().map(|arg| {
271                        let mut toks = TokenStream::new();
272                        match arg {
273                            FnArg::Receiver(arg) => {
274                                for attr in &arg.attrs {
275                                    if propagate_attr_to_impl(&attr) {
276                                        attr.to_tokens(&mut toks);
277                                    }
278                                }
279                                if let Some(member) = self.using.as_ref() {
280                                    if let ReceiverKind::Reference(r, _, mutability) = arg.kind {
281                                        r.to_tokens(&mut toks);
282                                        if let Some(m) = mutability {
283                                            m.to_tokens(&mut toks);
284                                        }
285                                    }
286                                    let self_ = &arg.self_token;
287                                    toks.append_all(quote! { #self_ . #member });
288                                } else {
289                                    arg.self_token.to_tokens(&mut toks);
290                                }
291                            }
292                            FnArg::Typed(arg) => {
293                                for attr in &arg.attrs {
294                                    if propagate_attr_to_impl(&attr) {
295                                        attr.to_tokens(&mut toks);
296                                    };
297                                }
298
299                                arg.pat.to_tokens(&mut toks);
300                            }
301                        };
302                        toks
303                    });
304                    tokens.append_all(quote! { {
305                        #fn_definitive :: #ident ( #(#params),* )
306                    } });
307                }
308                TraitItem::Type(item) => {
309                    let Some(definitive) = opt_definitive.as_ref() else {
310                        emit_error!(
311                            item,
312                            "cannot autoimpl an associated type without a definitive type (e.g. `T: trait`)"
313                        );
314                        continue;
315                    };
316
317                    for attr in item.attrs.iter() {
318                        if *attr.path() == parse_quote! { cfg } {
319                            attr.to_tokens(tokens);
320                        }
321                    }
322
323                    if has_bound_on_self(&item.generics) {
324                        emit_call_site_error!(
325                            "cannot autoimpl trait with Deref";
326                            note = item.span() => "type has a bound on Self";
327                        );
328                    }
329
330                    item.type_token.to_tokens(tokens);
331                    item.ident.to_tokens(tokens);
332
333                    let (_, ty_generics, where_clause) = item.generics.split_for_impl();
334                    ty_generics.to_tokens(tokens);
335
336                    Eq::default().to_tokens(tokens);
337                    definitive.to_tokens(tokens);
338                    PathSep::default().to_tokens(tokens);
339                    item.ident.to_tokens(tokens);
340                    ty_generics.to_tokens(tokens);
341
342                    where_clause.to_tokens(tokens);
343                    item.semi_token.to_tokens(tokens);
344                }
345                TraitItem::Macro(item) => {
346                    emit_error!(item, "unsupported: macro item in trait");
347                }
348                TraitItem::Verbatim(item) => {
349                    emit_error!(item, "unsupported: verbatim item in trait");
350                }
351
352                /* Testing of exhaustive matching is disabled: syn 1.0.90 breaks it.
353                #[cfg(test)]
354                TraitItem::__TestExhaustive(_) => unimplemented!(),
355                #[cfg(not(test))]
356                */
357                _ => (),
358            }
359        }
360
361        let mut toks = TokenStream::new();
362        match bound {
363            Bound::None => (),
364            Bound::Deref(is_mut) => {
365                let Some(definitive_ty) = self.definitive.as_ref() else {
366                    emit_call_site_error!("require a definitive type (e.g. `T: trait`)");
367                    return toks;
368                };
369
370                // Emit a bound to improve error messages (see issue 27)
371                let bound = match is_mut {
372                    false => quote! { ::core::ops::Deref },
373                    true => quote! { ::core::ops::DerefMut },
374                };
375
376                let target_impls = self.targets.iter().map(|target| {
377                    quote! {
378                        impl #impl_generics TargetMustImplDeref #ty_generics for #target
379                        #where_clause {}
380                    }
381                });
382
383                toks.append_all(quote! {
384                    #[automatically_derived]
385                    const _: () = {
386                        trait TargetMustImplDeref #impl_generics: #bound<Target = #definitive_ty>
387                        #where_clause {}
388
389                        #(#target_impls)*
390                    };
391                });
392            }
393            Bound::ErrorEmitted => return toks,
394        }
395
396        for target in self.targets {
397            toks.append_all(quote! {
398                #[automatically_derived]
399                impl #impl_generics #trait_ty for #target #where_clause {
400                    #impl_items
401                }
402            });
403        }
404        toks
405    }
406}