1 | // Not supported by MSRV |
2 | #![allow (clippy::uninlined_format_args)] |
3 | |
4 | extern crate proc_macro; |
5 | |
6 | use proc_macro::TokenStream; |
7 | use proc_macro2::Span; |
8 | use quote::quote; |
9 | use syn::{parse_macro_input, Expr, Ident}; |
10 | |
11 | mod enum_attributes; |
12 | mod parsing; |
13 | use parsing::{get_crate_name, EnumInfo}; |
14 | mod utils; |
15 | mod variant_attributes; |
16 | |
17 | /// Implements `Into<Primitive>` for a `#[repr(Primitive)] enum`. |
18 | /// |
19 | /// (It actually implements `From<Enum> for Primitive`) |
20 | /// |
21 | /// ## Allows turning an enum into a primitive. |
22 | /// |
23 | /// ```rust |
24 | /// use num_enum::IntoPrimitive; |
25 | /// |
26 | /// #[derive(IntoPrimitive)] |
27 | /// #[repr(u8)] |
28 | /// enum Number { |
29 | /// Zero, |
30 | /// One, |
31 | /// } |
32 | /// |
33 | /// let zero: u8 = Number::Zero.into(); |
34 | /// assert_eq!(zero, 0u8); |
35 | /// ``` |
36 | #[proc_macro_derive (IntoPrimitive, attributes(num_enum, catch_all))] |
37 | pub fn derive_into_primitive(input: TokenStream) -> TokenStream { |
38 | let enum_info = parse_macro_input!(input as EnumInfo); |
39 | let catch_all = enum_info.catch_all(); |
40 | let name = &enum_info.name; |
41 | let repr = &enum_info.repr; |
42 | |
43 | let body = if let Some(catch_all_ident) = catch_all { |
44 | quote! { |
45 | match enum_value { |
46 | #name::#catch_all_ident(raw) => raw, |
47 | rest => unsafe { *(&rest as *const #name as *const Self) } |
48 | } |
49 | } |
50 | } else { |
51 | quote! { enum_value as Self } |
52 | }; |
53 | |
54 | TokenStream::from(quote! { |
55 | impl From<#name> for #repr { |
56 | #[inline] |
57 | fn from (enum_value: #name) -> Self |
58 | { |
59 | #body |
60 | } |
61 | } |
62 | }) |
63 | } |
64 | |
65 | /// Implements `From<Primitive>` for a `#[repr(Primitive)] enum`. |
66 | /// |
67 | /// Turning a primitive into an enum with `from`. |
68 | /// ---------------------------------------------- |
69 | /// |
70 | /// ```rust |
71 | /// use num_enum::FromPrimitive; |
72 | /// |
73 | /// #[derive(Debug, Eq, PartialEq, FromPrimitive)] |
74 | /// #[repr(u8)] |
75 | /// enum Number { |
76 | /// Zero, |
77 | /// #[num_enum(default)] |
78 | /// NonZero, |
79 | /// } |
80 | /// |
81 | /// let zero = Number::from(0u8); |
82 | /// assert_eq!(zero, Number::Zero); |
83 | /// |
84 | /// let one = Number::from(1u8); |
85 | /// assert_eq!(one, Number::NonZero); |
86 | /// |
87 | /// let two = Number::from(2u8); |
88 | /// assert_eq!(two, Number::NonZero); |
89 | /// ``` |
90 | #[proc_macro_derive (FromPrimitive, attributes(num_enum, default, catch_all))] |
91 | pub fn derive_from_primitive(input: TokenStream) -> TokenStream { |
92 | let enum_info: EnumInfo = parse_macro_input!(input); |
93 | let krate = Ident::new(&get_crate_name(), Span::call_site()); |
94 | |
95 | let is_naturally_exhaustive = enum_info.is_naturally_exhaustive(); |
96 | let catch_all_body = match is_naturally_exhaustive { |
97 | Ok(is_naturally_exhaustive) => { |
98 | if is_naturally_exhaustive { |
99 | quote! { unreachable!("exhaustive enum" ) } |
100 | } else if let Some(default_ident) = enum_info.default() { |
101 | quote! { Self::#default_ident } |
102 | } else if let Some(catch_all_ident) = enum_info.catch_all() { |
103 | quote! { Self::#catch_all_ident(number) } |
104 | } else { |
105 | let span = Span::call_site(); |
106 | let message = |
107 | "#[derive(num_enum::FromPrimitive)] requires enum to be exhaustive, or a variant marked with `#[default]`, `#[num_enum(default)]`, or `#[num_enum(catch_all)`" ; |
108 | return syn::Error::new(span, message).to_compile_error().into(); |
109 | } |
110 | } |
111 | Err(err) => { |
112 | return err.to_compile_error().into(); |
113 | } |
114 | }; |
115 | |
116 | let EnumInfo { |
117 | ref name, ref repr, .. |
118 | } = enum_info; |
119 | |
120 | let variant_idents: Vec<Ident> = enum_info.variant_idents(); |
121 | let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents(); |
122 | let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions(); |
123 | |
124 | debug_assert_eq!(variant_idents.len(), variant_expressions.len()); |
125 | |
126 | TokenStream::from(quote! { |
127 | impl ::#krate::FromPrimitive for #name { |
128 | type Primitive = #repr; |
129 | |
130 | fn from_primitive(number: Self::Primitive) -> Self { |
131 | // Use intermediate const(s) so that enums defined like |
132 | // `Two = ONE + 1u8` work properly. |
133 | #![allow(non_upper_case_globals)] |
134 | #( |
135 | #( |
136 | const #expression_idents: #repr = #variant_expressions; |
137 | )* |
138 | )* |
139 | #[deny(unreachable_patterns)] |
140 | match number { |
141 | #( |
142 | #( #expression_idents )|* |
143 | => Self::#variant_idents, |
144 | )* |
145 | #[allow(unreachable_patterns)] |
146 | _ => #catch_all_body, |
147 | } |
148 | } |
149 | } |
150 | |
151 | impl ::core::convert::From<#repr> for #name { |
152 | #[inline] |
153 | fn from ( |
154 | number: #repr, |
155 | ) -> Self { |
156 | ::#krate::FromPrimitive::from_primitive(number) |
157 | } |
158 | } |
159 | |
160 | #[doc(hidden)] |
161 | impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {} |
162 | }) |
163 | } |
164 | |
165 | /// Implements `TryFrom<Primitive>` for a `#[repr(Primitive)] enum`. |
166 | /// |
167 | /// Attempting to turn a primitive into an enum with `try_from`. |
168 | /// ---------------------------------------------- |
169 | /// |
170 | /// ```rust |
171 | /// use num_enum::TryFromPrimitive; |
172 | /// use std::convert::TryFrom; |
173 | /// |
174 | /// #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] |
175 | /// #[repr(u8)] |
176 | /// enum Number { |
177 | /// Zero, |
178 | /// One, |
179 | /// } |
180 | /// |
181 | /// let zero = Number::try_from(0u8); |
182 | /// assert_eq!(zero, Ok(Number::Zero)); |
183 | /// |
184 | /// let three = Number::try_from(3u8); |
185 | /// assert_eq!( |
186 | /// three.unwrap_err().to_string(), |
187 | /// "No discriminant in enum `Number` matches the value `3`" , |
188 | /// ); |
189 | /// ``` |
190 | #[proc_macro_derive (TryFromPrimitive, attributes(num_enum))] |
191 | pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream { |
192 | let enum_info: EnumInfo = parse_macro_input!(input); |
193 | let krate = Ident::new(&get_crate_name(), Span::call_site()); |
194 | |
195 | let EnumInfo { |
196 | ref name, |
197 | ref repr, |
198 | ref error_type_info, |
199 | .. |
200 | } = enum_info; |
201 | |
202 | let variant_idents: Vec<Ident> = enum_info.variant_idents(); |
203 | let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents(); |
204 | let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions(); |
205 | |
206 | debug_assert_eq!(variant_idents.len(), variant_expressions.len()); |
207 | |
208 | let error_type = &error_type_info.name; |
209 | let error_constructor = &error_type_info.constructor; |
210 | |
211 | TokenStream::from(quote! { |
212 | impl ::#krate::TryFromPrimitive for #name { |
213 | type Primitive = #repr; |
214 | type Error = #error_type; |
215 | |
216 | const NAME: &'static str = stringify!(#name); |
217 | |
218 | fn try_from_primitive ( |
219 | number: Self::Primitive, |
220 | ) -> ::core::result::Result< |
221 | Self, |
222 | #error_type |
223 | > { |
224 | // Use intermediate const(s) so that enums defined like |
225 | // `Two = ONE + 1u8` work properly. |
226 | #![allow(non_upper_case_globals)] |
227 | #( |
228 | #( |
229 | const #expression_idents: #repr = #variant_expressions; |
230 | )* |
231 | )* |
232 | #[deny(unreachable_patterns)] |
233 | match number { |
234 | #( |
235 | #( #expression_idents )|* |
236 | => ::core::result::Result::Ok(Self::#variant_idents), |
237 | )* |
238 | #[allow(unreachable_patterns)] |
239 | _ => ::core::result::Result::Err( |
240 | #error_constructor ( number ) |
241 | ), |
242 | } |
243 | } |
244 | } |
245 | |
246 | impl ::core::convert::TryFrom<#repr> for #name { |
247 | type Error = #error_type; |
248 | |
249 | #[inline] |
250 | fn try_from ( |
251 | number: #repr, |
252 | ) -> ::core::result::Result<Self, #error_type> |
253 | { |
254 | ::#krate::TryFromPrimitive::try_from_primitive(number) |
255 | } |
256 | } |
257 | |
258 | #[doc(hidden)] |
259 | impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {} |
260 | }) |
261 | } |
262 | |
263 | /// Generates a `unsafe fn unchecked_transmute_from(number: Primitive) -> Self` |
264 | /// associated function. |
265 | /// |
266 | /// Allows unsafely turning a primitive into an enum with unchecked_transmute_from |
267 | /// ------------------------------------------------------------------------------ |
268 | /// |
269 | /// If you're really certain a conversion will succeed, and want to avoid a small amount of overhead, you can use unsafe |
270 | /// code to do this conversion. Unless you have data showing that the match statement generated in the `try_from` above is a |
271 | /// bottleneck for you, you should avoid doing this, as the unsafe code has potential to cause serious memory issues in |
272 | /// your program. |
273 | /// |
274 | /// Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum. |
275 | /// If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`. |
276 | /// |
277 | /// ```rust |
278 | /// use num_enum::UnsafeFromPrimitive; |
279 | /// |
280 | /// #[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)] |
281 | /// #[repr(u8)] |
282 | /// enum Number { |
283 | /// Zero, |
284 | /// One, |
285 | /// } |
286 | /// |
287 | /// fn main() { |
288 | /// assert_eq!( |
289 | /// Number::Zero, |
290 | /// unsafe { Number::unchecked_transmute_from(0_u8) }, |
291 | /// ); |
292 | /// assert_eq!( |
293 | /// Number::One, |
294 | /// unsafe { Number::unchecked_transmute_from(1_u8) }, |
295 | /// ); |
296 | /// } |
297 | /// |
298 | /// unsafe fn undefined_behavior() { |
299 | /// let _ = Number::unchecked_transmute_from(2); // 2 is not a valid discriminant! |
300 | /// } |
301 | /// ``` |
302 | #[proc_macro_derive (UnsafeFromPrimitive, attributes(num_enum))] |
303 | pub fn derive_unsafe_from_primitive(stream: TokenStream) -> TokenStream { |
304 | let enum_info: EnumInfo = parse_macro_input!(stream as EnumInfo); |
305 | let krate: Ident = Ident::new(&get_crate_name(), Span::call_site()); |
306 | |
307 | let EnumInfo { |
308 | ref name: &Ident, ref repr: &Ident, .. |
309 | } = enum_info; |
310 | |
311 | TokenStream::from(quote! { |
312 | impl ::#krate::UnsafeFromPrimitive for #name { |
313 | type Primitive = #repr; |
314 | |
315 | unsafe fn unchecked_transmute_from(number: Self::Primitive) -> Self { |
316 | ::core::mem::transmute(number) |
317 | } |
318 | } |
319 | }) |
320 | } |
321 | |
322 | /// Implements `core::default::Default` for a `#[repr(Primitive)] enum`. |
323 | /// |
324 | /// Whichever variant has the `#[default]` or `#[num_enum(default)]` attribute will be returned. |
325 | /// ---------------------------------------------- |
326 | /// |
327 | /// ```rust |
328 | /// #[derive(Debug, Eq, PartialEq, num_enum::Default)] |
329 | /// #[repr(u8)] |
330 | /// enum Number { |
331 | /// Zero, |
332 | /// #[default] |
333 | /// One, |
334 | /// } |
335 | /// |
336 | /// assert_eq!(Number::One, Number::default()); |
337 | /// assert_eq!(Number::One, <Number as ::core::default::Default>::default()); |
338 | /// ``` |
339 | #[proc_macro_derive (Default, attributes(num_enum, default))] |
340 | pub fn derive_default(stream: TokenStream) -> TokenStream { |
341 | let enum_info: EnumInfo = parse_macro_input!(stream as EnumInfo); |
342 | |
343 | let default_ident: &Ident = match enum_info.default() { |
344 | Some(ident: &Ident) => ident, |
345 | None => { |
346 | let span: Span = Span::call_site(); |
347 | let message: &str = |
348 | "#[derive(num_enum::Default)] requires enum to be exhaustive, or a variant marked with `#[default]` or `#[num_enum(default)]`" ; |
349 | return syn::Error::new(span, message).to_compile_error().into(); |
350 | } |
351 | }; |
352 | |
353 | let EnumInfo { ref name: &Ident, .. } = enum_info; |
354 | |
355 | TokenStream::from(quote! { |
356 | impl ::core::default::Default for #name { |
357 | #[inline] |
358 | fn default() -> Self { |
359 | Self::#default_ident |
360 | } |
361 | } |
362 | }) |
363 | } |
364 | |