blob: be8ad8218ca8fd948d4b4a883865051591a347c9 [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 Tolnay577135e2020-11-27 16:23:53 -080019 Trait::Ord => expanded.extend(struct_ord(strct, span)),
David Tolnay21466df2020-11-27 14:04:42 -080020 Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
David Tolnay577135e2020-11-27 16:23:53 -080021 Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
David Tolnay2c24e5c2020-11-27 12:47:41 -080022 }
23 }
24
David Tolnay21466df2020-11-27 14:04:42 -080025 if traits.is_empty() {
26 *actual_derives = None;
27 } else {
28 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
29 }
David Tolnaya3619e92020-11-27 14:13:15 -080030
David Tolnay2c24e5c2020-11-27 12:47:41 -080031 expanded
32}
33
David Tolnayb3d7bb12020-11-27 14:11:41 -080034pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayfbc46692020-11-27 11:33:29 -080035 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -080036 let mut traits = Vec::new();
David Tolnayfbc46692020-11-27 11:33:29 -080037 let mut has_copy = false;
38 let mut has_clone = false;
David Tolnay21466df2020-11-27 14:04:42 -080039 let mut has_eq = false;
40 let mut has_partial_eq = false;
David Tolnayfbc46692020-11-27 11:33:29 -080041
42 for derive in &enm.derives {
43 let span = derive.span;
44 match derive.what {
45 Trait::Copy => {
46 expanded.extend(enum_copy(enm, span));
47 has_copy = true;
48 }
49 Trait::Clone => {
50 expanded.extend(enum_clone(enm, span));
51 has_clone = true;
52 }
David Tolnayf84c98b2020-11-27 12:59:42 -080053 Trait::Debug => expanded.extend(enum_debug(enm, span)),
David Tolnaya6a9e942020-11-27 17:22:35 -080054 Trait::Default => unreachable!(),
David Tolnay21466df2020-11-27 14:04:42 -080055 Trait::Eq => {
56 traits.push(quote_spanned!(span=> ::std::cmp::Eq));
57 has_eq = true;
58 }
David Tolnay577135e2020-11-27 16:23:53 -080059 Trait::Ord => expanded.extend(enum_ord(enm, span)),
David Tolnay21466df2020-11-27 14:04:42 -080060 Trait::PartialEq => {
61 traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
62 has_partial_eq = true;
63 }
David Tolnay577135e2020-11-27 16:23:53 -080064 Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
David Tolnayfbc46692020-11-27 11:33:29 -080065 }
66 }
67
68 let span = enm.name.rust.span();
69 if !has_copy {
70 expanded.extend(enum_copy(enm, span));
71 }
72 if !has_clone {
73 expanded.extend(enum_clone(enm, span));
74 }
David Tolnay21466df2020-11-27 14:04:42 -080075 if !has_eq {
David Tolnayb3d7bb12020-11-27 14:11:41 -080076 // Required to be derived in order for the enum's "variants" to be
77 // usable in patterns.
David Tolnay21466df2020-11-27 14:04:42 -080078 traits.push(quote!(::std::cmp::Eq));
79 }
80 if !has_partial_eq {
81 traits.push(quote!(::std::cmp::PartialEq));
82 }
83
84 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
David Tolnayb3d7bb12020-11-27 14:11:41 -080085
David Tolnayfbc46692020-11-27 11:33:29 -080086 expanded
87}
88
David Tolnay2c24e5c2020-11-27 12:47:41 -080089fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
90 let ident = &strct.name.rust;
91
92 quote_spanned! {span=>
93 impl ::std::marker::Copy for #ident {}
94 }
95}
96
97fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
98 let ident = &strct.name.rust;
99
David Tolnaybc047bb2020-11-27 14:30:12 -0800100 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -0800101 quote!(*self)
102 } else {
103 let fields = strct.fields.iter().map(|field| &field.ident);
104 let values = strct.fields.iter().map(|field| {
105 let ident = &field.ident;
106 let ty = field.ty.to_token_stream();
107 let span = ty.into_iter().last().unwrap().span();
108 quote_spanned!(span=> &self.#ident)
109 });
110 quote_spanned!(span=> #ident {
111 #(#fields: ::std::clone::Clone::clone(#values),)*
112 })
113 };
114
115 quote_spanned! {span=>
116 impl ::std::clone::Clone for #ident {
117 fn clone(&self) -> Self {
118 #body
David Tolnayd8ad9702020-11-27 12:43:59 -0800119 }
120 }
121 }
David Tolnayd8ad9702020-11-27 12:43:59 -0800122}
David Tolnayfbc46692020-11-27 11:33:29 -0800123
David Tolnayf84c98b2020-11-27 12:59:42 -0800124fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
125 let ident = &strct.name.rust;
126 let struct_name = ident.to_string();
127 let fields = strct.fields.iter().map(|field| &field.ident);
128 let field_names = fields.clone().map(Ident::to_string);
129
130 quote_spanned! {span=>
131 impl ::std::fmt::Debug for #ident {
132 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
133 formatter.debug_struct(#struct_name)
134 #(.field(#field_names, &self.#fields))*
135 .finish()
136 }
137 }
138 }
139}
140
David Tolnaya6a9e942020-11-27 17:22:35 -0800141fn struct_default(strct: &Struct, span: Span) -> TokenStream {
142 let ident = &strct.name.rust;
143 let fields = strct.fields.iter().map(|field| &field.ident);
144
145 quote_spanned! {span=>
146 impl ::std::default::Default for #ident {
147 fn default() -> Self {
148 #ident {
149 #(
150 #fields: ::std::default::Default::default(),
151 )*
152 }
153 }
154 }
155 }
156}
157
David Tolnay577135e2020-11-27 16:23:53 -0800158fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
159 let ident = &strct.name.rust;
160 let fields = strct.fields.iter().map(|field| &field.ident);
161
162 quote_spanned! {span=>
163 impl ::std::cmp::Ord for #ident {
164 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
165 #(
166 match ::std::cmp::Ord::cmp(&self.#fields, &other.#fields) {
167 ::std::cmp::Ordering::Equal => {}
168 ordering => return ordering,
169 }
170 )*
171 ::std::cmp::Ordering::Equal
172 }
173 }
174 }
175}
176
177fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
178 let ident = &strct.name.rust;
179
180 let body = if derive::contains(&strct.derives, Trait::Ord) {
181 quote! {
182 ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
183 }
184 } else {
185 let fields = strct.fields.iter().map(|field| &field.ident);
186 quote! {
187 #(
188 match ::std::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
189 ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {}
190 ordering => return ordering,
191 }
192 )*
193 ::std::option::Option::Some(::std::cmp::Ordering::Equal)
194 }
195 };
196
197 quote_spanned! {span=>
198 impl ::std::cmp::PartialOrd for #ident {
199 fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
200 #body
201 }
202 }
203 }
204}
205
David Tolnayfbc46692020-11-27 11:33:29 -0800206fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
207 let ident = &enm.name.rust;
208
209 quote_spanned! {span=>
210 impl ::std::marker::Copy for #ident {}
211 }
212}
213
214fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
215 let ident = &enm.name.rust;
216
217 quote_spanned! {span=>
218 impl ::std::clone::Clone for #ident {
219 fn clone(&self) -> Self {
220 *self
221 }
222 }
223 }
224}
David Tolnayf84c98b2020-11-27 12:59:42 -0800225
226fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
227 let ident = &enm.name.rust;
228 let variants = enm.variants.iter().map(|variant| {
229 let variant = &variant.ident;
230 let name = variant.to_string();
231 quote_spanned! {span=>
232 #ident::#variant => formatter.write_str(#name),
233 }
234 });
235 let fallback = format!("{}({{}})", ident);
236
237 quote_spanned! {span=>
238 impl ::std::fmt::Debug for #ident {
239 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
240 match *self {
241 #(#variants)*
242 _ => ::std::write!(formatter, #fallback, self.repr),
243 }
244 }
245 }
246 }
247}
David Tolnay577135e2020-11-27 16:23:53 -0800248
249fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
250 let ident = &enm.name.rust;
251
252 quote_spanned! {span=>
253 impl ::std::cmp::Ord for #ident {
254 pub fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
255 ::std::cmp::Ord::cmp(&self.repr, &other.repr)
256 }
257 }
258 }
259}
260
261fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
262 let ident = &enm.name.rust;
263
264 quote_spanned! {span=>
265 impl ::std::cmp::PartialOrd for #ident {
266 pub fn cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
267 ::std::cmp::PartialOrd::cmp(&self.repr, &other.repr)
268 }
269 }
270 }
271}