impl_tools_lib/
autoimpl.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::{clause_to_toks, WhereClause};
9use crate::SimplePath;
10use proc_macro2::{Span, TokenStream as Toks};
11use proc_macro_error2::emit_error;
12use quote::{quote, TokenStreamExt};
13use syn::spanned::Spanned;
14use syn::token::Comma;
15use syn::{
16    parse2, Field, Fields, Ident, Index, Item, ItemEnum, ItemStruct, Member, Path, PathArguments,
17    Token,
18};
19
20mod for_deref;
21mod impl_misc;
22mod impl_using;
23
24pub use for_deref::ForDeref;
25pub use impl_misc::*;
26pub use impl_using::*;
27
28/// List of all builtin trait implementations
29pub const STD_IMPLS: &[&dyn ImplTrait] = &[
30    &ImplClone,
31    &ImplCopy,
32    &ImplDebug,
33    &ImplDefault,
34    &ImplPartialEq,
35    &ImplEq,
36    &ImplPartialOrd,
37    &ImplOrd,
38    &ImplHash,
39    &ImplBorrow,
40    &ImplBorrowMut,
41    &ImplAsRef,
42    &ImplAsMut,
43    &ImplDeref,
44    &ImplDerefMut,
45];
46
47/// Trait required by extensions
48pub trait ImplTrait {
49    /// Trait path
50    ///
51    /// This path is matched against trait names in `#[autoimpl]` parameters.
52    fn path(&self) -> SimplePath;
53
54    /// True if this target supports path arguments
55    fn support_path_arguments(&self) -> bool {
56        false
57    }
58
59    /// True if this target supports ignoring fields
60    ///
61    /// Default implementation: `false`
62    fn support_ignore(&self) -> bool {
63        false
64    }
65
66    /// If the target does not support `ignore` but does tolerate `ignore` in
67    /// the presence of another target (e.g. `autoimpl(Eq, PartialEq ignore self.foo)`),
68    /// return the path of that other target here.
69    fn allow_ignore_with(&self) -> Option<SimplePath> {
70        None
71    }
72
73    /// True if this target supports using a field
74    ///
75    /// Default implementation: `false`
76    fn support_using(&self) -> bool {
77        false
78    }
79
80    /// Generate an impl for an enum item
81    ///
82    /// The default implementation is a wrapper around [`Self::enum_items`]
83    /// and suffices for most cases. It may be overridden, e.g. to generate
84    /// multiple implementation items. It is not recommended to modify the
85    /// generics.
86    fn enum_impl(&self, item: &ItemEnum, args: &ImplArgs) -> Result<Toks> {
87        let type_ident = &item.ident;
88        let (impl_generics, ty_generics, item_wc) = item.generics.split_for_impl();
89
90        let (path, items) = self.enum_items(item, args)?;
91
92        let wc = clause_to_toks(&args.clause, item_wc, &path);
93
94        Ok(quote! {
95            #[automatically_derived]
96            impl #impl_generics #path for #type_ident #ty_generics #wc {
97                #items
98            }
99        })
100    }
101
102    /// Generate an impl for a struct item
103    ///
104    /// The default implementation is a wrapper around [`Self::struct_items`]
105    /// and suffices for most cases. It may be overridden, e.g. to generate
106    /// multiple implementation items. It is not recommended to modify the
107    /// generics.
108    fn struct_impl(&self, item: &ItemStruct, args: &ImplArgs) -> Result<Toks> {
109        let type_ident = &item.ident;
110        let (impl_generics, ty_generics, item_wc) = item.generics.split_for_impl();
111
112        let (path, items) = self.struct_items(item, args)?;
113
114        let wc = clause_to_toks(&args.clause, item_wc, &path);
115
116        Ok(quote! {
117            #[automatically_derived]
118            impl #impl_generics #path for #type_ident #ty_generics #wc {
119                #items
120            }
121        })
122    }
123
124    /// Generate enum items
125    ///
126    /// On success, this method returns the tuple `(trait_path, items)`. These
127    /// are used to generate the following implementation:
128    /// ```ignore
129    /// impl #impl_generics #trait_path for #type_ident #ty_generics #where_clause {
130    ///     #items
131    /// }
132    /// ```
133    ///
134    /// Note: this method is *only* called by the default implementation of [`Self::enum_impl`].
135    ///
136    /// Default implementation: returns an error indicating that enum expansion is not supported.
137    fn enum_items(&self, item: &ItemEnum, args: &ImplArgs) -> Result<(Toks, Toks)> {
138        let _ = (item, args);
139        Err(Error::CallSite("enum expansion not supported"))
140    }
141
142    /// Generate struct items
143    ///
144    /// On success, this method returns the tuple `(trait_path, items)`. These
145    /// are used to generate the following implementation:
146    /// ```ignore
147    /// impl #impl_generics #trait_path for #type_ident #ty_generics #where_clause {
148    ///     #items
149    /// }
150    /// ```
151    ///
152    /// Note: this method is *only* called by the default implementation of [`Self::struct_impl`].
153    fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)>;
154}
155
156#[allow(non_camel_case_types)]
157mod kw {
158    use syn::custom_keyword;
159
160    custom_keyword!(ignore);
161    custom_keyword!(using);
162}
163
164/// The `#[autoimpl]` attribute
165pub enum Attr {
166    /// Autoimpl for types supporting `Deref`
167    ForDeref(ForDeref),
168    /// Autoimpl for trait targets
169    ImplTraits(ImplTraits),
170}
171
172/// Autoimpl for trait targets
173pub struct ImplTraits {
174    targets: Vec<Path>,
175    args: ImplArgs,
176}
177
178/// Error type
179pub enum Error {
180    /// Emit an error clarifying that `using self.FIELD` is required
181    RequireUsing,
182    /// Emit an error over the target trait name using `message`
183    CallSite(&'static str),
184    /// Emit an error with the given `span` and `message`
185    WithSpan(Span, &'static str),
186    /// Emit an error regarding path arguments
187    PathArguments(&'static str),
188}
189
190impl Error {
191    /// Report via [`proc_macro_error2::emit_error`].
192    pub fn emit(self, target: Span, path_args: Span) {
193        match self {
194            Error::RequireUsing => {
195                emit_error!(target, "target requires argument `using self.FIELD`")
196            }
197            Error::CallSite(msg) => emit_error!(target, msg),
198            Error::WithSpan(span, msg) => emit_error!(span, msg),
199            Error::PathArguments(msg) => emit_error!(path_args, msg),
200        }
201    }
202}
203
204/// Result type
205pub type Result<T> = std::result::Result<T, Error>;
206
207mod parsing {
208    use super::*;
209    use syn::parse::{Parse, ParseStream, Result};
210
211    impl Parse for Attr {
212        fn parse(input: ParseStream) -> Result<Self> {
213            let mut empty_or_trailing = true;
214            let mut lookahead = input.lookahead1();
215
216            if lookahead.peek(Token![for]) {
217                return input.call(ForDeref::parse).map(Attr::ForDeref);
218            }
219
220            let mut targets = Vec::new();
221            let mut using = None;
222            let mut ignores = Vec::new();
223            let mut clause = None;
224
225            while !input.is_empty() {
226                if lookahead.peek(Token![where])
227                    || lookahead.peek(kw::using)
228                    || lookahead.peek(kw::ignore)
229                {
230                    break;
231                }
232
233                if empty_or_trailing {
234                    if lookahead.peek(Ident) {
235                        targets.push(input.parse()?);
236                        empty_or_trailing = false;
237                        lookahead = input.lookahead1();
238                        continue;
239                    }
240                } else if input.peek(Comma) {
241                    let _ = input.parse::<Comma>()?;
242                    empty_or_trailing = true;
243                    lookahead = input.lookahead1();
244                    continue;
245                }
246                return Err(lookahead.error());
247            }
248
249            while !input.is_empty() {
250                lookahead = input.lookahead1();
251                if clause.is_none() && using.is_none() && lookahead.peek(kw::using) {
252                    let _: kw::using = input.parse()?;
253                    let _ = input.parse::<Token![self]>()?;
254                    let _ = input.parse::<Token![.]>()?;
255                    using = Some(input.parse()?);
256                } else if clause.is_none() && ignores.is_empty() && lookahead.peek(kw::ignore) {
257                    let _: kw::ignore = input.parse()?;
258                    let _ = input.parse::<Token![self]>()?;
259                    let _ = input.parse::<Token![.]>()?;
260                    ignores.push(input.parse()?);
261                    while input.peek(Comma) {
262                        let _ = input.parse::<Comma>()?;
263                        if input.peek(Token![self]) {
264                            let _ = input.parse::<Token![self]>()?;
265                            let _ = input.parse::<Token![.]>()?;
266                            ignores.push(input.parse()?);
267                            continue;
268                        }
269                        break;
270                    }
271                } else if lookahead.peek(Token![where]) {
272                    // Note: assigning to clause disables other match branches since clause must come last!
273                    clause = Some(input.parse()?);
274                } else {
275                    return Err(lookahead.error());
276                }
277            }
278
279            let args = ImplArgs {
280                path_arguments: PathArguments::None,
281                ignores,
282                using,
283                clause,
284            };
285            Ok(Attr::ImplTraits(ImplTraits { targets, args }))
286        }
287    }
288}
289
290impl ImplTraits {
291    /// Expand over the given `item`
292    ///
293    /// This attribute does not modify the item.
294    /// The caller should append the result to `item` tokens.
295    ///
296    /// Errors are reported via [`proc_macro_error2::emit_error`].
297    pub fn expand(
298        self,
299        item: Toks,
300        find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
301    ) -> Toks {
302        match parse2::<Item>(item) {
303            Ok(Item::Enum(item)) => self.expand_enum(item, find_impl),
304            Ok(Item::Struct(item)) => self.expand_struct(item, find_impl),
305            Ok(_) => syn::Error::new(
306                Span::call_site(),
307                "#[autoimpl(Trait)] can only be used on enum or struct items",
308            )
309            .into_compile_error(),
310            Err(err) => err.into_compile_error(),
311        }
312    }
313
314    fn expand_enum(
315        self,
316        item: ItemEnum,
317        find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
318    ) -> Toks {
319        let ImplTraits {
320            mut targets,
321            mut args,
322        } = self;
323
324        if !args.ignores.is_empty() {
325            let ignores = args.ignores.iter();
326            let list = quote! { #(#ignores)* };
327            emit_error!(list, "enum expansion does not currently support `ignore`",);
328            return Toks::new();
329        }
330        if let Some(mem) = args.using {
331            emit_error!(mem, "enum expansion does not currently support `using`",);
332            return Toks::new();
333        }
334
335        let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len());
336        for mut target in targets.drain(..) {
337            let target_span = target.span();
338            let path_args = target
339                .segments
340                .last_mut()
341                .map(|seg| std::mem::take(&mut seg.arguments))
342                .unwrap_or(PathArguments::None);
343            let target_impl = match find_impl(&target) {
344                Some(impl_) => impl_,
345                None => {
346                    emit_error!(target, "unsupported trait");
347                    return Toks::new();
348                }
349            };
350
351            if !(path_args.is_empty() || target_impl.support_path_arguments()) {
352                emit_error!(
353                    target_span,
354                    "target {} does not support path arguments",
355                    target_impl.path()
356                );
357            }
358
359            impl_targets.push((target.span(), target_impl, path_args));
360        }
361
362        let mut toks = Toks::new();
363
364        for (span, target, path_args) in impl_targets.drain(..) {
365            let path_args_span = path_args.span();
366            args.path_arguments = path_args;
367            match target.enum_impl(&item, &args) {
368                Ok(items) => toks.append_all(items),
369                Err(error) => error.emit(span, path_args_span),
370            }
371        }
372        toks
373    }
374
375    fn expand_struct(
376        self,
377        item: ItemStruct,
378        find_impl: impl Fn(&Path) -> Option<&'static dyn ImplTrait>,
379    ) -> Toks {
380        let ImplTraits {
381            mut targets,
382            mut args,
383        } = self;
384
385        let mut not_supporting_ignore = vec![];
386        let mut not_supporting_using = vec![];
387
388        let mut impl_targets: Vec<(Span, _, _)> = Vec::with_capacity(targets.len());
389        for mut target in targets.drain(..) {
390            let target_span = target.span();
391            let path_args = target
392                .segments
393                .last_mut()
394                .map(|seg| std::mem::take(&mut seg.arguments))
395                .unwrap_or(PathArguments::None);
396            let target_impl = match find_impl(&target) {
397                Some(impl_) => impl_,
398                None => {
399                    emit_error!(target, "unsupported trait");
400                    return Toks::new();
401                }
402            };
403
404            if !target_impl.support_ignore() {
405                let except_with = target_impl.allow_ignore_with();
406                not_supporting_ignore.push((target.clone(), except_with));
407            }
408            if !target_impl.support_using() {
409                not_supporting_using.push(target.clone());
410            }
411            if !(path_args.is_empty() || target_impl.support_path_arguments()) {
412                emit_error!(
413                    target_span,
414                    "target {} does not support path arguments",
415                    target_impl.path()
416                );
417            }
418
419            impl_targets.push((target.span(), target_impl, path_args));
420        }
421
422        if !args.ignores.is_empty() {
423            for (target, except_with) in not_supporting_ignore.into_iter() {
424                if let Some(path) = except_with {
425                    if impl_targets
426                        .iter()
427                        .any(|(_, target_impl, _)| path == target_impl.path())
428                    {
429                        continue;
430                    }
431                }
432                emit_error!(target, "target does not support `ignore`",);
433            }
434        }
435        if args.using.is_some() {
436            for target in not_supporting_using.into_iter() {
437                emit_error!(target, "target does not support `using`",);
438            }
439        }
440
441        fn check_is_field(mem: &Member, fields: &Fields) {
442            match (fields, mem) {
443                (Fields::Named(fields), Member::Named(ref ident)) => {
444                    if fields
445                        .named
446                        .iter()
447                        .any(|field| field.ident.as_ref() == Some(ident))
448                    {
449                        return;
450                    }
451                }
452                (Fields::Unnamed(fields), Member::Unnamed(index)) => {
453                    if (index.index as usize) < fields.unnamed.len() {
454                        return;
455                    }
456                }
457                _ => (),
458            }
459            emit_error!(mem, "not a struct field");
460        }
461
462        let mut toks = Toks::new();
463        for mem in &args.ignores {
464            check_is_field(mem, &item.fields);
465        }
466        if let Some(mem) = args.using_member() {
467            check_is_field(mem, &item.fields);
468        }
469
470        for (span, target, path_args) in impl_targets.drain(..) {
471            let path_args_span = path_args.span();
472            args.path_arguments = path_args;
473            match target.struct_impl(&item, &args) {
474                Ok(items) => toks.append_all(items),
475                Err(error) => error.emit(span, path_args_span),
476            }
477        }
478        toks
479    }
480}
481
482/// Arguments passed to [`ImplTrait`] implementation methods
483pub struct ImplArgs {
484    /// Path arguments to trait
485    ///
486    /// Example: if the target is `Deref<Target = T>`, this is `<Target = T>`.
487    /// This is always empty unless [`ImplTrait::support_path_arguments`] returns true.
488    pub path_arguments: PathArguments,
489    /// Fields ignored in attribute
490    pub ignores: Vec<Member>,
491    /// Field specified to 'use' in attribute
492    pub using: Option<Member>,
493    /// Where clause added to attribute
494    pub clause: Option<WhereClause>,
495}
496
497impl ImplArgs {
498    /// If true, this field is ignored
499    pub fn ignore(&self, member: &Member) -> bool {
500        self.ignores.contains(member)
501    }
502
503    /// If true, this named field is ignored
504    pub fn ignore_named(&self, ident: &Ident) -> bool {
505        self.ignores.iter().any(|ig| match ig {
506            Member::Named(m) => m == ident,
507            _ => false,
508        })
509    }
510
511    /// If true, this unnamed field is ignored
512    pub fn ignore_unnamed(&self, index: &Index) -> bool {
513        self.ignores.iter().any(|ig| match ig {
514            Member::Unnamed(m) => m == index,
515            _ => false,
516        })
517    }
518
519    /// Field to "use", if any
520    pub fn using_member(&self) -> Option<&Member> {
521        self.using.as_ref()
522    }
523
524    /// Find field to "use", if any
525    pub fn using_field<'b>(&self, fields: &'b Fields) -> Option<&'b Field> {
526        match fields {
527            Fields::Named(fields) => fields.named.iter().find(|field| match self.using {
528                Some(Member::Named(ref ident)) => ident == field.ident.as_ref().unwrap(),
529                _ => false,
530            }),
531            Fields::Unnamed(fields) => {
532                fields
533                    .unnamed
534                    .iter()
535                    .enumerate()
536                    .find_map(|(i, field)| match self.using {
537                        Some(Member::Unnamed(ref index)) => {
538                            (*index == Index::from(i)).then(|| field)
539                        }
540                        _ => None,
541                    })
542            }
543            Fields::Unit => None,
544        }
545    }
546
547    /// Call the given closure over all non-ignored fields
548    pub fn for_fields<'f>(&self, fields: &'f Fields, f: impl FnMut(Member, &'f Field)) {
549        self.for_fields_iter(fields.iter().enumerate(), f);
550    }
551
552    /// Call the given closure over all non-ignored fields
553    pub fn for_fields_iter<'f>(
554        &self,
555        fields: impl Iterator<Item = (usize, &'f Field)>,
556        mut f: impl FnMut(Member, &'f Field),
557    ) {
558        for (i, field) in fields {
559            let member = match field.ident.clone() {
560                Some(ident) => Member::Named(ident),
561                None => Member::Unnamed(Index::from(i)),
562            };
563            if !self.ignore(&member) {
564                f(member, field);
565            }
566        }
567    }
568}