impl_trait_for_tuples/
full_automatic.rs

1//! Implementation of the full-automatic tuple trait implementation.
2//!
3//! The full-automatic implementation uses the trait definition to generate the implementations for
4//! tuples. This has some limitations, as no support for associated types, consts, return values or
5//! functions with a default implementation.
6
7use crate::utils::add_tuple_element_generics;
8
9use proc_macro2::{Span, TokenStream};
10
11use std::iter::repeat;
12
13use syn::{
14    parse_quote,
15    spanned::Spanned,
16    visit::{self, Visit},
17    Error, FnArg, Generics, Ident, Index, ItemTrait, Pat, Result, ReturnType, Signature, TraitItem,
18    TraitItemMethod, Type,
19};
20
21use quote::quote;
22
23/// Generate the full-automatic tuple implementations for a given trait definition and the given tuples.
24pub fn full_automatic_impl(
25    definition: ItemTrait,
26    tuple_elements: Vec<Ident>,
27    min: Option<usize>,
28) -> Result<TokenStream> {
29    check_trait_declaration(&definition)?;
30
31    let impls = (min.unwrap_or(0)..=tuple_elements.len())
32        .map(|i| generate_tuple_impl(&definition, &tuple_elements[0..i]));
33
34    Ok(quote!(
35        #definition
36        #( #impls )*
37    ))
38}
39
40fn check_trait_declaration(trait_decl: &ItemTrait) -> Result<()> {
41    let mut visitor = CheckTraitDeclaration { errors: Vec::new() };
42    visit::visit_item_trait(&mut visitor, trait_decl);
43
44    match visitor.errors.pop() {
45        Some(init) => Err(visitor.errors.into_iter().fold(init, |mut old, new| {
46            old.combine(new);
47            old
48        })),
49        None => Ok(()),
50    }
51}
52
53/// Checks that the given trait declaration corresponds to the expected format.
54struct CheckTraitDeclaration {
55    /// Stores all errors that are found.
56    errors: Vec<Error>,
57}
58
59impl CheckTraitDeclaration {
60    fn add_error<T: Spanned>(&mut self, span: &T) {
61        self.errors.push(Error::new(span.span(), CHECK_ERROR_MSG));
62    }
63}
64
65const CHECK_ERROR_MSG: &str =
66    "Not supported by full-automatic tuple implementation. \
67     Use semi-automatic tuple implementation for more control of the implementation.";
68
69impl<'ast> Visit<'ast> for CheckTraitDeclaration {
70    fn visit_trait_item(&mut self, ti: &'ast TraitItem) {
71        match ti {
72            TraitItem::Method(m) => visit::visit_trait_item_method(self, m),
73            _ => self.add_error(ti),
74        }
75    }
76
77    fn visit_return_type(&mut self, rt: &'ast ReturnType) {
78        match rt {
79            ReturnType::Default => {}
80            ReturnType::Type(_, ty) => match **ty {
81                Type::Tuple(ref tuple) if tuple.elems.is_empty() => {}
82                _ => self.add_error(rt),
83            },
84        }
85    }
86}
87
88fn generate_tuple_impl(definition: &ItemTrait, tuple_elements: &[Ident]) -> TokenStream {
89    let name = &definition.ident;
90    let unsafety = &definition.unsafety;
91    let generics = generate_generics(definition, tuple_elements);
92    let ty_generics = definition.generics.split_for_impl().1;
93    let (impl_generics, _, where_clause) = generics.split_for_impl();
94    let fns = definition.items.iter().filter_map(|i| match i {
95        TraitItem::Method(method) => Some(generate_delegate_method(method, tuple_elements)),
96        _ => None,
97    });
98
99    quote!(
100        #[allow(unused)]
101        #unsafety impl #impl_generics #name #ty_generics for ( #( #tuple_elements, )* ) #where_clause {
102            #( #fns )*
103        }
104    )
105}
106
107/// Collects all non-reference argument types.
108struct CollectNonReferenceArgTypes {
109    result: Vec<Type>,
110}
111
112impl<'ast> Visit<'ast> for CollectNonReferenceArgTypes {
113    fn visit_type(&mut self, ty: &'ast Type) {
114        if !is_reference_type(ty) {
115            self.result.push(ty.clone());
116        }
117    }
118}
119
120fn generate_generics(definition: &ItemTrait, tuple_elements: &[Ident]) -> Generics {
121    let mut generics = definition.generics.clone();
122    let name = &definition.ident;
123    let ty_generics = definition.generics.split_for_impl().1;
124
125    // Make sure that all non-reference types implement `Clone`.
126    let mut visitor = CollectNonReferenceArgTypes { result: Vec::new() };
127    definition
128        .items
129        .iter()
130        .for_each(|item| visit::visit_trait_item(&mut visitor, item));
131    visitor.result.dedup();
132    {
133        let where_clause = generics.make_where_clause();
134        visitor
135            .result
136            .into_iter()
137            .for_each(|ty| where_clause.predicates.push(parse_quote!(#ty: Clone)));
138    }
139
140    add_tuple_element_generics(
141        tuple_elements,
142        Some(quote!(#name #ty_generics)),
143        &mut generics,
144    );
145
146    generics
147}
148
149fn generate_delegate_method(method: &TraitItemMethod, tuple_elements: &[Ident]) -> TokenStream {
150    let name = repeat(&method.sig.ident);
151    let self_arg = method
152        .sig
153        .inputs
154        .first()
155        .map(|a| match a {
156            FnArg::Receiver(_) => true,
157            _ => false,
158        })
159        .unwrap_or(false);
160    let (sig, arg_names, arg_clones) = update_signature_and_extract_arg_infos(method.sig.clone());
161    let arg_names_per_tuple = repeat(&arg_names);
162    let arg_clones_per_tuple = repeat(&arg_clones);
163    let tuple_access = tuple_elements.iter().enumerate().map(|(i, te)| {
164        if self_arg {
165            let index = Index::from(i);
166            quote!(self.#index.)
167        } else {
168            quote!(#te::)
169        }
170    });
171
172    let mut res = method.clone();
173    res.sig = sig;
174    res.semi_token = None;
175    res.default = Some(parse_quote!(
176        { #( #tuple_access #name ( #( #arg_names_per_tuple #arg_clones_per_tuple ),* ); )* }
177    ));
178
179    quote!( #res )
180}
181
182/// Update `Signature` by replacing all wild card argument names with unique identifiers, collect all
183/// argument names and if we need to clone an argument.
184fn update_signature_and_extract_arg_infos(
185    mut sig: Signature,
186) -> (Signature, Vec<Ident>, Vec<TokenStream>) {
187    let mut unique_id = 0;
188    let mut arg_clone = Vec::new();
189    let mut push_arg_clone = |is_ref: bool| {
190        if is_ref {
191            arg_clone.push(quote!())
192        } else {
193            arg_clone.push(quote!(.clone()))
194        }
195    };
196
197    let arg_names = sig
198        .inputs
199        .iter_mut()
200        .filter_map(|a| match a {
201            FnArg::Typed(ref mut arg) => match &mut *arg.pat {
202                Pat::Ident(p) => {
203                    push_arg_clone(is_reference_type(&arg.ty));
204                    Some(p.ident.clone())
205                }
206                Pat::Wild(_) => {
207                    push_arg_clone(is_reference_type(&arg.ty));
208                    let ident = Ident::new(
209                        &format!("tuple_element_unique_ident_{}", unique_id),
210                        Span::call_site(),
211                    );
212                    unique_id += 1;
213                    let ident2 = ident.clone();
214
215                    arg.pat = parse_quote!(#ident);
216                    Some(ident2)
217                }
218                _ => None,
219            },
220            _ => None,
221        })
222        .collect::<Vec<_>>();
223
224    (sig, arg_names, arg_clone)
225}
226
227fn is_reference_type(ty: &Type) -> bool {
228    match ty {
229        Type::Reference(_) => true,
230        _ => false,
231    }
232}