impl_tools_lib/autoimpl/
impl_misc.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//! Miscellaneous impls
7
8use super::{ImplArgs, ImplTrait, Result};
9use crate::{IdentFormatter, SimplePath};
10use proc_macro2::TokenStream as Toks;
11use quote::{quote, ToTokens, TokenStreamExt};
12use syn::{Fields, Index, ItemEnum, ItemStruct, Member, Token};
13
14/// Implement [`core::clone::Clone`]
15pub struct ImplClone;
16impl ImplTrait for ImplClone {
17    fn path(&self) -> SimplePath {
18        SimplePath::new(&["", "core", "clone", "Clone"])
19    }
20
21    fn support_ignore(&self) -> bool {
22        true
23    }
24
25    fn enum_items(&self, item: &ItemEnum, args: &ImplArgs) -> Result<(Toks, Toks)> {
26        let mut idfmt = IdentFormatter::new();
27        let name = &item.ident;
28        let mut variants = Toks::new();
29        for v in item.variants.iter() {
30            for attr in &v.attrs {
31                if attr.path().get_ident().is_some_and(|path| path == "cfg") {
32                    attr.to_tokens(&mut variants);
33                }
34            }
35
36            let ident = &v.ident;
37            let tag = quote! { #name :: #ident };
38            variants.append_all(match v.fields {
39                Fields::Named(ref fields) => {
40                    let mut idents = Toks::new();
41                    let mut toks = Toks::new();
42                    for field in fields.named.iter() {
43                        for attr in &field.attrs {
44                            if attr.path().get_ident().is_some_and(|path| path == "cfg") {
45                                attr.to_tokens(&mut idents);
46                                attr.to_tokens(&mut toks);
47                            }
48                        }
49
50                        let ident = field.ident.as_ref().unwrap();
51                        idents.append_all(quote! { ref #ident, });
52                        toks.append_all(if args.ignore_named(ident) {
53                            quote! { #ident: Default::default(), }
54                        } else {
55                            quote! { #ident: ::core::clone::Clone::clone(#ident), }
56                        });
57                    }
58                    quote! { #tag { #idents } => #tag { #toks }, }
59                }
60                Fields::Unnamed(ref fields) => {
61                    let mut bindings = Toks::new();
62                    let mut toks = Toks::new();
63                    for (i, field) in fields.unnamed.iter().enumerate() {
64                        let ident = idfmt.make_call_site(format_args!("_{i}"));
65
66                        for attr in &field.attrs {
67                            if attr.path().get_ident().is_some_and(|path| path == "cfg") {
68                                attr.to_tokens(&mut bindings);
69                                attr.to_tokens(&mut toks);
70                            }
71                        }
72
73                        bindings.append_all(quote! { ref #ident, });
74                        toks.append_all(quote! { ::core::clone::Clone::clone(#ident), });
75                    }
76                    quote! { #tag ( #bindings ) => #tag ( #toks ), }
77                }
78                Fields::Unit => quote! { #tag => #tag, },
79            });
80        }
81        let method = quote! {
82            fn clone(&self) -> Self {
83                match *self {
84                    #variants
85                }
86            }
87        };
88        Ok((quote! { ::core::clone::Clone }, method))
89    }
90
91    fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> {
92        let type_ident = &item.ident;
93        let inner = match &item.fields {
94            Fields::Named(fields) => {
95                let mut toks = Toks::new();
96                for field in fields.named.iter() {
97                    for attr in &field.attrs {
98                        if attr.path().get_ident().is_some_and(|path| path == "cfg") {
99                            attr.to_tokens(&mut toks);
100                        }
101                    }
102
103                    let ident = field.ident.as_ref().unwrap();
104                    toks.append_all(if args.ignore_named(ident) {
105                        quote! { #ident: Default::default(), }
106                    } else {
107                        quote! { #ident: ::core::clone::Clone::clone(&self.#ident), }
108                    });
109                }
110                quote! { #type_ident { #toks } }
111            }
112            Fields::Unnamed(fields) => {
113                let mut toks = Toks::new();
114                for (i, field) in fields.unnamed.iter().enumerate() {
115                    for attr in &field.attrs {
116                        if attr.path().get_ident().is_some_and(|path| path == "cfg") {
117                            attr.to_tokens(&mut toks);
118                        }
119                    }
120
121                    let index = Index::from(i);
122                    toks.append_all(if args.ignore_unnamed(&index) {
123                        quote! { Default::default(), }
124                    } else {
125                        quote! { ::core::clone::Clone::clone(&self.#index), }
126                    });
127                }
128                quote! { #type_ident ( #toks ) }
129            }
130            Fields::Unit => quote! { #type_ident },
131        };
132        let method = quote! {
133            fn clone(&self) -> Self {
134                #inner
135            }
136        };
137        Ok((quote! { ::core::clone::Clone }, method))
138    }
139}
140
141/// Implement [`core::marker::Copy`]
142pub struct ImplCopy;
143impl ImplTrait for ImplCopy {
144    fn path(&self) -> SimplePath {
145        SimplePath::new(&["", "core", "marker", "Copy"])
146    }
147
148    fn allow_ignore_with(&self) -> Option<SimplePath> {
149        Some(SimplePath::new(&["", "core", "clone", "Clone"]))
150    }
151
152    fn enum_items(&self, _: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> {
153        Ok((quote! { ::core::marker::Copy }, quote! {}))
154    }
155
156    fn struct_items(&self, _: &ItemStruct, _: &ImplArgs) -> Result<(Toks, Toks)> {
157        Ok((quote! { ::core::marker::Copy }, quote! {}))
158    }
159}
160
161/// Implement [`core::fmt::Debug`]
162pub struct ImplDebug;
163impl ImplTrait for ImplDebug {
164    fn path(&self) -> SimplePath {
165        SimplePath::new(&["", "core", "fmt", "Debug"])
166    }
167
168    fn support_ignore(&self) -> bool {
169        true
170    }
171
172    fn enum_items(&self, item: &ItemEnum, args: &ImplArgs) -> Result<(Toks, Toks)> {
173        let mut idfmt = IdentFormatter::new();
174        let name = &item.ident;
175        let type_name = item.ident.to_string();
176        let mut variants = Toks::new();
177        for v in item.variants.iter() {
178            let ident = &v.ident;
179            let var_name = ident.to_string();
180            let tag = quote! { #name :: #ident };
181            variants.append_all(match v.fields {
182                Fields::Named(ref fields) => {
183                    let mut idents = Toks::new();
184                    let mut stmts = quote! { let mut debug = f.debug_struct(#var_name); };
185                    for field in fields.named.iter() {
186                        for attr in &field.attrs {
187                            if attr.path().get_ident().is_some_and(|path| path == "cfg") {
188                                attr.to_tokens(&mut idents);
189                                attr.to_tokens(&mut stmts);
190                            }
191                        }
192
193                        let ident = field.ident.as_ref().unwrap();
194                        idents.append_all(quote! { ref #ident, });
195                        if !args.ignore_named(ident) {
196                            let name = ident.to_string();
197                            stmts.append_all(quote! { { debug.field(#name, #ident); } });
198                        }
199                    }
200                    stmts.append_all(quote! { debug.finish() });
201
202                    quote! { #tag { #idents } => { #stmts }, }
203                }
204                Fields::Unnamed(ref fields) => {
205                    let mut bindings = Toks::new();
206                    let mut stmts = quote! { let mut debug = f.debug_tuple(#var_name); };
207                    for (i, field) in fields.unnamed.iter().enumerate() {
208                        for attr in &field.attrs {
209                            if attr.path().get_ident().is_some_and(|path| path == "cfg") {
210                                attr.to_tokens(&mut bindings);
211                                attr.to_tokens(&mut stmts);
212                            }
213                        }
214
215                        let ident = idfmt.make_call_site(format_args!("_{i}"));
216                        bindings.append_all(quote! { ref #ident, });
217                        stmts.append_all(quote! { { debug.field(#ident); } });
218                    }
219                    quote! {
220                        #tag ( #bindings ) => { #stmts; debug.finish() },
221                    }
222                }
223                Fields::Unit => quote! { #tag => f.write_str(#var_name), },
224            })
225        }
226
227        // Note: unlike #[derive(Debug)], we include the name of the enum!
228        let method = quote! {
229            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
230                write!(f, "{}::", #type_name)?;
231                match *self {
232                    #variants
233                }
234            }
235        };
236        Ok((quote! { ::core::fmt::Debug }, method))
237    }
238
239    fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> {
240        let type_name = item.ident.to_string();
241        let inner = match &item.fields {
242            Fields::Named(fields) => {
243                let mut stmts = quote! { let mut debug = f.debug_struct(#type_name); };
244                let mut no_skips = true;
245                for field in fields.named.iter() {
246                    for attr in &field.attrs {
247                        if attr.path().get_ident().is_some_and(|path| path == "cfg") {
248                            attr.to_tokens(&mut stmts);
249                        }
250                    }
251
252                    let ident = field.ident.as_ref().unwrap();
253                    if !args.ignore_named(ident) {
254                        let name = ident.to_string();
255                        stmts.append_all(quote! { { debug.field(#name, &self.#ident); } });
256                    } else {
257                        no_skips = false;
258                    }
259                }
260                if no_skips {
261                    quote! { #stmts; debug.finish() }
262                } else {
263                    quote! { #stmts; debug.finish_non_exhaustive() }
264                }
265            }
266            Fields::Unnamed(fields) => {
267                let mut stmts = quote! { let mut debug = f.debug_tuple(#type_name); };
268                for (i, field) in fields.unnamed.iter().enumerate() {
269                    for attr in &field.attrs {
270                        if attr.path().get_ident().is_some_and(|path| path == "cfg") {
271                            attr.to_tokens(&mut stmts);
272                        }
273                    }
274
275                    let index = Index::from(i);
276                    if !args.ignore_unnamed(&index) {
277                        stmts.append_all(quote! { { debug.field(&self.#index); } });
278                    } else {
279                        stmts.append_all(quote! { { debug.field(&format_args!("_")); } });
280                    }
281                }
282                quote! { #stmts; debug.finish() }
283            }
284            Fields::Unit => quote! { f.write_str(#type_name) },
285        };
286
287        let method = quote! {
288            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
289                #inner
290            }
291        };
292        Ok((quote! { ::core::fmt::Debug }, method))
293    }
294}
295
296/// Implement [`core::default::Default`]
297pub struct ImplDefault;
298impl ImplTrait for ImplDefault {
299    fn path(&self) -> SimplePath {
300        SimplePath::new(&["", "core", "default", "Default"])
301    }
302
303    fn struct_items(&self, item: &ItemStruct, _: &ImplArgs) -> Result<(Toks, Toks)> {
304        let type_ident = &item.ident;
305        let inner = match &item.fields {
306            Fields::Named(fields) => {
307                let mut toks = Toks::new();
308                for field in fields.named.iter() {
309                    for attr in &field.attrs {
310                        if attr.path().get_ident().is_some_and(|path| path == "cfg") {
311                            attr.to_tokens(&mut toks);
312                        }
313                    }
314
315                    let ident = field.ident.as_ref().unwrap();
316                    toks.append_all(quote! { #ident: Default::default(), });
317                }
318                quote! { #type_ident { #toks } }
319            }
320            Fields::Unnamed(fields) => {
321                let mut toks = Toks::new();
322                for field in fields.unnamed.iter() {
323                    for attr in &field.attrs {
324                        if attr.path().get_ident().is_some_and(|path| path == "cfg") {
325                            attr.to_tokens(&mut toks);
326                        }
327                    }
328
329                    toks.append_all(quote! { Default::default(), });
330                }
331                quote! { #type_ident(#toks) }
332            }
333            Fields::Unit => quote! { #type_ident },
334        };
335
336        let method = quote! {
337            fn default() -> Self {
338                #inner
339            }
340        };
341        Ok((quote! { ::core::default::Default }, method))
342    }
343}
344
345/// Implement [`core::cmp::PartialEq`]
346///
347/// Restriction: `Rhs == Self`
348pub struct ImplPartialEq;
349impl ImplTrait for ImplPartialEq {
350    fn path(&self) -> SimplePath {
351        SimplePath::new(&["", "core", "cmp", "PartialEq"])
352    }
353
354    fn support_ignore(&self) -> bool {
355        true
356    }
357
358    fn enum_items(&self, item: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> {
359        let mut idfmt = IdentFormatter::new();
360        let name = &item.ident;
361        let mut variants = Toks::new();
362        for v in item.variants.iter() {
363            let ident = &v.ident;
364            let tag = quote! { #name :: #ident };
365            variants.append_all(match v.fields {
366                Fields::Named(ref fields) => {
367                    let mut l_args = quote! {};
368                    let mut r_args = quote! {};
369                    let mut cond = quote! {};
370                    for (i, field) in fields.named.iter().enumerate() {
371                        let ident = field.ident.as_ref().unwrap();
372                        let li = idfmt.make_call_site(format_args!("__l{i}"));
373                        let ri = idfmt.make_call_site(format_args!("__r{i}"));
374                        l_args.append_all(quote! { #ident: #li, });
375                        r_args.append_all(quote! { #ident: #ri, });
376                        if !cond.is_empty() {
377                            cond.append_all(quote! { && });
378                        }
379                        cond.append_all(quote! { #li == #ri });
380                    }
381
382                    quote! { (#tag { #l_args }, #tag { #r_args }) => #cond, }
383                }
384                Fields::Unnamed(ref fields) => {
385                    let len = fields.unnamed.len();
386                    let mut l_args = quote! {};
387                    let mut r_args = quote! {};
388                    let mut cond = quote! {};
389                    for i in 0..len {
390                        let li = idfmt.make_call_site(format_args!("__l{i}"));
391                        let ri = idfmt.make_call_site(format_args!("__r{i}"));
392                        l_args.append_all(quote! { #li, });
393                        r_args.append_all(quote! { #ri, });
394                        if !cond.is_empty() {
395                            cond.append_all(quote! { && });
396                        }
397                        cond.append_all(quote! { #li == #ri });
398                    }
399
400                    quote! { (#tag ( #l_args ), #tag ( #r_args )) => #cond, }
401                }
402                Fields::Unit => quote! { (#tag, #tag) => true, },
403            });
404        }
405        variants.append_all(quote! { (_, _) => false, });
406
407        let method = quote! {
408            #[inline]
409            fn eq(&self, other: &Self) -> bool {
410                match (self, other) {
411                    #variants
412                }
413            }
414        };
415        Ok((quote! { ::core::cmp::PartialEq }, method))
416    }
417
418    fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> {
419        let mut toks = Toks::new();
420        let mut require_sep = false;
421        args.for_fields(&item.fields, |member: Member, _| {
422            if require_sep {
423                <Token![&&]>::default().to_tokens(&mut toks);
424            }
425            toks.append_all(quote! { self.#member == other.#member });
426            require_sep = true;
427        });
428        if toks.is_empty() {
429            toks = quote! { true };
430        }
431
432        let method = quote! {
433            #[inline]
434            fn eq(&self, other: &Self) -> bool {
435                #toks
436            }
437        };
438        Ok((quote! { ::core::cmp::PartialEq }, method))
439    }
440}
441
442/// Implement [`core::cmp::Eq`]
443pub struct ImplEq;
444impl ImplTrait for ImplEq {
445    fn path(&self) -> SimplePath {
446        SimplePath::new(&["", "core", "cmp", "Eq"])
447    }
448
449    fn allow_ignore_with(&self) -> Option<SimplePath> {
450        Some(SimplePath::new(&["", "core", "cmp", "PartialEq"]))
451    }
452
453    fn enum_items(&self, _: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> {
454        Ok((quote! { ::core::cmp::Eq }, quote! {}))
455    }
456
457    fn struct_items(&self, _: &ItemStruct, _: &ImplArgs) -> Result<(Toks, Toks)> {
458        Ok((quote! { ::core::cmp::Eq }, quote! {}))
459    }
460}
461
462/// Implement [`core::cmp::PartialOrd`]
463///
464/// Restriction: `Rhs == Self`
465pub struct ImplPartialOrd;
466impl ImplTrait for ImplPartialOrd {
467    fn path(&self) -> SimplePath {
468        SimplePath::new(&["", "core", "cmp", "PartialOrd"])
469    }
470
471    fn support_ignore(&self) -> bool {
472        true
473    }
474
475    fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> {
476        let mut toks = Toks::new();
477        args.for_fields_iter(item.fields.iter().enumerate().rev(), |member: Member, _| {
478            let cmp =
479                quote! { ::core::cmp::PartialOrd::partial_cmp(&self.#member, &other.#member) };
480            if toks.is_empty() {
481                toks = cmp;
482            } else {
483                toks = quote! {
484                    match #cmp {
485                        ::core::option::Option::Some(::core::cmp::Ordering::Equal) => #toks,
486                        cmp => cmp,
487                    }
488                }
489            }
490        });
491        if toks.is_empty() {
492            toks = quote! { ::core::option::Option::Some(::core::cmp::Ordering::Equal) };
493        }
494
495        let method = quote! {
496            #[inline]
497            fn partial_cmp(&self, other: &Self) -> ::core::option::Option<::core::cmp::Ordering> {
498                #toks
499            }
500        };
501        Ok((quote! { ::core::cmp::PartialOrd }, method))
502    }
503}
504
505/// Implement [`core::cmp::Ord`]
506pub struct ImplOrd;
507impl ImplTrait for ImplOrd {
508    fn path(&self) -> SimplePath {
509        SimplePath::new(&["", "core", "cmp", "Ord"])
510    }
511
512    fn support_ignore(&self) -> bool {
513        true
514    }
515
516    fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> {
517        let mut toks = Toks::new();
518        args.for_fields_iter(item.fields.iter().enumerate().rev(), |member: Member, _| {
519            let cmp = quote! { ::core::cmp::Ord::cmp(&self.#member, &other.#member) };
520            if toks.is_empty() {
521                toks = cmp;
522            } else {
523                toks = quote! {
524                    match #cmp {
525                        ::core::cmp::Ordering::Equal => #toks,
526                        cmp => cmp,
527                    }
528                }
529            }
530        });
531        if toks.is_empty() {
532            toks = quote! { ::core::cmp::Ordering::Equal };
533        }
534
535        let method = quote! {
536            #[inline]
537            fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
538                #toks
539            }
540        };
541        Ok((quote! { ::core::cmp::Ord }, method))
542    }
543}
544
545/// Implement [`core::hash::Hash`]
546pub struct ImplHash;
547impl ImplTrait for ImplHash {
548    fn path(&self) -> SimplePath {
549        SimplePath::new(&["", "core", "hash", "Hash"])
550    }
551
552    fn support_ignore(&self) -> bool {
553        true
554    }
555
556    fn enum_items(&self, item: &ItemEnum, _: &ImplArgs) -> Result<(Toks, Toks)> {
557        let mut idfmt = IdentFormatter::new();
558        let name = &item.ident;
559        let mut variants = Toks::new();
560        for v in item.variants.iter() {
561            let ident = &v.ident;
562            variants.append_all(quote! { #name :: #ident });
563            variants.append_all(match v.fields {
564                Fields::Named(ref fields) => {
565                    let idents = fields.named.iter().map(|f| f.ident.as_ref().unwrap());
566                    let hashes = fields.named.iter().map(|f| {
567                        let ident = f.ident.as_ref().unwrap();
568                        quote! { ::core::hash::Hash::hash(&#ident, state); }
569                    });
570                    quote! { { #(ref #idents),* } => { #(#hashes);* } }
571                }
572                Fields::Unnamed(ref fields) => {
573                    let len = fields.unnamed.len();
574                    let mut bindings = Vec::with_capacity(len);
575                    let mut hashes = quote! {};
576                    for i in 0..len {
577                        let ident = idfmt.make_call_site(format_args!("_{i}"));
578                        bindings.push(quote! { ref #ident });
579                        hashes.append_all(quote! {
580                            ::core::hash::Hash::hash(#ident, state);
581                        });
582                    }
583                    quote! { ( #(#bindings),* ) => { #hashes } }
584                }
585                Fields::Unit => quote! { => (), },
586            });
587        }
588        let method = quote! {
589            fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
590                match *self {
591                    #variants
592                }
593            }
594        };
595        Ok((quote! { ::core::hash::Hash }, method))
596    }
597
598    fn struct_items(&self, item: &ItemStruct, args: &ImplArgs) -> Result<(Toks, Toks)> {
599        let mut toks = Toks::new();
600        args.for_fields_iter(item.fields.iter().enumerate().rev(), |member: Member, _| {
601            toks.append_all(quote! { ::core::hash::Hash::hash(&self.#member, state); });
602        });
603
604        let method = quote! {
605            #[inline]
606            fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
607                #toks
608            }
609        };
610        Ok((quote! { ::core::hash::Hash }, method))
611    }
612}