Skip to main content

zerofrom_derive/
lib.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5// https://github.com/unicode-org/icu4x/blob/main/documents/process/boilerplate.md#library-annotations
6// #![cfg_attr(not(any(test, doc)), no_std)]
7#![cfg_attr(
8    not(test),
9    deny(
10        clippy::indexing_slicing,
11        clippy::unwrap_used,
12        clippy::expect_used,
13        clippy::panic,
14    )
15)]
16#![warn(missing_docs)]
17
18//! Custom derives for `ZeroFrom` from the `zerofrom` crate.
19
20use proc_macro::TokenStream;
21use proc_macro2::{Span, TokenStream as TokenStream2};
22use quote::quote;
23use std::collections::{HashMap, HashSet};
24use syn::fold::{self, Fold};
25use syn::punctuated::Punctuated;
26use syn::spanned::Spanned;
27use syn::{
28    parse_macro_input, parse_quote, DeriveInput, Ident, Lifetime, MetaList, Token,
29    TraitBoundModifier, Type, TypeParamBound, TypePath, WherePredicate,
30};
31use synstructure::Structure;
32mod visitor;
33
34/// Custom derive for `zerofrom::ZeroFrom`,
35///
36/// This implements `ZeroFrom<Ty> for Ty` for types
37/// without a lifetime parameter, and `ZeroFrom<Ty<'data>> for Ty<'static>`
38/// for types with a lifetime parameter.
39///
40/// Apply the `#[zerofrom(clone)]` attribute to a field if it doesn't implement
41/// [`Copy`] or [`ZeroFrom`]; this data will be cloned when the struct is zero-from'ed.
42///
43/// Apply the `#[zerofrom(maybe_borrow(T, U, V))]` attribute to the struct to indicate
44/// that certain type parameters may themselves contain borrows (by default
45/// the derives assume that type parameters perform no borrows and can be copied or cloned).
46///
47/// In rust versions where [this issue](https://github.com/rust-lang/rust/issues/114393) is fixed,
48/// `#[zerofrom(may_borrow)]` can be applied directly to type parameters.
49#[proc_macro_derive(ZeroFrom, attributes(zerofrom))]
50pub fn zf_derive(input: TokenStream) -> TokenStream {
51    let input = match ::syn::parse::<DeriveInput>(input) {
    ::syn::__private::Ok(data) => data,
    ::syn::__private::Err(err) => {
        return ::syn::__private::TokenStream::from(err.to_compile_error());
    }
}parse_macro_input!(input as DeriveInput);
52    TokenStream::from(zf_derive_impl(&input))
53}
54
55fn has_attr(attrs: &[syn::Attribute], name: &str) -> bool {
56    attrs.iter().any(|a| {
57        if let Ok(i) = a.parse_args::<Ident>() {
58            if i == name {
59                return true;
60            }
61        }
62        false
63    })
64}
65
66// Collects all idents from #[zerofrom(may_borrow(A, B, C, D))]
67// needed since #[zerofrom(may_borrow)] doesn't work yet
68// (https://github.com/rust-lang/rust/issues/114393)
69fn get_may_borrow_attr(attrs: &[syn::Attribute]) -> Result<HashSet<Ident>, Span> {
70    let mut params = HashSet::new();
71    for attr in attrs {
72        if let Ok(list) = attr.parse_args::<MetaList>() {
73            if list.path.is_ident("may_borrow") {
74                if let Ok(list) =
75                    list.parse_args_with(Punctuated::<Ident, ::syn::token::CommaToken![,]>::parse_terminated)
76                {
77                    params.extend(list)
78                } else {
79                    return Err(attr.span());
80                }
81            }
82        }
83    }
84    Ok(params)
85}
86
87fn zf_derive_impl(input: &DeriveInput) -> TokenStream2 {
88    let mut tybounds = input
89        .generics
90        .type_params()
91        .map(|ty| {
92            // Strip out param defaults, we don't need them in the impl
93            let mut ty = ty.clone();
94            ty.eq_token = None;
95            ty.default = None;
96            ty
97        })
98        .collect::<Vec<_>>();
99    let typarams = tybounds
100        .iter()
101        .map(|ty| ty.ident.clone())
102        .collect::<Vec<_>>();
103    let lts = input.generics.lifetimes().count();
104    let name = &input.ident;
105    let structure = Structure::new(input);
106
107    let may_borrow_attrs = match get_may_borrow_attr(&input.attrs) {
108        Ok(mb) => mb,
109        Err(span) => {
110            return syn::Error::new(
111            span,
112            "#[zerofrom(may_borrow)] on the struct takes in a comma separated list of type parameters, like so: `#[zerofrom(may_borrow(A, B, C, D)]`",
113        ).to_compile_error();
114        }
115    };
116
117    // This contains every generic type introduced in this code.
118    // If the gneeric type is may_borrow, this additionally contains the identifier corresponding to
119    // a newly introduced mirror type parameter that we are borrowing from, similar to C in the original trait.
120    // For convenience, we are calling these "C types"
121    let generics_env: HashMap<Ident, Option<Ident>> = tybounds
122        .iter_mut()
123        .map(|param| {
124            // First one doesn't work yet https://github.com/rust-lang/rust/issues/114393
125            let maybe_new_param = if has_attr(&param.attrs, "may_borrow")
126                || may_borrow_attrs.contains(&param.ident)
127            {
128                // Remove `?Sized`` bound because we need a param to be Sized in order to take a ZeroFrom of it.
129                // This only applies to fields marked as `may_borrow`.
130                let mut bounds = core::mem::take(&mut param.bounds);
131                while let Some(bound_pair) = bounds.pop() {
132                    let bound = bound_pair.into_value();
133                    if let TypeParamBound::Trait(ref trait_bound) = bound {
134                        if trait_bound.path.get_ident().map(|ident| ident == "Sized") == Some(true)
135                            && #[allow(non_exhaustive_omitted_patterns)] match trait_bound.modifier {
    TraitBoundModifier::Maybe(_) => true,
    _ => false,
}matches!(trait_bound.modifier, TraitBoundModifier::Maybe(_))
136                        {
137                            continue;
138                        }
139                    }
140                    param.bounds.push(bound);
141                }
142                Some(Ident::new(
143                    &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}ZFParamC", param.ident))
    })format!("{}ZFParamC", param.ident),
144                    param.ident.span(),
145                ))
146            } else {
147                None
148            };
149            (param.ident.clone(), maybe_new_param)
150        })
151        .collect();
152
153    // Do any of the generics potentially borrow?
154    let generics_may_borrow = generics_env.values().any(|x| x.is_some());
155
156    if lts == 0 && !generics_may_borrow {
157        let has_clone = structure
158            .variants()
159            .iter()
160            .flat_map(|variant| variant.bindings().iter())
161            .any(|binding| has_attr(&binding.ast().attrs, "clone"));
162        let (clone, clone_trait) = if has_clone {
163            ({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "this");
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "clone");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    _s
}quote!(this.clone()), {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Clone");
    _s
}quote!(Clone))
164        } else {
165            ({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_star(&mut _s);
    ::quote::__private::push_ident(&mut _s, "this");
    _s
}quote!(*this), {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "Copy");
    _s
}quote!(Copy))
166        };
167        let bounds: Vec<WherePredicate> = typarams
168            .iter()
169            .map(|ty| ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&ty, &mut _s);
        ::quote::__private::push_colon(&mut _s);
        ::quote::ToTokens::to_tokens(&clone_trait, &mut _s);
        ::quote::__private::push_add(&mut _s);
        ::quote::__private::push_lifetime(&mut _s, "\'static");
        _s
    })parse_quote!(#ty: #clone_trait + 'static))
170            .collect();
171        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'zf");
    ::quote::__private::push_comma(&mut _s);
    {
        use ::quote::__private::ext::*;
        let mut _first = true;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut tybounds, i) = tybounds.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let tybounds =
                match tybounds.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&tybounds, &mut _s);
        }
    }
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "zerofrom");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ZeroFrom");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'zf");
    ::quote::__private::push_comma(&mut _s);
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_lt(&mut _s);
    {
        use ::quote::__private::ext::*;
        let mut _first = true;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut typarams, i) = typarams.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let typarams =
                match typarams.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&typarams, &mut _s);
        }
    }
    ::quote::__private::push_shr(&mut _s);
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_lt(&mut _s);
    {
        use ::quote::__private::ext::*;
        let mut _first = true;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut typarams, i) = typarams.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let typarams =
                match typarams.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&typarams, &mut _s);
        }
    }
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "where");
    {
        use ::quote::__private::ext::*;
        let mut _first = true;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut bounds, i) = bounds.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let bounds =
                match bounds.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&bounds, &mut _s);
        }
    }
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "zero_from");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "this");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_lifetime(&mut _s, "\'zf");
                    ::quote::__private::push_ident(&mut _s, "Self");
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::ToTokens::to_tokens(&clone, &mut _s);
                    _s
                });
            _s
        });
    _s
}quote! {
172            impl<'zf, #(#tybounds),*> zerofrom::ZeroFrom<'zf, #name<#(#typarams),*>> for #name<#(#typarams),*> where #(#bounds),* {
173                fn zero_from(this: &'zf Self) -> Self {
174                    #clone
175                }
176            }
177        }
178    } else {
179        if lts > 1 {
180            return syn::Error::new(
181                input.generics.span(),
182                "derive(ZeroFrom) cannot have multiple lifetime parameters",
183            )
184            .to_compile_error();
185        }
186
187        let mut zf_bounds: Vec<WherePredicate> = ::alloc::vec::Vec::new()vec![];
188        let body = structure.each_variant(|vi| {
189            vi.construct(|f, i| {
190                let binding = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("__binding_{0}", i))
    })format!("__binding_{i}");
191                let field = Ident::new(&binding, Span::call_site());
192
193                if has_attr(&f.attrs, "clone") {
194                    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&field, &mut _s);
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "clone");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    _s
}quote! {
195                        #field.clone()
196                    }
197                } else {
198                    // the field type
199                    let fty = replace_lifetime(&f.ty, custom_lt("'zf"));
200                    // the corresponding lifetimey type we are borrowing from (effectively, the C type)
201                    let lifetime_ty =
202                        replace_lifetime_and_type(&f.ty, custom_lt("'zf_inner"), &generics_env);
203
204                    let (has_ty, has_lt) = visitor::check_type_for_parameters(&f.ty, &generics_env);
205                    if has_ty {
206                        // For types without type parameters, the compiler can figure out that the field implements
207                        // ZeroFrom on its own. However, if there are type parameters, there may be complex preconditions
208                        // to `FieldTy: ZeroFrom` that need to be satisfied. We get them to be satisfied by requiring
209                        // `FieldTy<'zf>: ZeroFrom<'zf, FieldTy<'zf_inner>>`
210                        if has_lt {
211                            zf_bounds
212                                .push(::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&fty, &mut _s);
        ::quote::__private::push_colon(&mut _s);
        ::quote::__private::push_ident(&mut _s, "zerofrom");
        ::quote::__private::push_colon2(&mut _s);
        ::quote::__private::push_ident(&mut _s, "ZeroFrom");
        ::quote::__private::push_lt(&mut _s);
        ::quote::__private::push_lifetime(&mut _s, "\'zf");
        ::quote::__private::push_comma(&mut _s);
        ::quote::ToTokens::to_tokens(&lifetime_ty, &mut _s);
        ::quote::__private::push_gt(&mut _s);
        _s
    })parse_quote!(#fty: zerofrom::ZeroFrom<'zf, #lifetime_ty>));
213                        } else {
214                            zf_bounds.push(::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&fty, &mut _s);
        ::quote::__private::push_colon(&mut _s);
        ::quote::__private::push_ident(&mut _s, "zerofrom");
        ::quote::__private::push_colon2(&mut _s);
        ::quote::__private::push_ident(&mut _s, "ZeroFrom");
        ::quote::__private::push_lt(&mut _s);
        ::quote::__private::push_lifetime(&mut _s, "\'zf");
        ::quote::__private::push_comma(&mut _s);
        ::quote::ToTokens::to_tokens(&fty, &mut _s);
        ::quote::__private::push_gt(&mut _s);
        _s
    })parse_quote!(#fty: zerofrom::ZeroFrom<'zf, #fty>));
215                        }
216                    }
217                    if has_ty || has_lt {
218                        // By doing this we essentially require ZF to be implemented
219                        // on all fields
220                        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&fty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_ident(&mut _s, "zerofrom");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ZeroFrom");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'zf");
    ::quote::__private::push_comma(&mut _s);
    ::quote::ToTokens::to_tokens(&lifetime_ty, &mut _s);
    ::quote::__private::push_shr(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "zero_from");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&field, &mut _s);
            _s
        });
    _s
}quote! {
221                            <#fty as zerofrom::ZeroFrom<'zf, #lifetime_ty>>::zero_from(#field)
222                        }
223                    } else {
224                        // No lifetimes, so we can just copy
225                        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_star(&mut _s);
    ::quote::ToTokens::to_tokens(&field, &mut _s);
    _s
}quote! { *#field }
226                    }
227                }
228            })
229        });
230        // Due to the possibility of generics_may_borrow, we might reach here with no lifetimes on self,
231        // don't accidentally feed them to self later
232        let (maybe_zf_lifetime, maybe_zf_inner_lifetime) = if lts == 0 {
233            (::quote::__private::TokenStream::new()quote!(), ::quote::__private::TokenStream::new()quote!())
234        } else {
235            ({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lifetime(&mut _s, "\'zf");
    ::quote::__private::push_comma(&mut _s);
    _s
}quote!('zf,), {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lifetime(&mut _s, "\'zf_inner");
    ::quote::__private::push_comma(&mut _s);
    _s
}quote!('zf_inner,))
236        };
237
238        // Array of C types. Only different if generics are allowed to borrow
239        let mut typarams_c = typarams.clone();
240
241        if generics_may_borrow {
242            for typaram_c in &mut typarams_c {
243                if let Some(Some(replacement)) = generics_env.get(typaram_c) {
244                    // we use mem::replace here so we can be really clear about the C vs the T type
245                    let typaram_t = core::mem::replace(typaram_c, replacement.clone());
246                    zf_bounds
247                        .push(::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&typaram_c, &mut _s);
        ::quote::__private::push_colon(&mut _s);
        ::quote::__private::push_ident(&mut _s, "zerofrom");
        ::quote::__private::push_colon2(&mut _s);
        ::quote::__private::push_ident(&mut _s, "ZeroFrom");
        ::quote::__private::push_lt(&mut _s);
        ::quote::__private::push_lifetime(&mut _s, "\'zf_inner");
        ::quote::__private::push_comma(&mut _s);
        ::quote::ToTokens::to_tokens(&typaram_t, &mut _s);
        ::quote::__private::push_gt(&mut _s);
        _s
    })parse_quote!(#typaram_c: zerofrom::ZeroFrom<'zf_inner, #typaram_t>));
248                    tybounds.push(::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&typaram_c, &mut _s);
        _s
    })parse_quote!(#typaram_c));
249                }
250            }
251        }
252
253        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'zf");
    ::quote::__private::push_comma(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'zf_inner");
    ::quote::__private::push_comma(&mut _s);
    {
        use ::quote::__private::ext::*;
        let mut _first = true;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut tybounds, i) = tybounds.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let tybounds =
                match tybounds.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&tybounds, &mut _s);
        }
    }
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "zerofrom");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ZeroFrom");
    ::quote::__private::push_lt(&mut _s);
    ::quote::__private::push_lifetime(&mut _s, "\'zf");
    ::quote::__private::push_comma(&mut _s);
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&maybe_zf_inner_lifetime, &mut _s);
    {
        use ::quote::__private::ext::*;
        let mut _first = true;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut typarams_c, i) = typarams_c.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let typarams_c =
                match typarams_c.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&typarams_c, &mut _s);
        }
    }
    ::quote::__private::push_shr(&mut _s);
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&maybe_zf_lifetime, &mut _s);
    {
        use ::quote::__private::ext::*;
        let mut _first = true;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut typarams, i) = typarams.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let typarams =
                match typarams.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            if !_first { ::quote::__private::push_comma(&mut _s); }
            _first = false;
            ::quote::ToTokens::to_tokens(&typarams, &mut _s);
        }
    }
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_ident(&mut _s, "where");
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut zf_bounds, i) = zf_bounds.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let zf_bounds =
                match zf_bounds.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::ToTokens::to_tokens(&zf_bounds, &mut _s);
            ::quote::__private::push_comma(&mut _s);
        }
    }
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "zero_from");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "this");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_lifetime(&mut _s, "\'zf");
                    ::quote::ToTokens::to_tokens(&name, &mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::ToTokens::to_tokens(&maybe_zf_inner_lifetime,
                        &mut _s);
                    {
                        use ::quote::__private::ext::*;
                        let mut _first = true;
                        let has_iter = ::quote::__private::HasIterator::<false>;
                        #[allow(unused_mut)]
                        let (mut typarams_c, i) = typarams_c.quote_into_iter();
                        let has_iter = has_iter | i;
                        <_ as
                                ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                        while true {
                            let typarams_c =
                                match typarams_c.next() {
                                    Some(_x) => ::quote::__private::RepInterp(_x),
                                    None => break,
                                };
                            if !_first { ::quote::__private::push_comma(&mut _s); }
                            _first = false;
                            ::quote::ToTokens::to_tokens(&typarams_c, &mut _s);
                        }
                    }
                    ::quote::__private::push_gt(&mut _s);
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "match");
                    ::quote::__private::push_star(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "this");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&body, &mut _s);
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
254            impl<'zf, 'zf_inner, #(#tybounds),*> zerofrom::ZeroFrom<'zf, #name<#maybe_zf_inner_lifetime #(#typarams_c),*>> for #name<#maybe_zf_lifetime #(#typarams),*>
255                where
256                #(#zf_bounds,)* {
257                fn zero_from(this: &'zf #name<#maybe_zf_inner_lifetime #(#typarams_c),*>) -> Self {
258                    match *this { #body }
259                }
260            }
261        }
262    }
263}
264
265fn custom_lt(s: &str) -> Lifetime {
266    Lifetime::new(s, Span::call_site())
267}
268
269/// Replace all lifetimes in a type with a specified one
270fn replace_lifetime(x: &Type, lt: Lifetime) -> Type {
271    struct ReplaceLifetime(Lifetime);
272
273    impl Fold for ReplaceLifetime {
274        fn fold_lifetime(&mut self, _: Lifetime) -> Lifetime {
275            self.0.clone()
276        }
277    }
278    ReplaceLifetime(lt).fold_type(x.clone())
279}
280
281/// Replace all lifetimes in a type with a specified one, AND replace all types that have a corresponding C type
282/// with the C type
283fn replace_lifetime_and_type(
284    x: &Type,
285    lt: Lifetime,
286    generics_env: &HashMap<Ident, Option<Ident>>,
287) -> Type {
288    struct ReplaceLifetimeAndTy<'a>(Lifetime, &'a HashMap<Ident, Option<Ident>>);
289
290    impl Fold for ReplaceLifetimeAndTy<'_> {
291        fn fold_lifetime(&mut self, _: Lifetime) -> Lifetime {
292            self.0.clone()
293        }
294        fn fold_type_path(&mut self, i: TypePath) -> TypePath {
295            if i.qself.is_none() {
296                if let Some(ident) = i.path.get_ident() {
297                    if let Some(Some(replacement)) = self.1.get(ident) {
298                        return ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::ToTokens::to_tokens(&replacement, &mut _s);
        _s
    })parse_quote!(#replacement);
299                    }
300                }
301            }
302            fold::fold_type_path(self, i)
303        }
304    }
305    ReplaceLifetimeAndTy(lt, generics_env).fold_type(x.clone())
306}