cxxbridge_macro/
derive.rs

1use crate::syntax::{derive, Enum, Struct};
2use proc_macro2::{Ident, Span, TokenStream};
3use quote::{quote, quote_spanned, ToTokens};
4
5pub(crate) use crate::syntax::derive::*;
6
7pub(crate) fn expand_struct(
8    strct: &Struct,
9    actual_derives: &mut Option<TokenStream>,
10) -> TokenStream {
11    let mut expanded = TokenStream::new();
12    let mut traits = Vec::new();
13
14    for derive in &strct.derives {
15        let span = derive.span;
16        match derive.what {
17            Trait::BitAnd => unreachable!(),
18            Trait::BitOr => unreachable!(),
19            Trait::BitXor => unreachable!(),
20            Trait::Copy => expanded.extend(struct_copy(strct, span)),
21            Trait::Clone => expanded.extend(struct_clone(strct, span)),
22            Trait::Debug => expanded.extend(struct_debug(strct, span)),
23            Trait::Default => expanded.extend(struct_default(strct, span)),
24            Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
25            Trait::ExternType => unreachable!(),
26            Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
27            Trait::Ord => expanded.extend(struct_ord(strct, span)),
28            Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
29            Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
30            Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
31            Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
32        }
33    }
34
35    if traits.is_empty() {
36        *actual_derives = None;
37    } else {
38        *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
39    }
40
41    expanded
42}
43
44pub(crate) fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
45    let mut expanded = TokenStream::new();
46    let mut traits = Vec::new();
47    let mut has_copy = false;
48    let mut has_clone = false;
49    let mut has_eq = false;
50    let mut has_partial_eq = false;
51
52    for derive in &enm.derives {
53        let span = derive.span;
54        match derive.what {
55            Trait::BitAnd => expanded.extend(enum_bitand(enm, span)),
56            Trait::BitOr => expanded.extend(enum_bitor(enm, span)),
57            Trait::BitXor => expanded.extend(enum_bitxor(enm, span)),
58            Trait::Copy => {
59                expanded.extend(enum_copy(enm, span));
60                has_copy = true;
61            }
62            Trait::Clone => {
63                expanded.extend(enum_clone(enm, span));
64                has_clone = true;
65            }
66            Trait::Debug => expanded.extend(enum_debug(enm, span)),
67            Trait::Default => expanded.extend(enum_default(enm, span)),
68            Trait::Eq => {
69                traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
70                has_eq = true;
71            }
72            Trait::ExternType => unreachable!(),
73            Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
74            Trait::Ord => expanded.extend(enum_ord(enm, span)),
75            Trait::PartialEq => {
76                traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
77                has_partial_eq = true;
78            }
79            Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
80            Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
81            Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
82        }
83    }
84
85    let span = enm.name.rust.span();
86    if !has_copy {
87        expanded.extend(enum_copy(enm, span));
88    }
89    if !has_clone {
90        expanded.extend(enum_clone(enm, span));
91    }
92    if !has_eq {
93        // Required to be derived in order for the enum's "variants" to be
94        // usable in patterns.
95        traits.push(quote!(::cxx::core::cmp::Eq));
96    }
97    if !has_partial_eq {
98        traits.push(quote!(::cxx::core::cmp::PartialEq));
99    }
100
101    *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
102
103    expanded
104}
105
106fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
107    let ident = &strct.name.rust;
108    let generics = &strct.generics;
109    let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
110
111    quote_spanned! {span=>
112        #cfg_and_lint_attrs
113        #[automatically_derived]
114        impl #generics ::cxx::core::marker::Copy for #ident #generics {}
115    }
116}
117
118fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
119    let ident = &strct.name.rust;
120    let generics = &strct.generics;
121    let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
122
123    let body = if derive::contains(&strct.derives, Trait::Copy) {
124        quote!(*self)
125    } else {
126        let fields = strct.fields.iter().map(|field| &field.name.rust);
127        let values = strct.fields.iter().map(|field| {
128            let ident = &field.name.rust;
129            let ty = field.ty.to_token_stream();
130            let span = ty.into_iter().last().unwrap().span();
131            quote_spanned!(span=> &self.#ident)
132        });
133        quote_spanned!(span=> #ident {
134            #(#fields: ::cxx::core::clone::Clone::clone(#values),)*
135        })
136    };
137
138    quote_spanned! {span=>
139        #cfg_and_lint_attrs
140        #[automatically_derived]
141        #[allow(clippy::expl_impl_clone_on_copy)]
142        impl #generics ::cxx::core::clone::Clone for #ident #generics {
143            fn clone(&self) -> Self {
144                #body
145            }
146        }
147    }
148}
149
150fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
151    let ident = &strct.name.rust;
152    let generics = &strct.generics;
153    let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
154    let struct_name = ident.to_string();
155    let fields = strct.fields.iter().map(|field| &field.name.rust);
156    let field_names = fields.clone().map(Ident::to_string);
157
158    quote_spanned! {span=>
159        #cfg_and_lint_attrs
160        #[automatically_derived]
161        impl #generics ::cxx::core::fmt::Debug for #ident #generics {
162            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
163                formatter.debug_struct(#struct_name)
164                    #(.field(#field_names, &self.#fields))*
165                    .finish()
166            }
167        }
168    }
169}
170
171fn struct_default(strct: &Struct, span: Span) -> TokenStream {
172    let ident = &strct.name.rust;
173    let generics = &strct.generics;
174    let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
175    let fields = strct.fields.iter().map(|field| &field.name.rust);
176
177    quote_spanned! {span=>
178        #cfg_and_lint_attrs
179        #[automatically_derived]
180        #[allow(clippy::derivable_impls)] // different spans than the derived impl
181        impl #generics ::cxx::core::default::Default for #ident #generics {
182            fn default() -> Self {
183                #ident {
184                    #(
185                        #fields: ::cxx::core::default::Default::default(),
186                    )*
187                }
188            }
189        }
190    }
191}
192
193fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
194    let ident = &strct.name.rust;
195    let generics = &strct.generics;
196    let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
197    let fields = strct.fields.iter().map(|field| &field.name.rust);
198
199    quote_spanned! {span=>
200        #cfg_and_lint_attrs
201        #[automatically_derived]
202        impl #generics ::cxx::core::cmp::Ord for #ident #generics {
203            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
204                #(
205                    match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
206                        ::cxx::core::cmp::Ordering::Equal => {}
207                        ordering => return ordering,
208                    }
209                )*
210                ::cxx::core::cmp::Ordering::Equal
211            }
212        }
213    }
214}
215
216fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
217    let ident = &strct.name.rust;
218    let generics = &strct.generics;
219    let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
220
221    let body = if derive::contains(&strct.derives, Trait::Ord) {
222        quote! {
223            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
224        }
225    } else {
226        let fields = strct.fields.iter().map(|field| &field.name.rust);
227        quote! {
228            #(
229                match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
230                    ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
231                    ordering => return ordering,
232                }
233            )*
234            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
235        }
236    };
237
238    quote_spanned! {span=>
239        #cfg_and_lint_attrs
240        #[automatically_derived]
241        impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
242            #[allow(clippy::non_canonical_partial_ord_impl)]
243            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
244                #body
245            }
246        }
247    }
248}
249
250fn enum_bitand(enm: &Enum, span: Span) -> TokenStream {
251    let ident = &enm.name.rust;
252    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
253
254    quote_spanned! {span=>
255        #cfg_and_lint_attrs
256        #[automatically_derived]
257        impl ::cxx::core::ops::BitAnd for #ident {
258            type Output = #ident;
259            fn bitand(self, rhs: Self) -> Self::Output {
260                #ident {
261                    repr: self.repr & rhs.repr,
262                }
263            }
264        }
265    }
266}
267
268fn enum_bitor(enm: &Enum, span: Span) -> TokenStream {
269    let ident = &enm.name.rust;
270    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
271
272    quote_spanned! {span=>
273        #cfg_and_lint_attrs
274        #[automatically_derived]
275        impl ::cxx::core::ops::BitOr for #ident {
276            type Output = #ident;
277            fn bitor(self, rhs: Self) -> Self::Output {
278                #ident {
279                    repr: self.repr | rhs.repr,
280                }
281            }
282        }
283    }
284}
285
286fn enum_bitxor(enm: &Enum, span: Span) -> TokenStream {
287    let ident = &enm.name.rust;
288    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
289
290    quote_spanned! {span=>
291        #cfg_and_lint_attrs
292        #[automatically_derived]
293        impl ::cxx::core::ops::BitXor for #ident {
294            type Output = #ident;
295            fn bitxor(self, rhs: Self) -> Self::Output {
296                #ident {
297                    repr: self.repr ^ rhs.repr,
298                }
299            }
300        }
301    }
302}
303
304fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
305    let ident = &enm.name.rust;
306    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
307
308    quote_spanned! {span=>
309        #cfg_and_lint_attrs
310        #[automatically_derived]
311        impl ::cxx::core::marker::Copy for #ident {}
312    }
313}
314
315fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
316    let ident = &enm.name.rust;
317    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
318
319    quote_spanned! {span=>
320        #cfg_and_lint_attrs
321        #[automatically_derived]
322        #[allow(clippy::expl_impl_clone_on_copy)]
323        impl ::cxx::core::clone::Clone for #ident {
324            fn clone(&self) -> Self {
325                *self
326            }
327        }
328    }
329}
330
331fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
332    let ident = &enm.name.rust;
333    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
334    let variants = enm.variants.iter().map(|variant| {
335        let variant = &variant.name.rust;
336        let name = variant.to_string();
337        quote_spanned! {span=>
338            #ident::#variant => formatter.write_str(#name),
339        }
340    });
341    let fallback = format!("{}({{}})", ident);
342
343    quote_spanned! {span=>
344        #cfg_and_lint_attrs
345        #[automatically_derived]
346        impl ::cxx::core::fmt::Debug for #ident {
347            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
348                match *self {
349                    #(#variants)*
350                    _ => ::cxx::core::write!(formatter, #fallback, self.repr),
351                }
352            }
353        }
354    }
355}
356
357fn enum_default(enm: &Enum, span: Span) -> TokenStream {
358    let ident = &enm.name.rust;
359    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
360
361    for variant in &enm.variants {
362        if variant.default {
363            let variant = &variant.name.rust;
364            return quote_spanned! {span=>
365                #cfg_and_lint_attrs
366                #[automatically_derived]
367                impl ::cxx::core::default::Default for #ident {
368                    fn default() -> Self {
369                        #ident::#variant
370                    }
371                }
372            };
373        }
374    }
375
376    unreachable!("no #[default] variant");
377}
378
379fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
380    let ident = &enm.name.rust;
381    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
382
383    quote_spanned! {span=>
384        #cfg_and_lint_attrs
385        #[automatically_derived]
386        impl ::cxx::core::cmp::Ord for #ident {
387            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
388                ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
389            }
390        }
391    }
392}
393
394fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
395    let ident = &enm.name.rust;
396    let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
397
398    quote_spanned! {span=>
399        #cfg_and_lint_attrs
400        #[automatically_derived]
401        impl ::cxx::core::cmp::PartialOrd for #ident {
402            #[allow(clippy::non_canonical_partial_ord_impl)]
403            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
404                ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
405            }
406        }
407    }
408}