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