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