ty_tag_macros/
lib.rs

1//! Proc macro crate for `ty-tag`.
2
3#[cfg(test)]
4mod tests;
5
6mod pretty;
7
8mod errors;
9mod options;
10
11use std::collections::HashMap;
12
13use core::ops::Not;
14use errors::tri;
15use options::AttrOptions;
16use proc_macro2::{Span, TokenStream};
17use quote::{quote, ToTokens};
18use syn::{fold::Fold, parse_quote};
19
20#[proc_macro_attribute]
21pub fn tag(
22    attr: proc_macro::TokenStream,
23    item: proc_macro::TokenStream,
24) -> proc_macro::TokenStream {
25    tag_impl(attr.into(), item.into()).into()
26}
27
28fn tag_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
29    let options = tri!(syn::parse2::<AttrOptions>(attr));
30    let alias = tri!(syn::parse2::<syn::ItemType>(item));
31
32    let options: Options = tri!(options.try_into());
33
34    let attributes = tri!(Attributes::new(&alias.attrs));
35
36    let generics = tri!(Generics::new(&alias.generics.params));
37
38    let struct_def = StructDef::new(&alias, &attributes, &generics);
39
40    let tags_tag_impl = TagsTagImpl::new(&alias, &attributes, &generics);
41
42    let tag_impl = TagImpl::new(&alias, &attributes, &generics);
43
44    let with_lt_impl = WithLtImpl::new(&alias, &attributes, &generics);
45
46    let local_impl = if !options.remote {
47        Some(TagsTyImpl::new(&alias, &attributes, &generics, None))
48    } else {
49        None
50    };
51
52    let local_groups = options
53        .local_groups
54        .iter()
55        .map(|group| TagsTyImpl::new(&alias, &attributes, &generics, Some(group)))
56        .collect::<Vec<_>>();
57
58    quote! {
59        #struct_def
60
61        #local_impl
62
63        #(#local_groups)*
64
65        #tags_tag_impl
66
67        #tag_impl
68
69        #with_lt_impl
70    }
71}
72
73struct Options {
74    remote: bool,
75    local_groups: Vec<syn::Type>,
76}
77
78impl TryFrom<AttrOptions> for Options {
79    type Error = syn::Error;
80
81    fn try_from(value: AttrOptions) -> Result<Self, Self::Error> {
82        let mut remote = false;
83        let mut local_groups = Vec::new();
84
85        for option in value.options {
86            match &*option.name {
87                "remote" => {
88                    remote = true;
89                }
90                "group" => {
91                    let Some(value) = option.value else {
92                        return Err(syn::Error::new(Span::call_site(), "missing type of group"));
93                    };
94
95                    local_groups.push(syn::parse2(value)?);
96                }
97                _ => {
98                    return Err(syn::Error::new(
99                        Span::call_site(),
100                        format!("unknown option `{}`", option.name),
101                    ))
102                }
103            }
104        }
105
106        Ok(Self {
107            remote,
108            local_groups,
109        })
110    }
111}
112
113struct TyTagPath;
114
115impl ToTokens for TyTagPath {
116    fn to_tokens(&self, tokens: &mut TokenStream) {
117        tokens.extend(quote! { ::ty_tag });
118    }
119}
120
121const CRATE: TyTagPath = TyTagPath;
122
123struct StructDef<'a> {
124    attributes: &'a Attributes<'a>,
125    custom_doc: Option<String>,
126    vis: &'a syn::Visibility,
127    name: &'a syn::Ident,
128    generics: &'a Generics<'a>,
129}
130
131impl<'a> StructDef<'a> {
132    fn new(
133        alias: &'a syn::ItemType,
134        attributes: &'a Attributes<'a>,
135        generics: &'a Generics<'a>,
136    ) -> Self {
137        let custom_doc = attributes.docs.is_empty().then(|| {
138            if let Some(link) = JustTypePath::strip_type(&alias.ty) {
139                format!(
140                    "Tag for type [`{0}`]({1}).",
141                    pretty::unparse_type(&alias.ty),
142                    pretty::unparse_type(&link),
143                )
144            } else {
145                format!("Tag for type `{0}`.", pretty::unparse_type(&alias.ty))
146            }
147        });
148
149        Self {
150            attributes,
151            custom_doc,
152            vis: &alias.vis,
153            name: &alias.ident,
154            generics,
155        }
156    }
157}
158
159impl ToTokens for StructDef<'_> {
160    fn to_tokens(&self, tokens: &mut TokenStream) {
161        let Self {
162            attributes,
163            custom_doc,
164            vis,
165            name,
166            generics,
167        } = &self;
168
169        // Each generic type needs to be used so we put it in a PhantomData.
170        // Its the result of a function pointer to not effect the auto traits.
171        // Its also behind a *const because they are ?Sized.
172        let generic_type_holder = generics
173            .types_is_empty()
174            .not()
175            .then(|| {
176                let type_generics =
177                    generics.with_types(|TypeGeneric { ident, .. }| quote! { *const #ident });
178
179                quote! { , ::core::marker::PhantomData<fn() -> (#(#type_generics,)*)> }
180            })
181            .unwrap_or_default();
182
183        // We only need the type and const generics for the tag.
184        // We also include the type alias defaults.
185        let generics_def = generics
186            .types_and_consts_is_empty()
187            .not()
188            .then(|| {
189                let generics = generics.with_types_and_consts(
190                    |TypeGeneric { ident, default, .. }| {
191                        let default = default.iter();
192
193                        quote! { #ident: ?Sized #(= #default)* }
194                    },
195                    |ConstGeneric { ident, ty, default }| {
196                        let default = default.iter();
197
198                        quote! { const #ident: #ty #(= #default)* }
199                    },
200                );
201
202                quote! { <#(#generics),*> }
203            })
204            .unwrap_or_default();
205
206        let custom_doc = custom_doc.as_ref().map(|doc| {
207            quote! { #[doc = #doc] }
208        });
209
210        let attrs = &attributes.struct_attrs;
211        let docs = &attributes.docs;
212
213        // The tag is a normal struct.
214        // The __ type is included to stop a user from constructing the struct.
215        tokens.extend(quote! {
216            #(#docs)*
217            #custom_doc
218            #(#attrs)*
219            #vis struct #name #generics_def(#CRATE::__ #generic_type_holder);
220        });
221    }
222}
223
224struct TagsTyImpl<'a> {
225    attributes: &'a Attributes<'a>,
226    tag_type: TagType<'a>,
227    ty: &'a syn::Type,
228    generics: &'a Generics<'a>,
229    group: Option<&'a syn::Type>,
230}
231
232impl<'a> TagsTyImpl<'a> {
233    fn new(
234        alias: &'a syn::ItemType,
235        attributes: &'a Attributes<'a>,
236        generics: &'a Generics,
237        group: Option<&'a syn::Type>,
238    ) -> Self {
239        Self {
240            attributes,
241            tag_type: TagType::new(&alias.ident, generics),
242            generics,
243            ty: &alias.ty,
244            group,
245        }
246    }
247}
248
249impl ToTokens for TagsTyImpl<'_> {
250    fn to_tokens(&self, tokens: &mut TokenStream) {
251        let Self {
252            attributes,
253            tag_type,
254            generics,
255            ty,
256            group,
257        } = &self;
258
259        let krate = CRATE;
260
261        let lt_generics = generics.with_lifetimes(|LifetimeGeneric { lifetime, bounds }| {
262            if bounds.is_empty() {
263                lifetime.into_token_stream()
264            } else {
265                quote! { #lifetime: #(#bounds)+* }
266            }
267        });
268
269        let types_and_consts_generics = generics.with_types_and_consts(
270            |TypeGeneric {
271                 ident,
272                 bounds,
273                 is_itself,
274                 ..
275             }| {
276                if *is_itself {
277                    quote! {
278                        #ident: #(#bounds)+*
279                    }
280                } else {
281                    quote! {
282                        #ident: #(#bounds + )* ::ty_tag::Tagged<__Group>
283                    }
284                }
285            },
286            |ConstGeneric { ident, ty, .. }| quote! { const #ident: #ty },
287        );
288
289        let generics_def = if generics.is_empty() {
290            if group.is_some() {
291                TokenStream::new()
292            } else {
293                quote! { <__Group: ?Sized> }
294            }
295        } else if group.is_none() {
296            quote! { <#(#lt_generics,)* __Group: ?Sized #(, #types_and_consts_generics)*> }
297        } else {
298            quote! { <#(#lt_generics,)* #( #types_and_consts_generics),*> }
299        };
300
301        let group = if let Some(group) = group {
302            group.into_token_stream()
303        } else {
304            quote! { __Group }
305        };
306
307        let mut replacement = GenericTypeReplace {
308            replacements: generics
309                .with_types(|ty| ty)
310                .filter(|ty| !ty.is_itself)
311                .map(|TypeGeneric { ident, .. }| {
312                    (
313                        *ident,
314                        parse_quote! {
315                            <#ident as #krate::Tagged<#group>>::Tag
316                        },
317                    )
318                })
319                .collect(),
320        };
321
322        let tag_type_tag = replacement.fold_type(parse_quote! { #tag_type });
323
324        let where_clause = if generics
325            .with_types(|ty| !ty.is_itself)
326            .filter(|x| *x)
327            .count()
328            <= 1
329        {
330            TokenStream::new()
331        } else {
332            let type_generics = generics.with_types(|ty| ty).flat_map(
333                |TypeGeneric {
334                     ident, is_itself, ..
335                 }| {
336                    if *is_itself {
337                        None
338                    } else {
339                        Some(quote! {
340                            <
341                                <#ident as #krate::Tagged<#group>>::Tag
342                            as #krate::Tag>::NeededLifetimes,
343                        })
344                    }
345                },
346            );
347
348            quote! {
349                where
350                    (#(#type_generics)*): ::ty_tag::lifetime_list::ops::Longest<
351                        Output: ::ty_tag::lifetime_list::HoleList
352                    >
353            }
354        };
355
356        let attrs = &attributes.impl_attrs;
357
358        tokens.extend(quote! {
359            #(#attrs)*
360            impl #generics_def #CRATE::Tagged<#group> for #ty
361            #where_clause
362            {
363                type Tag = #tag_type_tag;
364            }
365        });
366    }
367}
368
369struct TagsTagImpl<'a> {
370    attributes: &'a Attributes<'a>,
371    generics: &'a Generics<'a>,
372    tag_type: TagType<'a>,
373}
374
375impl<'a> TagsTagImpl<'a> {
376    fn new(
377        alias: &'a syn::ItemType,
378        attributes: &'a Attributes<'a>,
379        generics: &'a Generics,
380    ) -> Self {
381        let tag_type = TagType::new(&alias.ident, generics);
382
383        Self {
384            tag_type,
385            generics,
386            attributes,
387        }
388    }
389}
390
391impl ToTokens for TagsTagImpl<'_> {
392    fn to_tokens(&self, tokens: &mut TokenStream) {
393        let Self {
394            tag_type,
395            generics,
396            attributes,
397        } = &self;
398
399        let krate = CRATE;
400
401        let generics_def = if generics.types_and_consts_is_empty() {
402            quote! { <__Group: ?Sized> }
403        } else {
404            let generics = generics.with_types_and_consts(
405                |TypeGeneric {
406                     ident,
407                     bounds,
408                     is_itself,
409                     ..
410                 }| {
411                    if *is_itself {
412                        quote! { #ident: #(#bounds)+* }
413                    } else {
414                        quote! { #ident: ?Sized + ::ty_tag::Tagged<__Group> }
415                    }
416                },
417                |ConstGeneric { ident, ty, .. }| quote! { const #ident: #ty },
418            );
419
420            quote! { <__Group: ?Sized #(, #generics)*> }
421        };
422
423        let mut replacement = GenericTypeReplace {
424            replacements: generics
425                .with_types(|ty| ty)
426                .filter(|ty| !ty.is_itself)
427                .map(|TypeGeneric { ident, .. }| {
428                    (
429                        *ident,
430                        parse_quote! {
431                            <#ident as #krate::Tagged<__Group>>::Tag
432                        },
433                    )
434                })
435                .collect(),
436        };
437
438        let tag_type_tag = replacement.fold_type(parse_quote! { #tag_type });
439
440        let where_clause = if generics
441            .with_types(|ty| !ty.is_itself)
442            .filter(|x| *x)
443            .count()
444            <= 1
445        {
446            TokenStream::new()
447        } else {
448            let type_generics = generics.with_types(|ty| ty).flat_map(
449                |TypeGeneric {
450                     ident, is_itself, ..
451                 }| {
452                    if *is_itself {
453                        None
454                    } else {
455                        Some(quote! {
456                            <
457                                <#ident as #krate::Tagged<__Group>>::Tag
458                            as #krate::Tag>::NeededLifetimes,
459                        })
460                    }
461                },
462            );
463
464            quote! {
465                where
466                    (#(#type_generics)*): ::ty_tag::lifetime_list::ops::Longest<
467                        Output: ::ty_tag::lifetime_list::HoleList
468                    >
469            }
470        };
471
472        let attrs = &attributes.impl_attrs;
473
474        tokens.extend(quote! {
475            #(#attrs)*
476            impl #generics_def #CRATE::Tagged<__Group> for #tag_type
477            #where_clause
478            {
479                type Tag = #tag_type_tag;
480            }
481        });
482    }
483}
484
485struct TagImpl<'a> {
486    attributes: &'a Attributes<'a>,
487    hole_count: usize,
488    generics: &'a Generics<'a>,
489    tag_type: TagType<'a>,
490}
491
492impl<'a> TagImpl<'a> {
493    fn new(
494        alias: &'a syn::ItemType,
495        attributes: &'a Attributes<'a>,
496        generics: &'a Generics,
497    ) -> Self {
498        let tag_type = TagType::new(&alias.ident, generics);
499
500        Self {
501            attributes,
502            hole_count: alias.generics.lifetimes().count(),
503            generics,
504            tag_type,
505        }
506    }
507}
508
509impl ToTokens for TagImpl<'_> {
510    fn to_tokens(&self, tokens: &mut TokenStream) {
511        let Self {
512            hole_count,
513            generics,
514            tag_type,
515            attributes,
516        } = &self;
517
518        let holes = if *hole_count != 0 {
519            let tail = if generics
520                .with_types(|ty| !ty.is_itself)
521                .filter(|x| *x)
522                .count()
523                == 1
524            {
525                let type_generics = generics.with_types(|ty| &ty.ident);
526
527                Some(quote! { <#(#type_generics)* as #CRATE::Tag>::NeededLifetimes })
528            } else if generics
529                .with_types(|ty| !ty.is_itself)
530                .filter(|x| *x)
531                .count()
532                == 0
533            {
534                None
535            } else {
536                let type_generics = generics.with_types(|ty| ty).flat_map(
537                    |TypeGeneric {
538                         ident, is_itself, ..
539                     }| if *is_itself { None } else { Some(ident) },
540                );
541
542                Some(quote! {
543                    <(#(
544                        <#type_generics as ::ty_tag::Tag>::NeededLifetimes,
545                    )*) as ::ty_tag::lifetime_list::ops::Longest>::Output
546                })
547            };
548
549            let holes = HoleList {
550                count: *hole_count,
551                tail,
552            };
553            quote! { #holes }
554        } else if generics
555            .with_types(|ty| !ty.is_itself)
556            .filter(|x| *x)
557            .count()
558            == 0
559        {
560            let holes = HoleList {
561                count: 0,
562                tail: None,
563            };
564            quote! { #holes }
565        } else if generics
566            .with_types(|ty| !ty.is_itself)
567            .filter(|x| *x)
568            .count()
569            == 1
570        {
571            let type_generics = generics.with_types(|ty| ty).flat_map(
572                |TypeGeneric {
573                     ident, is_itself, ..
574                 }| if *is_itself { None } else { Some(ident) },
575            );
576
577            quote! { <#(#type_generics)* as #CRATE::Tag>::NeededLifetimes }
578        } else {
579            let type_generics = generics.with_types(|ty| ty).flat_map(
580                |TypeGeneric {
581                     ident, is_itself, ..
582                 }| if *is_itself { None } else { Some(ident) },
583            );
584
585            quote! {
586                <(#(
587                    <#type_generics as ::ty_tag::Tag>::NeededLifetimes,
588                )*) as ::ty_tag::lifetime_list::ops::Longest>::Output
589            }
590        };
591
592        let krate = CRATE;
593
594        let where_clause = if generics
595            .with_types(|ty| !ty.is_itself)
596            .filter(|x| *x)
597            .count()
598            <= 1
599        {
600            TokenStream::new()
601        } else {
602            let type_generics = generics.with_types(|ty| ty).flat_map(
603                |TypeGeneric {
604                     ident, is_itself, ..
605                 }| {
606                    if *is_itself {
607                        None
608                    } else {
609                        Some(quote! {
610                            <#ident as #krate::Tag>::NeededLifetimes,
611                        })
612                    }
613                },
614            );
615
616            quote! {
617                where
618                    (#(#type_generics)*): ::ty_tag::lifetime_list::ops::Longest<
619                        Output: ::ty_tag::lifetime_list::HoleList
620                    >
621            }
622        };
623
624        let generics_def = if generics.is_empty() {
625            TokenStream::new()
626        } else {
627            let generics = generics.with_types_and_consts(
628                |TypeGeneric {
629                     ident,
630                     bounds,
631                     is_itself,
632                     ..
633                 }| {
634                    if *is_itself {
635                        quote! {
636                            #ident: #(#bounds)+*
637                        }
638                    } else {
639                        quote! {
640                            #ident: ?Sized + ::ty_tag::Tag
641                        }
642                    }
643                },
644                |ConstGeneric { ident, ty, .. }| quote! { const #ident: #ty },
645            );
646
647            quote! { <#(#generics),*> }
648        };
649
650        let attrs = &attributes.impl_attrs;
651
652        tokens.extend(quote! {
653            #(#attrs)*
654            impl #generics_def #CRATE::Tag for #tag_type
655            #where_clause
656            {
657                type NeededLifetimes = #holes;
658            }
659        });
660    }
661}
662
663struct WithLtImpl<'a> {
664    attributes: &'a Attributes<'a>,
665    ty: syn::Type,
666    sized_bounds: Vec<syn::Ident>,
667    where_bounds: Vec<syn::WherePredicate>,
668    tag_type: TagType<'a>,
669    generics: &'a Generics<'a>,
670}
671
672impl<'a> WithLtImpl<'a> {
673    fn new(
674        alias: &'a syn::ItemType,
675        attributes: &'a Attributes<'a>,
676        generics: &'a Generics,
677    ) -> Self {
678        let mut sized_bounds = HashMap::new();
679        let mut where_bounds = Vec::new();
680
681        for generic in generics.with_types(|ty| ty) {
682            if generic.is_itself {
683                continue
684            }
685
686            let bound = syn::WherePredicate::Type(syn::PredicateType {
687                lifetimes: None,
688                bounded_ty: syn::Type::Path(syn::TypePath {
689                    qself: None,
690                    path: syn::Path {
691                        leading_colon: None,
692                        segments: [syn::PathSegment {
693                            ident: generic.ident.clone(),
694                            arguments: syn::PathArguments::None,
695                        }]
696                        .into_iter()
697                        .collect(),
698                    },
699                }),
700                colon_token: Default::default(),
701                bounds: generic.bounds.iter().map(|x| (**x).clone()).collect(),
702            });
703
704            let sized = sized_bounds
705                .entry(generic.ident.clone())
706                .or_insert(FindAndRemoveSized { sized: None });
707
708            let bound = sized.fold_where_predicate(bound);
709
710            where_bounds.push(bound);
711        }
712
713        if let Some(clause) = &alias.generics.where_clause {
714            for predicate in &clause.predicates {
715                match predicate {
716                    syn::WherePredicate::Type(predicate_type) => {
717                        let bound = match &predicate_type.bounded_ty {
718                            syn::Type::Path(type_path) => {
719                                if let Some(ident) = type_path.path.get_ident() {
720                                    if let Some(sized) = sized_bounds.get_mut(ident) {
721                                        sized.fold_where_predicate(predicate.clone())
722                                    } else {
723                                        predicate.clone()
724                                    }
725                                } else {
726                                    predicate.clone()
727                                }
728                            }
729                            _ => predicate.clone(),
730                        };
731                        where_bounds.push(bound);
732                    }
733                    _ => where_bounds.push(predicate.clone()),
734                }
735            }
736        }
737
738        let mut replacement = GenericTypeReplace {
739            replacements: generics
740                .with_types(|ty| ty)
741                .filter(|ty| !ty.is_itself)
742                .map(|TypeGeneric { ident, .. }| {
743                    (
744                        *ident,
745                        parse_quote! {
746                            <#ident as #CRATE::WithLt<__L>>::Reified
747                        },
748                    )
749                })
750                .collect(),
751        };
752
753        for bound in &mut where_bounds {
754            *bound = replacement.fold_where_predicate(bound.clone());
755        }
756
757        if generics
758            .with_types(|ty| ty)
759            .filter(|ty| !ty.is_itself)
760            .count()
761            >= 2
762        {
763            let krate = CRATE;
764
765            let type_generics = generics.with_types(|ty| ty).filter(|ty| !ty.is_itself).map(
766                |TypeGeneric { ident, .. }| {
767                    quote! {
768                        <#ident as #krate::Tag>::NeededLifetimes,
769                    }
770                },
771            );
772
773            where_bounds.push(parse_quote! {
774                    (#(#type_generics)*): ::ty_tag::lifetime_list::ops::Longest<
775                        Output: ::ty_tag::lifetime_list::HoleList
776                    >
777            });
778        }
779
780        where_bounds.retain(|bound| match bound {
781            syn::WherePredicate::Type(predicate_type) => !predicate_type.bounds.is_empty(),
782            _ => true,
783        });
784
785        let tag_type = TagType::new(&alias.ident, generics);
786
787        let mut sized_bounds = sized_bounds
788            .into_iter()
789            .flat_map(|(ident, find)| {
790                if matches!(find.sized, Some(true) | None) {
791                    Some(ident)
792                } else {
793                    None
794                }
795            })
796            .collect::<Vec<_>>();
797
798        sized_bounds.sort();
799
800        Self {
801            ty: (*alias.ty).clone(),
802            generics,
803            tag_type,
804            where_bounds,
805            sized_bounds,
806            attributes,
807        }
808    }
809}
810
811impl ToTokens for WithLtImpl<'_> {
812    fn to_tokens(&self, tokens: &mut TokenStream) {
813        let Self {
814            ty,
815            generics,
816            sized_bounds,
817            where_bounds,
818            tag_type,
819            attributes,
820        } = self;
821
822        let krate = CRATE;
823
824        let generics_def = if generics.lifetimes_is_empty() {
825            if generics.is_empty() {
826                TokenStream::new()
827            } else if generics.with_types(|ty| ty).filter(|ty| !ty.is_itself).count() == 0 {
828                let generics = generics.with(
829                    |LifetimeGeneric { lifetime, bounds }| {
830                        if bounds.is_empty() {
831                            lifetime.into_token_stream()
832                        } else {
833                            quote! { #lifetime: #(#bounds)+* }
834                        }
835                    },
836                    |TypeGeneric {
837                         ident,
838                         bounds,
839                         is_itself,
840                         ..
841                     }| {
842                        if *is_itself {
843                            quote! {
844                                #ident: #(#bounds)+*
845                            }
846                        } else {
847                            quote! {
848                                #ident: ?Sized + ::ty_tag::WithLt<__L>
849                            }
850                        }
851                    },
852                    |ConstGeneric { ident, ty, .. }| quote! { const #ident: #ty },
853                );
854
855                quote! {
856                    <#(#generics),*>
857                }
858            } else {
859                let generics = generics.with(
860                    |LifetimeGeneric { lifetime, bounds }| {
861                        if bounds.is_empty() {
862                            lifetime.into_token_stream()
863                        } else {
864                            quote! { #lifetime: #(#bounds)+* }
865                        }
866                    },
867                    |TypeGeneric {
868                         ident,
869                         bounds,
870                         is_itself,
871                         ..
872                     }| {
873                        if *is_itself {
874                            quote! {
875                                #ident: #(#bounds)+*
876                            }
877                        } else {
878                            quote! {
879                                #ident: ?Sized + ::ty_tag::WithLt<__L>
880                            }
881                        }
882                    },
883                    |ConstGeneric { ident, ty, .. }| quote! { const #ident: #ty },
884                );
885
886                quote! {
887                    <
888                        __L: #krate::lifetime_list::LifetimeList,
889                        #(#generics),*
890                    >
891                }
892            }
893        } else if generics.is_empty() {
894            let lt_generics = generics.with_lifetimes(|LifetimeGeneric { lifetime, bounds }| {
895                if bounds.is_empty() {
896                    lifetime.into_token_stream()
897                } else {
898                    quote! { #lifetime: #(#bounds)+* }
899                }
900            });
901
902            quote! { <#(#lt_generics),*> }
903        } else if generics.with_types(|ty| ty).filter(|ty| !ty.is_itself).count() == 0 {
904            let generics = generics.with(
905                |LifetimeGeneric { lifetime, bounds }| {
906                    if bounds.is_empty() {
907                        lifetime.into_token_stream()
908                    } else {
909                        quote! { #lifetime: #(#bounds)+* }
910                    }
911                },
912                |TypeGeneric {
913                     ident,
914                     bounds,
915                     is_itself,
916                     ..
917                 }| {
918                    if *is_itself {
919                        quote! {
920                            #ident: #(#bounds)+*
921                        }
922                    } else {
923                        quote! {
924                            #ident: ?Sized + ::ty_tag::WithLt<__L>
925                        }
926                    }
927                },
928                |ConstGeneric { ident, ty, .. }| quote! { const #ident: #ty },
929            );
930
931            quote! {
932                <#(#generics),*>
933            }
934        } else {
935            let lt_generics = generics.with_lifetimes(|LifetimeGeneric { lifetime, bounds }| {
936                if bounds.is_empty() {
937                    lifetime.into_token_stream()
938                } else {
939                    quote! { #lifetime: #(#bounds)+* }
940                }
941            });
942
943            let generics = generics.with_types_and_consts(
944                |TypeGeneric {
945                     ident,
946                     bounds,
947                     is_itself,
948                     ..
949                 }| {
950                    if *is_itself {
951                        quote! {
952                            #ident: #(#bounds)+*
953                        }
954                    } else {
955                        quote! {
956                            #ident: ?Sized + ::ty_tag::WithLt<__L>
957                        }
958                    }
959                },
960                |ConstGeneric { ident, ty, .. }| quote! { const #ident: #ty },
961            );
962
963            quote! {
964                <
965                    #(#lt_generics,)*
966                    __L: #krate::lifetime_list::LifetimeList,
967                    #(#generics),*
968                >
969            }
970        };
971
972        let where_clause = match (where_bounds.is_empty(), sized_bounds.is_empty()) {
973            (true, true) => TokenStream::new(),
974            (true, false) => {
975                let sized_bounds = sized_bounds.iter().collect::<Vec<_>>();
976
977                quote! {
978                    where
979                        #(<#sized_bounds as #krate::WithLt<__L>>::Reified: Sized),*
980                }
981            }
982            (false, true) => {
983                quote! {
984                    where
985                        #(#where_bounds),*
986                }
987            }
988            (false, false) => {
989                let sized_bounds = sized_bounds.iter().collect::<Vec<_>>();
990
991                quote! {
992                    where
993                        #(<#sized_bounds as #krate::WithLt<__L>>::Reified: Sized,)*
994                        #(#where_bounds),*
995                }
996            }
997        };
998
999        let l = if generics.with_types(|ty| ty).filter(|ty| !ty.is_itself).count() == 0 {
1000            let lifetimes = generics
1001                .with_lifetimes(|l| l.lifetime.clone())
1002                .collect::<Vec<_>>();
1003
1004            let l = LifetimeList {
1005                lifetimes: &lifetimes,
1006                tail: None,
1007            };
1008
1009            quote! { #l }
1010        } else if generics.lifetimes_is_empty() {
1011            quote! { __L }
1012        } else {
1013            let lifetimes = generics
1014                .with_lifetimes(|l| l.lifetime.clone())
1015                .collect::<Vec<_>>();
1016
1017            let l = LifetimeList {
1018                lifetimes: &lifetimes,
1019                tail: Some(quote! { __L }),
1020            };
1021            quote! { #l }
1022        };
1023
1024        let mut replacement = GenericTypeReplace {
1025            replacements: generics
1026                .with_types(|ty| ty)
1027                .filter(|ty| !ty.is_itself)
1028                .map(|TypeGeneric { ident, .. }| {
1029                    (
1030                        *ident,
1031                        parse_quote! {
1032                            <#ident as #CRATE::WithLt<__L>>::Reified
1033                        },
1034                    )
1035                })
1036                .collect(),
1037        };
1038
1039        let reified_ty = replacement.fold_type(ty.clone());
1040
1041        let attrs = &attributes.impl_attrs;
1042
1043        tokens.extend(quote! {
1044            #(#attrs)*
1045            impl #generics_def #CRATE::WithLt<#l> for #tag_type
1046            #where_clause
1047            {
1048                type Reified = #reified_ty;
1049            }
1050        });
1051    }
1052}
1053
1054struct LifetimeList<'a> {
1055    lifetimes: &'a [syn::Lifetime],
1056    tail: Option<TokenStream>,
1057}
1058
1059impl ToTokens for LifetimeList<'_> {
1060    fn to_tokens(&self, tokens: &mut TokenStream) {
1061        let mut iter = self.lifetimes.iter();
1062
1063        if let Some(lt) = iter.next() {
1064            tokens.extend(
1065                quote! { #CRATE::lifetime_list::cons::Cons<#CRATE::lifetime_list::Inv<#lt> },
1066            );
1067        } else {
1068            tokens.extend(quote! { #CRATE::lifetime_list::cons::Nil });
1069            return;
1070        }
1071
1072        let mut count = 1;
1073
1074        for lt in iter {
1075            count += 1;
1076            tokens.extend(
1077                quote! { , #CRATE::lifetime_list::cons::Cons<#CRATE::lifetime_list::Inv<#lt> },
1078            );
1079        }
1080
1081        if let Some(tail) = &self.tail {
1082            tokens.extend(quote! {, #tail});
1083        }
1084
1085        // End the cons list.
1086        for _ in 0..count {
1087            tokens.extend(quote! { > });
1088        }
1089    }
1090}
1091
1092struct HoleList {
1093    count: usize,
1094    tail: Option<TokenStream>,
1095}
1096
1097impl ToTokens for HoleList {
1098    fn to_tokens(&self, tokens: &mut TokenStream) {
1099        if self.count != 0 {
1100            tokens.extend(quote! { #CRATE::lifetime_list::cons::Cons<#CRATE::lifetime_list::Hole });
1101        } else {
1102            tokens.extend(quote! { #CRATE::lifetime_list::cons::Nil });
1103            return;
1104        }
1105
1106        for _ in 0..(self.count - 1) {
1107            tokens
1108                .extend(quote! { , #CRATE::lifetime_list::cons::Cons<#CRATE::lifetime_list::Hole });
1109        }
1110
1111        if let Some(tail) = &self.tail {
1112            tokens.extend(quote! {, #tail});
1113        }
1114
1115        // End the cons list.
1116        for _ in 0..self.count {
1117            tokens.extend(quote! { > });
1118        }
1119    }
1120}
1121
1122struct GenericTypeReplace<'a> {
1123    replacements: HashMap<&'a syn::Ident, syn::TypePath>,
1124}
1125
1126impl Fold for GenericTypeReplace<'_> {
1127    fn fold_type_path(&mut self, i: syn::TypePath) -> syn::TypePath {
1128        if i.qself.is_some() {
1129            return syn::fold::fold_type_path(self, i);
1130        }
1131
1132        let Some(ident) = i.path.get_ident() else {
1133            return syn::fold::fold_type_path(self, i);
1134        };
1135
1136        if let Some(path) = self.replacements.get(ident) {
1137            parse_quote!(#path)
1138        } else {
1139            syn::fold::fold_type_path(self, i)
1140        }
1141    }
1142}
1143
1144struct FindAndRemoveSized {
1145    sized: Option<bool>,
1146}
1147
1148impl Fold for FindAndRemoveSized {
1149    fn fold_predicate_type(&mut self, i: syn::PredicateType) -> syn::PredicateType {
1150        syn::PredicateType {
1151            lifetimes: i.lifetimes,
1152            bounded_ty: i.bounded_ty,
1153            colon_token: i.colon_token,
1154            bounds: i
1155                .bounds
1156                .into_iter()
1157                .flat_map(|bound| match &bound {
1158                    syn::TypeParamBound::Trait(trait_bound) => {
1159                        if trait_bound
1160                            .path
1161                            .get_ident()
1162                            .map(|ident| ident == "Sized")
1163                            .unwrap_or_default()
1164                        {
1165                            if matches!(self.sized, Some(false) | None) {
1166                                self.sized = Some(matches!(
1167                                    trait_bound.modifier,
1168                                    syn::TraitBoundModifier::None
1169                                ));
1170                            }
1171
1172                            None
1173                        } else {
1174                            Some(bound)
1175                        }
1176                    }
1177                    _ => Some(bound),
1178                })
1179                .collect(),
1180        }
1181    }
1182}
1183
1184struct TagType<'a> {
1185    ident: &'a syn::Ident,
1186    generics: &'a Generics<'a>,
1187}
1188
1189impl<'a> TagType<'a> {
1190    fn new(ident: &'a syn::Ident, generics: &'a Generics) -> Self {
1191        Self { ident, generics }
1192    }
1193}
1194
1195impl ToTokens for TagType<'_> {
1196    fn to_tokens(&self, tokens: &mut TokenStream) {
1197        if self.generics.types_and_consts_is_empty() {
1198            self.ident.to_tokens(tokens)
1199        } else {
1200            let Self { ident, generics } = self;
1201
1202            let generics = generics.with_types_and_consts(
1203                |TypeGeneric { ident, .. }| ident,
1204                |ConstGeneric { ident, .. }| ident,
1205            );
1206
1207            tokens.extend(quote! {
1208                #ident <#(#generics),*>
1209            });
1210        }
1211    }
1212}
1213
1214struct JustTypePath;
1215
1216impl JustTypePath {
1217    fn strip_type(ty: &syn::Type) -> Option<syn::Type> {
1218        match ty {
1219            syn::Type::Path(_) => Some(JustTypePath.fold_type(ty.clone())),
1220            _ => None,
1221        }
1222    }
1223}
1224
1225impl Fold for JustTypePath {
1226    fn fold_path_segment(&mut self, mut segment: syn::PathSegment) -> syn::PathSegment {
1227        segment.arguments = syn::PathArguments::None;
1228        segment
1229    }
1230}
1231
1232struct Generics<'a> {
1233    generics: Vec<Generic<'a>>,
1234}
1235
1236enum Generic<'a> {
1237    Lifetime(LifetimeGeneric<'a>),
1238    Type(TypeGeneric<'a>),
1239    Const(ConstGeneric<'a>),
1240}
1241
1242struct LifetimeGeneric<'a> {
1243    lifetime: &'a syn::Lifetime,
1244    bounds: Vec<&'a syn::Lifetime>,
1245}
1246
1247struct TypeGeneric<'a> {
1248    ident: &'a syn::Ident,
1249    bounds: Vec<&'a syn::TypeParamBound>,
1250    default: Option<&'a syn::Type>,
1251    is_itself: bool,
1252}
1253
1254struct ConstGeneric<'a> {
1255    ident: &'a syn::Ident,
1256    ty: &'a syn::Type,
1257    default: Option<&'a syn::Expr>,
1258}
1259
1260impl<'a> Generics<'a> {
1261    fn new<T: IntoIterator<Item = &'a syn::GenericParam>>(generics: T) -> syn::Result<Self> {
1262        Ok(Self {
1263            generics: generics
1264                .into_iter()
1265                .map(|generic| {
1266                    Ok(match generic {
1267                        syn::GenericParam::Lifetime(lifetime_param) => {
1268                            if let Some(attr) = lifetime_param.attrs.first() {
1269                                return Err(syn::Error::new_spanned(attr, "unknown attribute"));
1270                            }
1271
1272                            Generic::Lifetime(LifetimeGeneric {
1273                                lifetime: &lifetime_param.lifetime,
1274                                bounds: lifetime_param.bounds.iter().collect(),
1275                            })
1276                        }
1277                        syn::GenericParam::Type(type_param) => {
1278                            let mut is_itself = false;
1279
1280                            for attr in &type_param.attrs {
1281                                if matches!(attr.style, syn::AttrStyle::Inner(_)) {
1282                                    return Err(syn::Error::new_spanned(
1283                                        attr,
1284                                        "inner attribute not allowed",
1285                                    ));
1286                                }
1287
1288                                match &attr.meta {
1289                                    syn::Meta::Path(path)
1290                                        if path
1291                                            .get_ident()
1292                                            .map(|ident| ident == "itself")
1293                                            .unwrap_or_default() =>
1294                                    {
1295                                        if is_itself {
1296                                            return Err(syn::Error::new_spanned(
1297                                                attr,
1298                                                "cannot set `itself` more than once",
1299                                            ));
1300                                        }
1301
1302                                        is_itself = true;
1303                                    }
1304                                    _ => {
1305                                        return Err(syn::Error::new_spanned(
1306                                            attr,
1307                                            "unknown attribute",
1308                                        ));
1309                                    }
1310                                }
1311                            }
1312
1313                            Generic::Type(TypeGeneric {
1314                                ident: &type_param.ident,
1315                                bounds: type_param.bounds.iter().collect(),
1316                                default: type_param.default.as_ref(),
1317                                is_itself,
1318                            })
1319                        }
1320                        syn::GenericParam::Const(const_param) => {
1321                            if let Some(attr) = const_param.attrs.first() {
1322                                return Err(syn::Error::new_spanned(attr, "unknown attribute"));
1323                            }
1324
1325                            Generic::Const(ConstGeneric {
1326                                ident: &const_param.ident,
1327                                ty: &const_param.ty,
1328                                default: const_param.default.as_ref(),
1329                            })
1330                        }
1331                    })
1332                })
1333                .collect::<syn::Result<_>>()?,
1334        })
1335    }
1336
1337    fn is_empty(&self) -> bool {
1338        self.generics.is_empty()
1339    }
1340
1341    fn types_is_empty(&self) -> bool {
1342        !self
1343            .generics
1344            .iter()
1345            .any(|generic| matches!(generic, Generic::Type(_)))
1346    }
1347
1348    fn lifetimes_is_empty(&self) -> bool {
1349        !self
1350            .generics
1351            .iter()
1352            .any(|generic| matches!(generic, Generic::Lifetime(_)))
1353    }
1354
1355    fn types_and_consts_is_empty(&self) -> bool {
1356        !self
1357            .generics
1358            .iter()
1359            .any(|generic| matches!(generic, Generic::Type(_) | Generic::Const(_)))
1360    }
1361
1362    fn with_types<'b, T, R>(&'b self, mut t: T) -> impl Iterator<Item = R> + use<'b, T, R>
1363    where
1364        T: FnMut(&'b TypeGeneric) -> R,
1365    {
1366        self.generics.iter().flat_map(move |generic| match generic {
1367            Generic::Type(ty) => Some(t(ty)),
1368            _ => None,
1369        })
1370    }
1371
1372    fn with_types_and_consts<'b, T, C, R>(
1373        &'b self,
1374        mut t: T,
1375        mut c: C,
1376    ) -> impl Iterator<Item = R> + use<'b, T, C, R>
1377    where
1378        T: FnMut(&'b TypeGeneric) -> R,
1379        C: FnMut(&'b ConstGeneric) -> R,
1380    {
1381        self.generics.iter().flat_map(move |generic| match generic {
1382            Generic::Type(ty) => Some(t(ty)),
1383            Generic::Const(cons) => Some(c(cons)),
1384            _ => None,
1385        })
1386    }
1387
1388    fn with_lifetimes<'b, L, R>(&'b self, mut l: L) -> impl Iterator<Item = R> + use<'b, L, R>
1389    where
1390        L: FnMut(&'b LifetimeGeneric) -> R,
1391    {
1392        self.generics.iter().flat_map(move |generic| match generic {
1393            Generic::Lifetime(lifetime) => Some(l(lifetime)),
1394            _ => None,
1395        })
1396    }
1397
1398    fn with<'b, L, T, C, R>(
1399        &'b self,
1400        mut l: L,
1401        mut t: T,
1402        mut c: C,
1403    ) -> impl Iterator<Item = R> + use<'b, L, T, C, R>
1404    where
1405        L: FnMut(&'b LifetimeGeneric) -> R,
1406        T: FnMut(&'b TypeGeneric) -> R,
1407        C: FnMut(&'b ConstGeneric) -> R,
1408    {
1409        self.generics.iter().map(move |generic| match generic {
1410            Generic::Lifetime(lifetime) => l(lifetime),
1411            Generic::Type(ty) => t(ty),
1412            Generic::Const(cons) => c(cons),
1413        })
1414    }
1415}
1416
1417struct Attributes<'a> {
1418    docs: Vec<&'a syn::Attribute>,
1419    struct_attrs: Vec<&'a syn::Attribute>,
1420    impl_attrs: Vec<&'a syn::Attribute>,
1421}
1422
1423impl<'a> Attributes<'a> {
1424    fn new<I: IntoIterator<Item = &'a syn::Attribute>>(attrs: I) -> syn::Result<Self> {
1425        let mut docs = Vec::new();
1426        let mut struct_attrs = Vec::new();
1427        let mut impl_attrs = Vec::new();
1428
1429        for attr in attrs {
1430            if let Some(ident) = attr.path().get_ident() {
1431                match &*ident.to_string() {
1432                    // Only put this on the struct because we can't expect the user to know the
1433                    // impls exist.
1434                    "expect" => {
1435                        struct_attrs.push(attr);
1436                    }
1437                    // Apply to all the generated code so the user can set lints for all of it.
1438                    "allow" | "warn" | "deny" | "forbid" => {
1439                        struct_attrs.push(attr);
1440                        impl_attrs.push(attr);
1441                    }
1442                    // cfg always happens first, but we shouldn't error on it.
1443                    // cfg_attr isn't allowed because we don't know where to put it.
1444                    "cfg" => {}
1445                    // Deprecated only works on the struct not the impls.
1446                    "deprecated" => {
1447                        struct_attrs.push(attr);
1448                    }
1449                    // Doc attributes are separate because we generate a doc when they aren't
1450                    // there.
1451                    "doc" | "doc_cfg" => {
1452                        docs.push(attr);
1453                    }
1454                    // Everything else we don't allow because we may want to do something specific
1455                    // in the future.
1456                    _ => {
1457                        return Err(syn::Error::new_spanned(attr, "unknown attribute"));
1458                    }
1459                }
1460            } else {
1461                // We don't allow any non ident attributes #[something::other] because there are
1462                // none of those we care about currently.
1463                return Err(syn::Error::new_spanned(attr, "unknown attribute"));
1464            }
1465        }
1466
1467        Ok(Self {
1468            docs,
1469            struct_attrs,
1470            impl_attrs,
1471        })
1472    }
1473}