]> git.proxmox.com Git - rustc.git/blob - vendor/zerofrom-derive/src/lib.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / vendor / zerofrom-derive / src / 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 //! Custom derives for `ZeroFrom` from the `zerofrom` crate.
6
7 // https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
8 #![cfg_attr(
9 not(test),
10 deny(
11 clippy::indexing_slicing,
12 clippy::unwrap_used,
13 clippy::expect_used,
14 clippy::panic,
15 clippy::exhaustive_structs,
16 clippy::exhaustive_enums,
17 missing_debug_implementations,
18 )
19 )]
20
21 use proc_macro::TokenStream;
22 use proc_macro2::{Span, TokenStream as TokenStream2};
23 use quote::quote;
24 use syn::spanned::Spanned;
25 use syn::{parse_macro_input, parse_quote, DeriveInput, Ident, Lifetime, Type, WherePredicate};
26 use synstructure::Structure;
27
28 mod visitor;
29
30 /// Custom derive for `zerofrom::ZeroFrom`,
31 ///
32 /// This implements `ZeroFrom<Ty> for Ty` for types
33 /// without a lifetime parameter, and `ZeroFrom<Ty<'data>> for Ty<'static>`
34 /// for types with a lifetime parameter.
35 ///
36 /// Apply the `#[zerofrom(clone)]` attribute to a field if it doesn't implement
37 /// Copy or ZeroFrom; this data will be cloned when the struct is zero_from'ed.
38 #[proc_macro_derive(ZeroFrom, attributes(zerofrom))]
39 pub fn zf_derive(input: TokenStream) -> TokenStream {
40 let input = parse_macro_input!(input as DeriveInput);
41 TokenStream::from(zf_derive_impl(&input))
42 }
43
44 fn has_clone_attr(attrs: &[syn::Attribute]) -> bool {
45 attrs.iter().any(|a| {
46 if let Ok(i) = a.parse_args::<Ident>() {
47 if i == "clone" {
48 return true;
49 }
50 }
51 false
52 })
53 }
54
55 fn zf_derive_impl(input: &DeriveInput) -> TokenStream2 {
56 let tybounds = input
57 .generics
58 .type_params()
59 .map(|ty| {
60 // Strip out param defaults, we don't need them in the impl
61 let mut ty = ty.clone();
62 ty.eq_token = None;
63 ty.default = None;
64 ty
65 })
66 .collect::<Vec<_>>();
67 let typarams = tybounds
68 .iter()
69 .map(|ty| ty.ident.clone())
70 .collect::<Vec<_>>();
71 let lts = input.generics.lifetimes().count();
72 let name = &input.ident;
73 let structure = Structure::new(input);
74
75 if lts == 0 {
76 let has_clone = structure
77 .variants()
78 .iter()
79 .flat_map(|variant| variant.bindings().iter())
80 .any(|binding| has_clone_attr(&binding.ast().attrs));
81 let (clone, clone_trait) = if has_clone {
82 (quote!(this.clone()), quote!(Clone))
83 } else {
84 (quote!(*this), quote!(Copy))
85 };
86 let bounds: Vec<WherePredicate> = typarams
87 .iter()
88 .map(|ty| parse_quote!(#ty: #clone_trait + 'static))
89 .collect();
90 quote! {
91 impl<'zf, #(#tybounds),*> zerofrom::ZeroFrom<'zf, #name<#(#typarams),*>> for #name<#(#typarams),*> where #(#bounds),* {
92 fn zero_from(this: &'zf Self) -> Self {
93 #clone
94 }
95 }
96 }
97 } else {
98 if lts != 1 {
99 return syn::Error::new(
100 input.generics.span(),
101 "derive(ZeroFrom) cannot have multiple lifetime parameters",
102 )
103 .to_compile_error();
104 }
105
106 let generics_env = typarams.iter().cloned().collect();
107
108 let mut zf_bounds: Vec<WherePredicate> = vec![];
109 let body = structure.each_variant(|vi| {
110 vi.construct(|f, i| {
111 let binding = format!("__binding_{i}");
112 let field = Ident::new(&binding, Span::call_site());
113
114 if has_clone_attr(&f.attrs) {
115 quote! {
116 #field.clone()
117 }
118 } else {
119 let fty = replace_lifetime(&f.ty, custom_lt("'zf"));
120 let lifetime_ty = replace_lifetime(&f.ty, custom_lt("'zf_inner"));
121
122 let (has_ty, has_lt) = visitor::check_type_for_parameters(&f.ty, &generics_env);
123 if has_ty {
124 // For types without type parameters, the compiler can figure out that the field implements
125 // ZeroFrom on its own. However, if there are type parameters, there may be complex preconditions
126 // to `FieldTy: ZeroFrom` that need to be satisfied. We get them to be satisfied by requiring
127 // `FieldTy<'zf>: ZeroFrom<'zf, FieldTy<'zf_inner>>`
128 if has_lt {
129 zf_bounds
130 .push(parse_quote!(#fty: zerofrom::ZeroFrom<'zf, #lifetime_ty>));
131 } else {
132 zf_bounds.push(parse_quote!(#fty: zerofrom::ZeroFrom<'zf, #fty>));
133 }
134 }
135 if has_ty || has_lt {
136 // By doing this we essentially require ZF to be implemented
137 // on all fields
138 quote! {
139 <#fty as zerofrom::ZeroFrom<'zf, #lifetime_ty>>::zero_from(#field)
140 }
141 } else {
142 // No lifetimes, so we can just copy
143 quote! { *#field }
144 }
145 }
146 })
147 });
148
149 quote! {
150 impl<'zf, 'zf_inner, #(#tybounds),*> zerofrom::ZeroFrom<'zf, #name<'zf_inner, #(#typarams),*>> for #name<'zf, #(#typarams),*>
151 where
152 #(#zf_bounds,)* {
153 fn zero_from(this: &'zf #name<'zf_inner, #(#typarams),*>) -> Self {
154 match *this { #body }
155 }
156 }
157 }
158 }
159 }
160
161 fn custom_lt(s: &str) -> Lifetime {
162 Lifetime::new(s, Span::call_site())
163 }
164
165 fn replace_lifetime(x: &Type, lt: Lifetime) -> Type {
166 use syn::fold::Fold;
167 struct ReplaceLifetime(Lifetime);
168
169 impl Fold for ReplaceLifetime {
170 fn fold_lifetime(&mut self, _: Lifetime) -> Lifetime {
171 self.0.clone()
172 }
173 }
174 ReplaceLifetime(lt).fold_type(x.clone())
175 }