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