blob: 3e95ea497c9d6c0fb2082907c87ed794c5e336c6 [file] [log] [blame]
David Tolnaybc047bb2020-11-27 14:30:12 -08001use crate::syntax::{derive, Enum, Struct, Trait};
David Tolnayf84c98b2020-11-27 12:59:42 -08002use proc_macro2::{Ident, Span, TokenStream};
David Tolnayd8ad9702020-11-27 12:43:59 -08003use quote::{quote, quote_spanned, ToTokens};
4
David Tolnayb960ed22020-11-27 14:34:30 -08005pub use crate::syntax::derive::*;
6
David Tolnaya3619e92020-11-27 14:13:15 -08007pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayd8ad9702020-11-27 12:43:59 -08008 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -08009 let mut traits = Vec::new();
David Tolnayd8ad9702020-11-27 12:43:59 -080010
David Tolnay2c24e5c2020-11-27 12:47:41 -080011 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)),
David Tolnayf84c98b2020-11-27 12:59:42 -080016 Trait::Debug => expanded.extend(struct_debug(strct, span)),
David Tolnaya6a9e942020-11-27 17:22:35 -080017 Trait::Default => expanded.extend(struct_default(strct, span)),
David Tolnay21466df2020-11-27 14:04:42 -080018 Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
David Tolnay16e26202020-11-27 19:28:37 -080019 Trait::ExternType => unreachable!(),
David Tolnayafdf3d72020-11-27 18:22:08 -080020 Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
David Tolnay577135e2020-11-27 16:23:53 -080021 Trait::Ord => expanded.extend(struct_ord(strct, span)),
David Tolnay21466df2020-11-27 14:04:42 -080022 Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
David Tolnay577135e2020-11-27 16:23:53 -080023 Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
David Tolnay2c24e5c2020-11-27 12:47:41 -080024 }
25 }
26
David Tolnay21466df2020-11-27 14:04:42 -080027 if traits.is_empty() {
28 *actual_derives = None;
29 } else {
30 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
31 }
David Tolnaya3619e92020-11-27 14:13:15 -080032
David Tolnay2c24e5c2020-11-27 12:47:41 -080033 expanded
34}
35
David Tolnayb3d7bb12020-11-27 14:11:41 -080036pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayfbc46692020-11-27 11:33:29 -080037 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -080038 let mut traits = Vec::new();
David Tolnayfbc46692020-11-27 11:33:29 -080039 let mut has_copy = false;
40 let mut has_clone = false;
David Tolnay21466df2020-11-27 14:04:42 -080041 let mut has_eq = false;
42 let mut has_partial_eq = false;
David Tolnayfbc46692020-11-27 11:33:29 -080043
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 }
David Tolnayf84c98b2020-11-27 12:59:42 -080055 Trait::Debug => expanded.extend(enum_debug(enm, span)),
David Tolnaya6a9e942020-11-27 17:22:35 -080056 Trait::Default => unreachable!(),
David Tolnay21466df2020-11-27 14:04:42 -080057 Trait::Eq => {
58 traits.push(quote_spanned!(span=> ::std::cmp::Eq));
59 has_eq = true;
60 }
David Tolnay16e26202020-11-27 19:28:37 -080061 Trait::ExternType => unreachable!(),
David Tolnayafdf3d72020-11-27 18:22:08 -080062 Trait::Hash => traits.push(quote_spanned!(span=> ::std::hash::Hash)),
David Tolnay577135e2020-11-27 16:23:53 -080063 Trait::Ord => expanded.extend(enum_ord(enm, span)),
David Tolnay21466df2020-11-27 14:04:42 -080064 Trait::PartialEq => {
65 traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
66 has_partial_eq = true;
67 }
David Tolnay577135e2020-11-27 16:23:53 -080068 Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
David Tolnayfbc46692020-11-27 11:33:29 -080069 }
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 }
David Tolnay21466df2020-11-27 14:04:42 -080079 if !has_eq {
David Tolnayb3d7bb12020-11-27 14:11:41 -080080 // Required to be derived in order for the enum's "variants" to be
81 // usable in patterns.
David Tolnay21466df2020-11-27 14:04:42 -080082 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),*)]));
David Tolnayb3d7bb12020-11-27 14:11:41 -080089
David Tolnayfbc46692020-11-27 11:33:29 -080090 expanded
91}
92
David Tolnay2c24e5c2020-11-27 12:47:41 -080093fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
94 let ident = &strct.name.rust;
95
96 quote_spanned! {span=>
97 impl ::std::marker::Copy for #ident {}
98 }
99}
100
101fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
102 let ident = &strct.name.rust;
103
David Tolnaybc047bb2020-11-27 14:30:12 -0800104 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -0800105 quote!(*self)
106 } else {
David Tolnay84ed6ad2021-01-01 15:30:14 -0800107 let fields = strct.fields.iter().map(|field| &field.name.rust);
David Tolnay2c24e5c2020-11-27 12:47:41 -0800108 let values = strct.fields.iter().map(|field| {
David Tolnay84ed6ad2021-01-01 15:30:14 -0800109 let ident = &field.name.rust;
David Tolnay2c24e5c2020-11-27 12:47:41 -0800110 let ty = field.ty.to_token_stream();
111 let span = ty.into_iter().last().unwrap().span();
112 quote_spanned!(span=> &self.#ident)
113 });
114 quote_spanned!(span=> #ident {
115 #(#fields: ::std::clone::Clone::clone(#values),)*
116 })
117 };
118
119 quote_spanned! {span=>
120 impl ::std::clone::Clone for #ident {
121 fn clone(&self) -> Self {
122 #body
David Tolnayd8ad9702020-11-27 12:43:59 -0800123 }
124 }
125 }
David Tolnayd8ad9702020-11-27 12:43:59 -0800126}
David Tolnayfbc46692020-11-27 11:33:29 -0800127
David Tolnayf84c98b2020-11-27 12:59:42 -0800128fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
129 let ident = &strct.name.rust;
130 let struct_name = ident.to_string();
David Tolnay84ed6ad2021-01-01 15:30:14 -0800131 let fields = strct.fields.iter().map(|field| &field.name.rust);
David Tolnayf84c98b2020-11-27 12:59:42 -0800132 let field_names = fields.clone().map(Ident::to_string);
133
134 quote_spanned! {span=>
135 impl ::std::fmt::Debug for #ident {
136 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
137 formatter.debug_struct(#struct_name)
138 #(.field(#field_names, &self.#fields))*
139 .finish()
140 }
141 }
142 }
143}
144
David Tolnaya6a9e942020-11-27 17:22:35 -0800145fn struct_default(strct: &Struct, span: Span) -> TokenStream {
146 let ident = &strct.name.rust;
David Tolnay84ed6ad2021-01-01 15:30:14 -0800147 let fields = strct.fields.iter().map(|field| &field.name.rust);
David Tolnaya6a9e942020-11-27 17:22:35 -0800148
149 quote_spanned! {span=>
150 impl ::std::default::Default for #ident {
151 fn default() -> Self {
152 #ident {
153 #(
154 #fields: ::std::default::Default::default(),
155 )*
156 }
157 }
158 }
159 }
160}
161
David Tolnay577135e2020-11-27 16:23:53 -0800162fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
163 let ident = &strct.name.rust;
David Tolnay84ed6ad2021-01-01 15:30:14 -0800164 let fields = strct.fields.iter().map(|field| &field.name.rust);
David Tolnay577135e2020-11-27 16:23:53 -0800165
166 quote_spanned! {span=>
167 impl ::std::cmp::Ord for #ident {
168 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
169 #(
170 match ::std::cmp::Ord::cmp(&self.#fields, &other.#fields) {
171 ::std::cmp::Ordering::Equal => {}
172 ordering => return ordering,
173 }
174 )*
175 ::std::cmp::Ordering::Equal
176 }
177 }
178 }
179}
180
181fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
182 let ident = &strct.name.rust;
183
184 let body = if derive::contains(&strct.derives, Trait::Ord) {
185 quote! {
186 ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
187 }
188 } else {
David Tolnay84ed6ad2021-01-01 15:30:14 -0800189 let fields = strct.fields.iter().map(|field| &field.name.rust);
David Tolnay577135e2020-11-27 16:23:53 -0800190 quote! {
191 #(
192 match ::std::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
193 ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {}
194 ordering => return ordering,
195 }
196 )*
197 ::std::option::Option::Some(::std::cmp::Ordering::Equal)
198 }
199 };
200
201 quote_spanned! {span=>
202 impl ::std::cmp::PartialOrd for #ident {
203 fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
204 #body
205 }
206 }
207 }
208}
209
David Tolnayfbc46692020-11-27 11:33:29 -0800210fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
211 let ident = &enm.name.rust;
212
213 quote_spanned! {span=>
214 impl ::std::marker::Copy for #ident {}
215 }
216}
217
218fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
219 let ident = &enm.name.rust;
220
221 quote_spanned! {span=>
222 impl ::std::clone::Clone for #ident {
223 fn clone(&self) -> Self {
224 *self
225 }
226 }
227 }
228}
David Tolnayf84c98b2020-11-27 12:59:42 -0800229
230fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
231 let ident = &enm.name.rust;
232 let variants = enm.variants.iter().map(|variant| {
David Tolnaye6f62142020-12-21 16:00:41 -0800233 let variant = &variant.name.rust;
David Tolnayf84c98b2020-11-27 12:59:42 -0800234 let name = variant.to_string();
235 quote_spanned! {span=>
236 #ident::#variant => formatter.write_str(#name),
237 }
238 });
239 let fallback = format!("{}({{}})", ident);
240
241 quote_spanned! {span=>
242 impl ::std::fmt::Debug for #ident {
243 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
244 match *self {
245 #(#variants)*
246 _ => ::std::write!(formatter, #fallback, self.repr),
247 }
248 }
249 }
250 }
251}
David Tolnay577135e2020-11-27 16:23:53 -0800252
253fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
254 let ident = &enm.name.rust;
255
256 quote_spanned! {span=>
257 impl ::std::cmp::Ord for #ident {
David Tolnay22af60d2020-12-01 14:46:59 -0800258 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
David Tolnay577135e2020-11-27 16:23:53 -0800259 ::std::cmp::Ord::cmp(&self.repr, &other.repr)
260 }
261 }
262 }
263}
264
265fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
266 let ident = &enm.name.rust;
267
268 quote_spanned! {span=>
269 impl ::std::cmp::PartialOrd for #ident {
David Tolnay22af60d2020-12-01 14:46:59 -0800270 fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
271 ::std::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
David Tolnay577135e2020-11-27 16:23:53 -0800272 }
273 }
274 }
275}