1 use crate::syntax::{derive, Enum, Struct};
2 use proc_macro2::{Ident, Span, TokenStream};
3 use quote::{quote, quote_spanned, ToTokens};
4 
5 pub(crate) use crate::syntax::derive::*;
6 
expand_struct( strct: &Struct, actual_derives: &mut Option<TokenStream>, ) -> TokenStream7 pub(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::Copy => expanded.extend(struct_copy(strct, span)),
18             Trait::Clone => expanded.extend(struct_clone(strct, span)),
19             Trait::Debug => expanded.extend(struct_debug(strct, span)),
20             Trait::Default => expanded.extend(struct_default(strct, span)),
21             Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
22             Trait::ExternType => unreachable!(),
23             Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
24             Trait::Ord => expanded.extend(struct_ord(strct, span)),
25             Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
26             Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
27             Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
28             Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
29         }
30     }
31 
32     if traits.is_empty() {
33         *actual_derives = None;
34     } else {
35         *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
36     }
37 
38     expanded
39 }
40 
expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream41 pub(crate) fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
42     let mut expanded = TokenStream::new();
43     let mut traits = Vec::new();
44     let mut has_copy = false;
45     let mut has_clone = false;
46     let mut has_eq = false;
47     let mut has_partial_eq = false;
48 
49     for derive in &enm.derives {
50         let span = derive.span;
51         match derive.what {
52             Trait::Copy => {
53                 expanded.extend(enum_copy(enm, span));
54                 has_copy = true;
55             }
56             Trait::Clone => {
57                 expanded.extend(enum_clone(enm, span));
58                 has_clone = true;
59             }
60             Trait::Debug => expanded.extend(enum_debug(enm, span)),
61             Trait::Default => unreachable!(),
62             Trait::Eq => {
63                 traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
64                 has_eq = true;
65             }
66             Trait::ExternType => unreachable!(),
67             Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
68             Trait::Ord => expanded.extend(enum_ord(enm, span)),
69             Trait::PartialEq => {
70                 traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
71                 has_partial_eq = true;
72             }
73             Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
74             Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
75             Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
76         }
77     }
78 
79     let span = enm.name.rust.span();
80     if !has_copy {
81         expanded.extend(enum_copy(enm, span));
82     }
83     if !has_clone {
84         expanded.extend(enum_clone(enm, span));
85     }
86     if !has_eq {
87         // Required to be derived in order for the enum's "variants" to be
88         // usable in patterns.
89         traits.push(quote!(::cxx::core::cmp::Eq));
90     }
91     if !has_partial_eq {
92         traits.push(quote!(::cxx::core::cmp::PartialEq));
93     }
94 
95     *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
96 
97     expanded
98 }
99 
struct_copy(strct: &Struct, span: Span) -> TokenStream100 fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
101     let ident = &strct.name.rust;
102     let generics = &strct.generics;
103 
104     quote_spanned! {span=>
105         impl #generics ::cxx::core::marker::Copy for #ident #generics {}
106     }
107 }
108 
struct_clone(strct: &Struct, span: Span) -> TokenStream109 fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
110     let ident = &strct.name.rust;
111     let generics = &strct.generics;
112 
113     let body = if derive::contains(&strct.derives, Trait::Copy) {
114         quote!(*self)
115     } else {
116         let fields = strct.fields.iter().map(|field| &field.name.rust);
117         let values = strct.fields.iter().map(|field| {
118             let ident = &field.name.rust;
119             let ty = field.ty.to_token_stream();
120             let span = ty.into_iter().last().unwrap().span();
121             quote_spanned!(span=> &self.#ident)
122         });
123         quote_spanned!(span=> #ident {
124             #(#fields: ::cxx::core::clone::Clone::clone(#values),)*
125         })
126     };
127 
128     quote_spanned! {span=>
129         #[allow(clippy::expl_impl_clone_on_copy)]
130         impl #generics ::cxx::core::clone::Clone for #ident #generics {
131             fn clone(&self) -> Self {
132                 #body
133             }
134         }
135     }
136 }
137 
struct_debug(strct: &Struct, span: Span) -> TokenStream138 fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
139     let ident = &strct.name.rust;
140     let generics = &strct.generics;
141     let struct_name = ident.to_string();
142     let fields = strct.fields.iter().map(|field| &field.name.rust);
143     let field_names = fields.clone().map(Ident::to_string);
144 
145     quote_spanned! {span=>
146         impl #generics ::cxx::core::fmt::Debug for #ident #generics {
147             fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
148                 formatter.debug_struct(#struct_name)
149                     #(.field(#field_names, &self.#fields))*
150                     .finish()
151             }
152         }
153     }
154 }
155 
struct_default(strct: &Struct, span: Span) -> TokenStream156 fn struct_default(strct: &Struct, span: Span) -> TokenStream {
157     let ident = &strct.name.rust;
158     let generics = &strct.generics;
159     let fields = strct.fields.iter().map(|field| &field.name.rust);
160 
161     quote_spanned! {span=>
162         #[allow(clippy::derivable_impls)] // different spans than the derived impl
163         impl #generics ::cxx::core::default::Default for #ident #generics {
164             fn default() -> Self {
165                 #ident {
166                     #(
167                         #fields: ::cxx::core::default::Default::default(),
168                     )*
169                 }
170             }
171         }
172     }
173 }
174 
struct_ord(strct: &Struct, span: Span) -> TokenStream175 fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
176     let ident = &strct.name.rust;
177     let generics = &strct.generics;
178     let fields = strct.fields.iter().map(|field| &field.name.rust);
179 
180     quote_spanned! {span=>
181         impl #generics ::cxx::core::cmp::Ord for #ident #generics {
182             fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
183                 #(
184                     match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
185                         ::cxx::core::cmp::Ordering::Equal => {}
186                         ordering => return ordering,
187                     }
188                 )*
189                 ::cxx::core::cmp::Ordering::Equal
190             }
191         }
192     }
193 }
194 
struct_partial_ord(strct: &Struct, span: Span) -> TokenStream195 fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
196     let ident = &strct.name.rust;
197     let generics = &strct.generics;
198 
199     let body = if derive::contains(&strct.derives, Trait::Ord) {
200         quote! {
201             ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
202         }
203     } else {
204         let fields = strct.fields.iter().map(|field| &field.name.rust);
205         quote! {
206             #(
207                 match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
208                     ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
209                     ordering => return ordering,
210                 }
211             )*
212             ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
213         }
214     };
215 
216     quote_spanned! {span=>
217         impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
218             #[allow(clippy::non_canonical_partial_ord_impl)]
219             #[allow(renamed_and_removed_lints, clippy::incorrect_partial_ord_impl_on_ord_type)] // Rust 1.73 and older
220             fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
221                 #body
222             }
223         }
224     }
225 }
226 
enum_copy(enm: &Enum, span: Span) -> TokenStream227 fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
228     let ident = &enm.name.rust;
229 
230     quote_spanned! {span=>
231         impl ::cxx::core::marker::Copy for #ident {}
232     }
233 }
234 
enum_clone(enm: &Enum, span: Span) -> TokenStream235 fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
236     let ident = &enm.name.rust;
237 
238     quote_spanned! {span=>
239         #[allow(clippy::expl_impl_clone_on_copy)]
240         impl ::cxx::core::clone::Clone for #ident {
241             fn clone(&self) -> Self {
242                 *self
243             }
244         }
245     }
246 }
247 
enum_debug(enm: &Enum, span: Span) -> TokenStream248 fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
249     let ident = &enm.name.rust;
250     let variants = enm.variants.iter().map(|variant| {
251         let variant = &variant.name.rust;
252         let name = variant.to_string();
253         quote_spanned! {span=>
254             #ident::#variant => formatter.write_str(#name),
255         }
256     });
257     let fallback = format!("{}({{}})", ident);
258 
259     quote_spanned! {span=>
260         impl ::cxx::core::fmt::Debug for #ident {
261             fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
262                 match *self {
263                     #(#variants)*
264                     _ => ::cxx::core::write!(formatter, #fallback, self.repr),
265                 }
266             }
267         }
268     }
269 }
270 
enum_ord(enm: &Enum, span: Span) -> TokenStream271 fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
272     let ident = &enm.name.rust;
273 
274     quote_spanned! {span=>
275         impl ::cxx::core::cmp::Ord for #ident {
276             fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
277                 ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
278             }
279         }
280     }
281 }
282 
enum_partial_ord(enm: &Enum, span: Span) -> TokenStream283 fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
284     let ident = &enm.name.rust;
285 
286     quote_spanned! {span=>
287         impl ::cxx::core::cmp::PartialOrd for #ident {
288             #[allow(clippy::non_canonical_partial_ord_impl)]
289             #[allow(renamed_and_removed_lints, clippy::incorrect_partial_ord_impl_on_ord_type)] // Rust 1.73 and older
290             fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
291                 ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
292             }
293         }
294     }
295 }
296