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 ).
5 //! Custom derives for `ZeroFrom` from the `zerofrom` crate.
7 // https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
11 clippy
::indexing_slicing
,
15 clippy
::exhaustive_structs
,
16 clippy
::exhaustive_enums
,
17 missing_debug_implementations
,
21 use proc_macro
::TokenStream
;
22 use proc_macro2
::{Span, TokenStream as TokenStream2}
;
24 use syn
::spanned
::Spanned
;
25 use syn
::{parse_macro_input, parse_quote, DeriveInput, Ident, Lifetime, Type, WherePredicate}
;
26 use synstructure
::Structure
;
30 /// Custom derive for `zerofrom::ZeroFrom`,
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.
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
))
44 fn has_clone_attr(attrs
: &[syn
::Attribute
]) -> bool
{
45 attrs
.iter().any(|a
| {
46 if let Ok(i
) = a
.parse_args
::<Ident
>() {
55 fn zf_derive_impl(input
: &DeriveInput
) -> TokenStream2
{
60 // Strip out param defaults, we don't need them in the impl
61 let mut ty
= ty
.clone();
67 let typarams
= tybounds
69 .map(|ty
| ty
.ident
.clone())
71 let lts
= input
.generics
.lifetimes().count();
72 let name
= &input
.ident
;
73 let structure
= Structure
::new(input
);
76 let has_clone
= structure
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
))
84 (quote
!(*this
), quote
!(Copy
))
86 let bounds
: Vec
<WherePredicate
> = typarams
88 .map(|ty
| parse_quote
!(#ty: #clone_trait + 'static))
91 impl<'zf
, #(#tybounds),*> zerofrom::ZeroFrom<'zf, #name<#(#typarams),*>> for #name<#(#typarams),*> where #(#bounds),* {
92 fn zero_from(this
: &'zf
Self) -> Self {
99 return syn
::Error
::new(
100 input
.generics
.span(),
101 "derive(ZeroFrom) cannot have multiple lifetime parameters",
106 let generics_env
= typarams
.iter().cloned().collect();
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());
114 if has_clone_attr(&f
.attrs
) {
119 let fty
= replace_lifetime(&f
.ty
, custom_lt("'zf"));
120 let lifetime_ty
= replace_lifetime(&f
.ty
, custom_lt("'zf_inner"));
122 let (has_ty
, has_lt
) = visitor
::check_type_for_parameters(&f
.ty
, &generics_env
);
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>>`
130 .push(parse_quote
!(#fty: zerofrom::ZeroFrom<'zf, #lifetime_ty>));
132 zf_bounds
.push(parse_quote
!(#fty: zerofrom::ZeroFrom<'zf, #fty>));
135 if has_ty
|| has_lt
{
136 // By doing this we essentially require ZF to be implemented
139 <#fty as zerofrom::ZeroFrom<'zf, #lifetime_ty>>::zero_from(#field)
142 // No lifetimes, so we can just copy
150 impl<'zf
, 'zf_inner
, #(#tybounds),*> zerofrom::ZeroFrom<'zf, #name<'zf_inner, #(#typarams),*>> for #name<'zf, #(#typarams),*>
153 fn zero_from(this
: &'zf
#name<'zf_inner, #(#typarams),*>) -> Self {
154 match *this { #body }
161 fn custom_lt(s
: &str) -> Lifetime
{
162 Lifetime
::new(s
, Span
::call_site())
165 fn replace_lifetime(x
: &Type
, lt
: Lifetime
) -> Type
{
167 struct ReplaceLifetime(Lifetime
);
169 impl Fold
for ReplaceLifetime
{
170 fn fold_lifetime(&mut self, _
: Lifetime
) -> Lifetime
{
174 ReplaceLifetime(lt
).fold_type(x
.clone())