1 | use std::borrow::Cow; |
2 | use std::cell::RefCell; |
3 | use std::collections::btree_map::BTreeMap; |
4 | use std::collections::hash_map::HashMap; |
5 | use std::collections::HashSet; |
6 | use std::hash::BuildHasher; |
7 | use std::num; |
8 | use std::rc::Rc; |
9 | use std::sync::atomic::AtomicBool; |
10 | use std::sync::Arc; |
11 | |
12 | use syn::{Expr, Lit, Meta}; |
13 | |
14 | use crate::ast::NestedMeta; |
15 | use crate::util::path_to_string; |
16 | use crate::{Error, Result}; |
17 | |
18 | /// Create an instance from an item in an attribute declaration. |
19 | /// |
20 | /// # Implementing `FromMeta` |
21 | /// * Do not take a dependency on the `ident` of the passed-in meta item. The ident will be set by the field name of the containing struct. |
22 | /// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors. |
23 | /// |
24 | /// # Provided Implementations |
25 | /// ## bool |
26 | /// |
27 | /// * Word with no value specified - becomes `true`. |
28 | /// * As a boolean literal, e.g. `foo = true`. |
29 | /// * As a string literal, e.g. `foo = "true"`. |
30 | /// |
31 | /// ## char |
32 | /// * As a char literal, e.g. `foo = '#'`. |
33 | /// * As a string literal consisting of a single character, e.g. `foo = "#"`. |
34 | /// |
35 | /// ## String |
36 | /// * As a string literal, e.g. `foo = "hello"`. |
37 | /// * As a raw string literal, e.g. `foo = r#"hello "world""#`. |
38 | /// |
39 | /// ## Number |
40 | /// * As a string literal, e.g. `foo = "-25"`. |
41 | /// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks. |
42 | /// |
43 | /// ## () |
44 | /// * Word with no value specified, e.g. `foo`. This is best used with `Option`. |
45 | /// See `darling::util::Flag` for a more strongly-typed alternative. |
46 | /// |
47 | /// ## Option |
48 | /// * Any format produces `Some`. |
49 | /// |
50 | /// ## `Result<T, darling::Error>` |
51 | /// * Allows for fallible parsing; will populate the target field with the result of the |
52 | /// parse attempt. |
53 | pub trait FromMeta: Sized { |
54 | fn from_nested_meta(item: &NestedMeta) -> Result<Self> { |
55 | (match *item { |
56 | NestedMeta::Lit(ref lit) => Self::from_value(lit), |
57 | NestedMeta::Meta(ref mi) => Self::from_meta(mi), |
58 | }) |
59 | .map_err(|e| e.with_span(item)) |
60 | } |
61 | |
62 | /// Create an instance from a `syn::Meta` by dispatching to the format-appropriate |
63 | /// trait function. This generally should not be overridden by implementers. |
64 | /// |
65 | /// # Error Spans |
66 | /// If this method is overridden and can introduce errors that weren't passed up from |
67 | /// other `from_meta` calls, the override must call `with_span` on the error using the |
68 | /// `item` to make sure that the emitted diagnostic points to the correct location in |
69 | /// source code. |
70 | fn from_meta(item: &Meta) -> Result<Self> { |
71 | (match *item { |
72 | Meta::Path(_) => Self::from_word(), |
73 | Meta::List(ref value) => { |
74 | Self::from_list(&NestedMeta::parse_meta_list(value.tokens.clone())?[..]) |
75 | } |
76 | Meta::NameValue(ref value) => Self::from_expr(&value.value), |
77 | }) |
78 | .map_err(|e| e.with_span(item)) |
79 | } |
80 | |
81 | /// When a field is omitted from a parent meta-item, `from_none` is used to attempt |
82 | /// recovery before a missing field error is generated. |
83 | /// |
84 | /// **Most types should not override this method.** `darling` already allows field-level |
85 | /// missing-field recovery using `#[darling(default)]` and `#[darling(default = "...")]`, |
86 | /// and users who add a `String` field to their `FromMeta`-deriving struct would be surprised |
87 | /// if they get back `""` instead of a missing field error when that field is omitted. |
88 | /// |
89 | /// The primary use-case for this is `Option<T>` fields gracefully handlling absence without |
90 | /// needing `#[darling(default)]`. |
91 | fn from_none() -> Option<Self> { |
92 | None |
93 | } |
94 | |
95 | /// Create an instance from the presence of the word in the attribute with no |
96 | /// additional options specified. |
97 | fn from_word() -> Result<Self> { |
98 | Err(Error::unsupported_format("word" )) |
99 | } |
100 | |
101 | /// Create an instance from a list of nested meta items. |
102 | #[allow (unused_variables)] |
103 | fn from_list(items: &[NestedMeta]) -> Result<Self> { |
104 | Err(Error::unsupported_format("list" )) |
105 | } |
106 | |
107 | /// Create an instance from a literal value of either `foo = "bar"` or `foo("bar")`. |
108 | /// This dispatches to the appropriate method based on the type of literal encountered, |
109 | /// and generally should not be overridden by implementers. |
110 | /// |
111 | /// # Error Spans |
112 | /// If this method is overridden, the override must make sure to add `value`'s span |
113 | /// information to the returned error by calling `with_span(value)` on the `Error` instance. |
114 | fn from_value(value: &Lit) -> Result<Self> { |
115 | (match *value { |
116 | Lit::Bool(ref b) => Self::from_bool(b.value), |
117 | Lit::Str(ref s) => Self::from_string(&s.value()), |
118 | Lit::Char(ref ch) => Self::from_char(ch.value()), |
119 | _ => Err(Error::unexpected_lit_type(value)), |
120 | }) |
121 | .map_err(|e| e.with_span(value)) |
122 | } |
123 | |
124 | fn from_expr(expr: &Expr) -> Result<Self> { |
125 | match *expr { |
126 | Expr::Lit(ref lit) => Self::from_value(&lit.lit), |
127 | Expr::Group(ref group) => { |
128 | // syn may generate this invisible group delimiter when the input to the darling |
129 | // proc macro (specifically, the attributes) are generated by a |
130 | // macro_rules! (e.g. propagating a macro_rules!'s expr) |
131 | // Since we want to basically ignore these invisible group delimiters, |
132 | // we just propagate the call to the inner expression. |
133 | Self::from_expr(&group.expr) |
134 | } |
135 | _ => Err(Error::unexpected_expr_type(expr)), |
136 | } |
137 | .map_err(|e| e.with_span(expr)) |
138 | } |
139 | |
140 | /// Create an instance from a char literal in a value position. |
141 | #[allow (unused_variables)] |
142 | fn from_char(value: char) -> Result<Self> { |
143 | Err(Error::unexpected_type("char" )) |
144 | } |
145 | |
146 | /// Create an instance from a string literal in a value position. |
147 | #[allow (unused_variables)] |
148 | fn from_string(value: &str) -> Result<Self> { |
149 | Err(Error::unexpected_type("string" )) |
150 | } |
151 | |
152 | /// Create an instance from a bool literal in a value position. |
153 | #[allow (unused_variables)] |
154 | fn from_bool(value: bool) -> Result<Self> { |
155 | Err(Error::unexpected_type("bool" )) |
156 | } |
157 | } |
158 | |
159 | // FromMeta impls for std and syn types. |
160 | |
161 | impl FromMeta for () { |
162 | fn from_word() -> Result<Self> { |
163 | Ok(()) |
164 | } |
165 | } |
166 | |
167 | impl FromMeta for bool { |
168 | fn from_word() -> Result<Self> { |
169 | Ok(true) |
170 | } |
171 | |
172 | #[allow (clippy::wrong_self_convention)] // false positive |
173 | fn from_bool(value: bool) -> Result<Self> { |
174 | Ok(value) |
175 | } |
176 | |
177 | fn from_string(value: &str) -> Result<Self> { |
178 | value.parse().map_err(|_| Error::unknown_value(value)) |
179 | } |
180 | } |
181 | |
182 | impl FromMeta for AtomicBool { |
183 | fn from_meta(mi: &Meta) -> Result<Self> { |
184 | FromMeta::from_meta(mi) |
185 | .map(AtomicBool::new) |
186 | .map_err(|e: Error| e.with_span(node:mi)) |
187 | } |
188 | } |
189 | |
190 | impl FromMeta for char { |
191 | #[allow (clippy::wrong_self_convention)] // false positive |
192 | fn from_char(value: char) -> Result<Self> { |
193 | Ok(value) |
194 | } |
195 | |
196 | fn from_string(s: &str) -> Result<Self> { |
197 | let mut chars: Chars<'_> = s.chars(); |
198 | let char1: Option = chars.next(); |
199 | let char2: Option = chars.next(); |
200 | |
201 | if let (Some(char: char), None) = (char1, char2) { |
202 | Ok(char) |
203 | } else { |
204 | Err(Error::unexpected_type(ty:"string" )) |
205 | } |
206 | } |
207 | } |
208 | |
209 | impl FromMeta for String { |
210 | fn from_string(s: &str) -> Result<Self> { |
211 | Ok(s.to_string()) |
212 | } |
213 | } |
214 | |
215 | impl FromMeta for std::path::PathBuf { |
216 | fn from_string(s: &str) -> Result<Self> { |
217 | Ok(s.into()) |
218 | } |
219 | } |
220 | |
221 | /// Generate an impl of `FromMeta` that will accept strings which parse to numbers or |
222 | /// integer literals. |
223 | macro_rules! from_meta_num { |
224 | ($ty:path) => { |
225 | impl FromMeta for $ty { |
226 | fn from_string(s: &str) -> Result<Self> { |
227 | s.parse().map_err(|_| Error::unknown_value(s)) |
228 | } |
229 | |
230 | fn from_value(value: &Lit) -> Result<Self> { |
231 | (match *value { |
232 | Lit::Str(ref s) => Self::from_string(&s.value()), |
233 | Lit::Int(ref s) => s.base10_parse::<$ty>().map_err(Error::from), |
234 | _ => Err(Error::unexpected_lit_type(value)), |
235 | }) |
236 | .map_err(|e| e.with_span(value)) |
237 | } |
238 | } |
239 | }; |
240 | } |
241 | |
242 | from_meta_num!(u8); |
243 | from_meta_num!(u16); |
244 | from_meta_num!(u32); |
245 | from_meta_num!(u64); |
246 | from_meta_num!(u128); |
247 | from_meta_num!(usize); |
248 | from_meta_num!(i8); |
249 | from_meta_num!(i16); |
250 | from_meta_num!(i32); |
251 | from_meta_num!(i64); |
252 | from_meta_num!(i128); |
253 | from_meta_num!(isize); |
254 | from_meta_num!(num::NonZeroU8); |
255 | from_meta_num!(num::NonZeroU16); |
256 | from_meta_num!(num::NonZeroU32); |
257 | from_meta_num!(num::NonZeroU64); |
258 | from_meta_num!(num::NonZeroU128); |
259 | from_meta_num!(num::NonZeroUsize); |
260 | from_meta_num!(num::NonZeroI8); |
261 | from_meta_num!(num::NonZeroI16); |
262 | from_meta_num!(num::NonZeroI32); |
263 | from_meta_num!(num::NonZeroI64); |
264 | from_meta_num!(num::NonZeroI128); |
265 | from_meta_num!(num::NonZeroIsize); |
266 | |
267 | /// Generate an impl of `FromMeta` that will accept strings which parse to floats or |
268 | /// float literals. |
269 | macro_rules! from_meta_float { |
270 | ($ty:ident) => { |
271 | impl FromMeta for $ty { |
272 | fn from_string(s: &str) -> Result<Self> { |
273 | s.parse().map_err(|_| Error::unknown_value(s)) |
274 | } |
275 | |
276 | fn from_value(value: &Lit) -> Result<Self> { |
277 | (match *value { |
278 | Lit::Str(ref s) => Self::from_string(&s.value()), |
279 | Lit::Float(ref s) => s.base10_parse::<$ty>().map_err(Error::from), |
280 | _ => Err(Error::unexpected_lit_type(value)), |
281 | }) |
282 | .map_err(|e| e.with_span(value)) |
283 | } |
284 | } |
285 | }; |
286 | } |
287 | |
288 | from_meta_float!(f32); |
289 | from_meta_float!(f64); |
290 | |
291 | /// Parsing support for punctuated. This attempts to preserve span information |
292 | /// when available, but also supports parsing strings with the call site as the |
293 | /// emitted span. |
294 | impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> { |
295 | fn from_value(value: &Lit) -> Result<Self> { |
296 | if let Lit::Str(ref ident: &LitStr) = *value { |
297 | ident |
298 | .parse_with(syn::punctuated::Punctuated::parse_terminated) |
299 | .map_err(|_| Error::unknown_lit_str_value(ident)) |
300 | } else { |
301 | Err(Error::unexpected_lit_type(lit:value)) |
302 | } |
303 | } |
304 | } |
305 | |
306 | /// Support for arbitrary expressions as values in a meta item. |
307 | /// |
308 | /// For backwards-compatibility to versions of `darling` based on `syn` 1, |
309 | /// string literals will be "unwrapped" and their contents will be parsed |
310 | /// as an expression. |
311 | /// |
312 | /// See [`util::parse_expr`](crate::util::parse_expr) for functions to provide |
313 | /// alternate parsing modes for this type. |
314 | impl FromMeta for syn::Expr { |
315 | fn from_expr(expr: &Expr) -> Result<Self> { |
316 | match expr { |
317 | Expr::Lit(syn::ExprLit { |
318 | lit: lit @ syn::Lit::Str(_), |
319 | .. |
320 | }) => Self::from_value(lit), |
321 | Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr |
322 | _ => Ok(expr.clone()), |
323 | } |
324 | } |
325 | |
326 | fn from_string(value: &str) -> Result<Self> { |
327 | syn::parse_str(value).map_err(|_| Error::unknown_value(value)) |
328 | } |
329 | |
330 | fn from_value(value: &::syn::Lit) -> Result<Self> { |
331 | if let ::syn::Lit::Str(ref v) = *value { |
332 | v.parse::<syn::Expr>() |
333 | .map_err(|_| Error::unknown_lit_str_value(v)) |
334 | } else { |
335 | Err(Error::unexpected_lit_type(value)) |
336 | } |
337 | } |
338 | } |
339 | |
340 | /// Parser for paths that supports both quote-wrapped and bare values. |
341 | impl FromMeta for syn::Path { |
342 | fn from_string(value: &str) -> Result<Self> { |
343 | syn::parse_str(value).map_err(|_| Error::unknown_value(value)) |
344 | } |
345 | |
346 | fn from_value(value: &::syn::Lit) -> Result<Self> { |
347 | if let ::syn::Lit::Str(ref v: &LitStr) = *value { |
348 | v.parse().map_err(|_| Error::unknown_lit_str_value(v)) |
349 | } else { |
350 | Err(Error::unexpected_lit_type(lit:value)) |
351 | } |
352 | } |
353 | |
354 | fn from_expr(expr: &Expr) -> Result<Self> { |
355 | match expr { |
356 | Expr::Lit(lit: &ExprLit) => Self::from_value(&lit.lit), |
357 | Expr::Path(path: &ExprPath) => Ok(path.path.clone()), |
358 | Expr::Group(group: &ExprGroup) => Self::from_expr(&group.expr), // see FromMeta::from_expr |
359 | _ => Err(Error::unexpected_expr_type(expr)), |
360 | } |
361 | } |
362 | } |
363 | |
364 | impl FromMeta for syn::Ident { |
365 | fn from_string(value: &str) -> Result<Self> { |
366 | syn::parse_str(value).map_err(|_| Error::unknown_value(value)) |
367 | } |
368 | |
369 | fn from_value(value: &syn::Lit) -> Result<Self> { |
370 | if let syn::Lit::Str(ref v) = *value { |
371 | v.parse().map_err(|_| Error::unknown_lit_str_value(v)) |
372 | } else { |
373 | Err(Error::unexpected_lit_type(value)) |
374 | } |
375 | } |
376 | |
377 | fn from_expr(expr: &Expr) -> Result<Self> { |
378 | match expr { |
379 | Expr::Lit(lit) => Self::from_value(&lit.lit), |
380 | // All idents are paths, but not all paths are idents - |
381 | // the get_ident() method does additional validation to |
382 | // make sure the path is actually an ident. |
383 | Expr::Path(path) => match path.path.get_ident() { |
384 | Some(ident) => Ok(ident.clone()), |
385 | None => Err(Error::unexpected_expr_type(expr)), |
386 | }, |
387 | Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr |
388 | _ => Err(Error::unexpected_expr_type(expr)), |
389 | } |
390 | } |
391 | } |
392 | |
393 | /// Adapter for various expression types. |
394 | /// |
395 | /// Prior to syn 2.0, darling supported arbitrary expressions as long as they |
396 | /// were wrapped in quotation marks. This was helpful for people writing |
397 | /// libraries that needed expressions, but it now creates an ambiguity when |
398 | /// parsing a meta item. |
399 | /// |
400 | /// To address this, the macro supports both formats; if it cannot parse the |
401 | /// item as an expression of the right type and the passed-in expression is |
402 | /// a string literal, it will fall back to parsing the string contents. |
403 | macro_rules! from_syn_expr_type { |
404 | ($ty:path, $variant:ident) => { |
405 | impl FromMeta for $ty { |
406 | fn from_expr(expr: &syn::Expr) -> Result<Self> { |
407 | match expr { |
408 | syn::Expr::$variant(body) => Ok(body.clone()), |
409 | syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit), |
410 | syn::Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr |
411 | _ => Err(Error::unexpected_expr_type(expr)), |
412 | } |
413 | } |
414 | |
415 | fn from_value(value: &::syn::Lit) -> Result<Self> { |
416 | if let syn::Lit::Str(body) = &value { |
417 | body.parse::<$ty>() |
418 | .map_err(|_| Error::unknown_lit_str_value(body)) |
419 | } else { |
420 | Err(Error::unexpected_lit_type(value)) |
421 | } |
422 | } |
423 | } |
424 | }; |
425 | } |
426 | |
427 | from_syn_expr_type!(syn::ExprArray, Array); |
428 | from_syn_expr_type!(syn::ExprPath, Path); |
429 | from_syn_expr_type!(syn::ExprRange, Range); |
430 | |
431 | /// Adapter from `syn::parse::Parse` to `FromMeta` for items that cannot |
432 | /// be expressed in a [`syn::MetaNameValue`]. |
433 | /// |
434 | /// This cannot be a blanket impl, due to the `syn::Lit` family's need to handle non-string values. |
435 | /// Therefore, we use a macro and a lot of impls. |
436 | macro_rules! from_syn_parse { |
437 | ($ty:path) => { |
438 | impl FromMeta for $ty { |
439 | fn from_string(value: &str) -> Result<Self> { |
440 | syn::parse_str(value).map_err(|_| Error::unknown_value(value)) |
441 | } |
442 | |
443 | fn from_value(value: &::syn::Lit) -> Result<Self> { |
444 | if let ::syn::Lit::Str(ref v) = *value { |
445 | v.parse::<$ty>() |
446 | .map_err(|_| Error::unknown_lit_str_value(v)) |
447 | } else { |
448 | Err(Error::unexpected_lit_type(value)) |
449 | } |
450 | } |
451 | } |
452 | }; |
453 | } |
454 | |
455 | from_syn_parse!(syn::Type); |
456 | from_syn_parse!(syn::TypeArray); |
457 | from_syn_parse!(syn::TypeBareFn); |
458 | from_syn_parse!(syn::TypeGroup); |
459 | from_syn_parse!(syn::TypeImplTrait); |
460 | from_syn_parse!(syn::TypeInfer); |
461 | from_syn_parse!(syn::TypeMacro); |
462 | from_syn_parse!(syn::TypeNever); |
463 | from_syn_parse!(syn::TypeParam); |
464 | from_syn_parse!(syn::TypeParen); |
465 | from_syn_parse!(syn::TypePath); |
466 | from_syn_parse!(syn::TypePtr); |
467 | from_syn_parse!(syn::TypeReference); |
468 | from_syn_parse!(syn::TypeSlice); |
469 | from_syn_parse!(syn::TypeTraitObject); |
470 | from_syn_parse!(syn::TypeTuple); |
471 | from_syn_parse!(syn::Visibility); |
472 | from_syn_parse!(syn::WhereClause); |
473 | |
474 | macro_rules! from_numeric_array { |
475 | ($ty:ident) => { |
476 | /// Parsing an unsigned integer array, i.e. `example = "[1, 2, 3, 4]"`. |
477 | impl FromMeta for Vec<$ty> { |
478 | fn from_expr(expr: &syn::Expr) -> Result<Self> { |
479 | match expr { |
480 | syn::Expr::Array(expr_array) => expr_array |
481 | .elems |
482 | .iter() |
483 | .map(|expr| { |
484 | let unexpected = || { |
485 | Error::custom("Expected array of unsigned integers" ).with_span(expr) |
486 | }; |
487 | match expr { |
488 | Expr::Lit(lit) => $ty::from_value(&lit.lit), |
489 | Expr::Group(group) => match &*group.expr { |
490 | Expr::Lit(lit) => $ty::from_value(&lit.lit), |
491 | _ => Err(unexpected()), |
492 | }, |
493 | _ => Err(unexpected()), |
494 | } |
495 | }) |
496 | .collect::<Result<Vec<$ty>>>(), |
497 | syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit), |
498 | syn::Expr::Group(group) => Self::from_expr(&group.expr), // see FromMeta::from_expr |
499 | _ => Err(Error::unexpected_expr_type(expr)), |
500 | } |
501 | } |
502 | |
503 | fn from_value(value: &Lit) -> Result<Self> { |
504 | let expr_array = syn::ExprArray::from_value(value)?; |
505 | Self::from_expr(&syn::Expr::Array(expr_array)) |
506 | } |
507 | } |
508 | }; |
509 | } |
510 | |
511 | from_numeric_array!(u8); |
512 | from_numeric_array!(u16); |
513 | from_numeric_array!(u32); |
514 | from_numeric_array!(u64); |
515 | from_numeric_array!(usize); |
516 | |
517 | impl FromMeta for syn::Lit { |
518 | fn from_value(value: &Lit) -> Result<Self> { |
519 | Ok(value.clone()) |
520 | } |
521 | } |
522 | |
523 | macro_rules! from_meta_lit { |
524 | ($impl_ty:path, $lit_variant:path) => { |
525 | impl FromMeta for $impl_ty { |
526 | fn from_value(value: &Lit) -> Result<Self> { |
527 | if let $lit_variant(ref value) = *value { |
528 | Ok(value.clone()) |
529 | } else { |
530 | Err(Error::unexpected_lit_type(value)) |
531 | } |
532 | } |
533 | } |
534 | |
535 | impl FromMeta for Vec<$impl_ty> { |
536 | fn from_list(items: &[NestedMeta]) -> Result<Self> { |
537 | items |
538 | .iter() |
539 | .map(<$impl_ty as FromMeta>::from_nested_meta) |
540 | .collect() |
541 | } |
542 | |
543 | fn from_value(value: &syn::Lit) -> Result<Self> { |
544 | let expr_array = syn::ExprArray::from_value(value)?; |
545 | Self::from_expr(&syn::Expr::Array(expr_array)) |
546 | } |
547 | |
548 | fn from_expr(expr: &syn::Expr) -> Result<Self> { |
549 | match expr { |
550 | syn::Expr::Array(expr_array) => expr_array |
551 | .elems |
552 | .iter() |
553 | .map(<$impl_ty as FromMeta>::from_expr) |
554 | .collect::<Result<Vec<_>>>(), |
555 | syn::Expr::Lit(expr_lit) => Self::from_value(&expr_lit.lit), |
556 | syn::Expr::Group(g) => Self::from_expr(&g.expr), |
557 | _ => Err(Error::unexpected_expr_type(expr)), |
558 | } |
559 | } |
560 | } |
561 | }; |
562 | } |
563 | |
564 | from_meta_lit!(syn::LitInt, Lit::Int); |
565 | from_meta_lit!(syn::LitFloat, Lit::Float); |
566 | from_meta_lit!(syn::LitStr, Lit::Str); |
567 | from_meta_lit!(syn::LitByte, Lit::Byte); |
568 | from_meta_lit!(syn::LitByteStr, Lit::ByteStr); |
569 | from_meta_lit!(syn::LitChar, Lit::Char); |
570 | from_meta_lit!(syn::LitBool, Lit::Bool); |
571 | from_meta_lit!(proc_macro2::Literal, Lit::Verbatim); |
572 | |
573 | impl FromMeta for syn::Meta { |
574 | fn from_meta(value: &syn::Meta) -> Result<Self> { |
575 | Ok(value.clone()) |
576 | } |
577 | } |
578 | |
579 | impl FromMeta for Vec<syn::WherePredicate> { |
580 | fn from_string(value: &str) -> Result<Self> { |
581 | syn::WhereClause::from_string(&format!("where {}" , value)) |
582 | .map(|c: WhereClause| c.predicates.into_iter().collect()) |
583 | } |
584 | |
585 | fn from_value(value: &Lit) -> Result<Self> { |
586 | if let syn::Lit::Str(s: &LitStr) = value { |
587 | syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new( |
588 | &format!("where {}" , s.value()), |
589 | value.span(), |
590 | ))) |
591 | .map(|c: WhereClause| c.predicates.into_iter().collect()) |
592 | } else { |
593 | Err(Error::unexpected_lit_type(lit:value)) |
594 | } |
595 | } |
596 | } |
597 | |
598 | impl FromMeta for ident_case::RenameRule { |
599 | fn from_string(value: &str) -> Result<Self> { |
600 | value.parse().map_err(|_| Error::unknown_value(value)) |
601 | } |
602 | } |
603 | |
604 | impl<T: FromMeta> FromMeta for Option<T> { |
605 | fn from_none() -> Option<Self> { |
606 | Some(None) |
607 | } |
608 | |
609 | fn from_meta(item: &Meta) -> Result<Self> { |
610 | FromMeta::from_meta(item).map(op:Some) |
611 | } |
612 | } |
613 | |
614 | impl<T: FromMeta> FromMeta for Result<T> { |
615 | fn from_none() -> Option<Self> { |
616 | T::from_none().map(Ok) |
617 | } |
618 | |
619 | // `#[darling(flatten)]` forwards directly to this method, so it's |
620 | // necessary to declare it to avoid getting an unsupported format |
621 | // error if it's invoked directly. |
622 | fn from_list(items: &[NestedMeta]) -> Result<Self> { |
623 | Ok(FromMeta::from_list(items)) |
624 | } |
625 | |
626 | fn from_meta(item: &Meta) -> Result<Self> { |
627 | Ok(FromMeta::from_meta(item)) |
628 | } |
629 | } |
630 | |
631 | /// Create an impl that forwards to an inner type `T` for parsing. |
632 | macro_rules! smart_pointer_t { |
633 | ($ty:path, $map_fn:path) => { |
634 | impl<T: FromMeta> FromMeta for $ty { |
635 | fn from_none() -> Option<Self> { |
636 | T::from_none().map($map_fn) |
637 | } |
638 | |
639 | // `#[darling(flatten)]` forwards directly to this method, so it's |
640 | // necessary to declare it to avoid getting an unsupported format |
641 | // error if it's invoked directly. |
642 | fn from_list(items: &[NestedMeta]) -> Result<Self> { |
643 | FromMeta::from_list(items).map($map_fn) |
644 | } |
645 | |
646 | fn from_meta(item: &Meta) -> Result<Self> { |
647 | FromMeta::from_meta(item).map($map_fn) |
648 | } |
649 | } |
650 | }; |
651 | } |
652 | |
653 | smart_pointer_t!(Box<T>, Box::new); |
654 | smart_pointer_t!(Rc<T>, Rc::new); |
655 | smart_pointer_t!(Arc<T>, Arc::new); |
656 | smart_pointer_t!(RefCell<T>, RefCell::new); |
657 | |
658 | /// Parses the meta-item, and in case of error preserves a copy of the input for |
659 | /// later analysis. |
660 | impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> { |
661 | fn from_meta(item: &Meta) -> Result<Self> { |
662 | T::from_meta(item) |
663 | .map(Ok) |
664 | .or_else(|_| Ok(Err(item.clone()))) |
665 | } |
666 | } |
667 | |
668 | /// Trait to convert from a path into an owned key for a map. |
669 | trait KeyFromPath: Sized { |
670 | fn from_path(path: &syn::Path) -> Result<Self>; |
671 | fn to_display(&self) -> Cow<'_, str>; |
672 | } |
673 | |
674 | impl KeyFromPath for String { |
675 | fn from_path(path: &syn::Path) -> Result<Self> { |
676 | Ok(path_to_string(path)) |
677 | } |
678 | |
679 | fn to_display(&self) -> Cow<'_, str> { |
680 | Cow::Borrowed(self) |
681 | } |
682 | } |
683 | |
684 | impl KeyFromPath for syn::Path { |
685 | fn from_path(path: &syn::Path) -> Result<Self> { |
686 | Ok(path.clone()) |
687 | } |
688 | |
689 | fn to_display(&self) -> Cow<'_, str> { |
690 | Cow::Owned(path_to_string(self)) |
691 | } |
692 | } |
693 | |
694 | impl KeyFromPath for syn::Ident { |
695 | fn from_path(path: &syn::Path) -> Result<Self> { |
696 | if path.segments.len() == 1 |
697 | && path.leading_colon.is_none() |
698 | && path.segments[0].arguments.is_empty() |
699 | { |
700 | Ok(path.segments[0].ident.clone()) |
701 | } else { |
702 | Err(Error::custom("Key must be an identifier" ).with_span(node:path)) |
703 | } |
704 | } |
705 | |
706 | fn to_display(&self) -> Cow<'_, str> { |
707 | Cow::Owned(self.to_string()) |
708 | } |
709 | } |
710 | |
711 | macro_rules! map { |
712 | (hash_map, $key:ty, $nested:ident) => { |
713 | impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> { |
714 | map!( |
715 | HashMap::with_capacity_and_hasher($nested.len(), Default::default()), |
716 | $key, |
717 | $nested |
718 | ); |
719 | } |
720 | }; |
721 | |
722 | (btree_map, $key:ty, $nested:ident) => { |
723 | impl<V: FromMeta> FromMeta for BTreeMap<$key, V> { |
724 | map!(BTreeMap::new(), $key, $nested); |
725 | } |
726 | }; |
727 | |
728 | ($new:expr, $key:ty, $nested:ident) => { |
729 | fn from_list($nested: &[NestedMeta]) -> Result<Self> { |
730 | // Convert the nested meta items into a sequence of (path, value result) result tuples. |
731 | // An outer Err means no (key, value) structured could be found, while an Err in the |
732 | // second position of the tuple means that value was rejected by FromMeta. |
733 | // |
734 | // We defer key conversion into $key so that we don't lose span information in the case |
735 | // of String keys; we'll need it for good duplicate key errors later. |
736 | let pairs = $nested |
737 | .iter() |
738 | .map(|item| -> Result<(&syn::Path, Result<V>)> { |
739 | match *item { |
740 | NestedMeta::Meta(ref inner) => { |
741 | let path = inner.path(); |
742 | Ok(( |
743 | path, |
744 | FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)), |
745 | )) |
746 | } |
747 | NestedMeta::Lit(_) => Err(Error::unsupported_format("expression" )), |
748 | } |
749 | }); |
750 | |
751 | let mut errors = Error::accumulator(); |
752 | // We need to track seen keys separately from the final map, since a seen key with an |
753 | // Err value won't go into the final map but should trigger a duplicate field error. |
754 | // |
755 | // This is a set of $key rather than Path to avoid the possibility that a key type |
756 | // parses two paths of different values to the same key value. |
757 | let mut seen_keys = HashSet::with_capacity($nested.len()); |
758 | |
759 | // The map to return in the Ok case. Its size will always be exactly nested.len(), |
760 | // since otherwise ≥1 field had a problem and the entire map is dropped immediately |
761 | // when the function returns `Err`. |
762 | let mut map = $new; |
763 | |
764 | for item in pairs { |
765 | if let Some((path, value)) = errors.handle(item) { |
766 | let key: $key = match KeyFromPath::from_path(path) { |
767 | Ok(k) => k, |
768 | Err(e) => { |
769 | errors.push(e); |
770 | |
771 | // Surface value errors even under invalid keys |
772 | errors.handle(value); |
773 | |
774 | continue; |
775 | } |
776 | }; |
777 | |
778 | let already_seen = seen_keys.contains(&key); |
779 | |
780 | if already_seen { |
781 | errors.push(Error::duplicate_field(&key.to_display()).with_span(path)); |
782 | } |
783 | |
784 | match value { |
785 | Ok(_) if already_seen => {} |
786 | Ok(val) => { |
787 | map.insert(key.clone(), val); |
788 | } |
789 | Err(e) => { |
790 | errors.push(e); |
791 | } |
792 | } |
793 | |
794 | seen_keys.insert(key); |
795 | } |
796 | } |
797 | |
798 | errors.finish_with(map) |
799 | } |
800 | }; |
801 | } |
802 | |
803 | // This is done as a macro rather than a blanket impl to avoid breaking backwards compatibility |
804 | // with 0.12.x, while still sharing the same impl. |
805 | map!(hash_map, String, nested); |
806 | map!(hash_map, syn::Ident, nested); |
807 | map!(hash_map, syn::Path, nested); |
808 | |
809 | map!(btree_map, String, nested); |
810 | map!(btree_map, syn::Ident, nested); |
811 | |
812 | /// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input, |
813 | /// it should not be considered by the parsing. |
814 | #[cfg (test)] |
815 | mod tests { |
816 | use std::num::{NonZeroU32, NonZeroU64}; |
817 | |
818 | use proc_macro2::TokenStream; |
819 | use quote::quote; |
820 | use syn::parse_quote; |
821 | |
822 | use crate::{Error, FromMeta, Result}; |
823 | |
824 | /// parse a string as a syn::Meta instance. |
825 | fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> { |
826 | let attribute: syn::Attribute = parse_quote!(#[#tokens]); |
827 | Ok(attribute.meta) |
828 | } |
829 | |
830 | #[track_caller ] |
831 | fn fm<T: FromMeta>(tokens: TokenStream) -> T { |
832 | FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input" )) |
833 | .expect("Tests should pass valid input" ) |
834 | } |
835 | |
836 | #[test ] |
837 | fn unit_succeeds() { |
838 | fm::<()>(quote!(ignore)); |
839 | } |
840 | |
841 | #[test ] |
842 | #[allow (clippy::bool_assert_comparison)] |
843 | fn bool_succeeds() { |
844 | // word format |
845 | assert_eq!(fm::<bool>(quote!(ignore)), true); |
846 | |
847 | // bool literal |
848 | assert_eq!(fm::<bool>(quote!(ignore = true)), true); |
849 | assert_eq!(fm::<bool>(quote!(ignore = false)), false); |
850 | |
851 | // string literals |
852 | assert_eq!(fm::<bool>(quote!(ignore = "true" )), true); |
853 | assert_eq!(fm::<bool>(quote!(ignore = "false" )), false); |
854 | } |
855 | |
856 | #[test ] |
857 | fn char_succeeds() { |
858 | // char literal |
859 | assert_eq!(fm::<char>(quote!(ignore = '😬' )), '😬' ); |
860 | |
861 | // string literal |
862 | assert_eq!(fm::<char>(quote!(ignore = "😬" )), '😬' ); |
863 | } |
864 | |
865 | #[test ] |
866 | fn string_succeeds() { |
867 | // cooked form |
868 | assert_eq!(&fm::<String>(quote!(ignore = "world" )), "world" ); |
869 | |
870 | // raw form |
871 | assert_eq!(&fm::<String>(quote!(ignore = r#"world"# )), "world" ); |
872 | } |
873 | |
874 | #[test ] |
875 | fn pathbuf_succeeds() { |
876 | assert_eq!( |
877 | fm::<std::path::PathBuf>(quote!(ignore = r#"C:\"# )), |
878 | std::path::PathBuf::from(r#"C:\"# ) |
879 | ); |
880 | } |
881 | |
882 | #[test ] |
883 | #[allow (clippy::float_cmp)] // we want exact equality |
884 | fn number_succeeds() { |
885 | assert_eq!(fm::<u8>(quote!(ignore = "2" )), 2u8); |
886 | assert_eq!(fm::<i16>(quote!(ignore = "-25" )), -25i16); |
887 | assert_eq!(fm::<f64>(quote!(ignore = "1.4e10" )), 1.4e10); |
888 | } |
889 | |
890 | #[should_panic (expected = "UnknownValue( \"0 \")" )] |
891 | #[test ] |
892 | fn nonzero_number_fails() { |
893 | fm::<NonZeroU64>(quote!(ignore = "0" )); |
894 | } |
895 | |
896 | #[test ] |
897 | fn nonzero_number_succeeds() { |
898 | assert_eq!( |
899 | fm::<NonZeroU32>(quote!(ignore = "2" )), |
900 | NonZeroU32::new(2).unwrap() |
901 | ); |
902 | } |
903 | |
904 | #[test ] |
905 | fn int_without_quotes() { |
906 | assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8); |
907 | assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16); |
908 | assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32); |
909 | |
910 | // Check that we aren't tripped up by incorrect suffixes |
911 | assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32); |
912 | } |
913 | |
914 | #[test ] |
915 | fn negative_int_without_quotes() { |
916 | assert_eq!(fm::<i8>(quote!(ignore = -2)), -2i8); |
917 | assert_eq!(fm::<i32>(quote!(ignore = -255)), -255i32); |
918 | } |
919 | |
920 | #[test ] |
921 | #[allow (clippy::float_cmp)] // we want exact equality |
922 | fn float_without_quotes() { |
923 | assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32); |
924 | assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32); |
925 | assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64); |
926 | } |
927 | |
928 | #[test ] |
929 | fn too_large_int_produces_error() { |
930 | assert!(fm::<Result<u8>>(quote!(ignore = 2000)).is_err()); |
931 | } |
932 | |
933 | #[test ] |
934 | fn meta_succeeds() { |
935 | use syn::Meta; |
936 | |
937 | assert_eq!( |
938 | fm::<Meta>(quote!(hello(world, today))), |
939 | pm(quote!(hello(world, today))).unwrap() |
940 | ); |
941 | } |
942 | |
943 | #[test ] |
944 | fn hash_map_succeeds() { |
945 | use std::collections::HashMap; |
946 | |
947 | let comparison = { |
948 | let mut c = HashMap::new(); |
949 | c.insert("hello" .to_string(), true); |
950 | c.insert("world" .to_string(), false); |
951 | c.insert("there" .to_string(), true); |
952 | c |
953 | }; |
954 | |
955 | assert_eq!( |
956 | fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true" ))), |
957 | comparison |
958 | ); |
959 | } |
960 | |
961 | /// Check that a `HashMap` cannot have duplicate keys, and that the generated error |
962 | /// is assigned a span to correctly target the diagnostic message. |
963 | #[test ] |
964 | fn hash_map_duplicate() { |
965 | use std::collections::HashMap; |
966 | |
967 | let err: Result<HashMap<String, bool>> = |
968 | FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap()); |
969 | |
970 | let err = err.expect_err("Duplicate keys in HashMap should error" ); |
971 | |
972 | assert!(err.has_span()); |
973 | assert_eq!(err.to_string(), Error::duplicate_field("hello" ).to_string()); |
974 | } |
975 | |
976 | #[test ] |
977 | fn hash_map_multiple_errors() { |
978 | use std::collections::HashMap; |
979 | |
980 | let err = HashMap::<String, bool>::from_meta( |
981 | &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(), |
982 | ) |
983 | .expect_err("Duplicates and bad values should error" ); |
984 | |
985 | assert_eq!(err.len(), 3); |
986 | let errors = err.into_iter().collect::<Vec<_>>(); |
987 | assert!(errors[0].has_span()); |
988 | assert!(errors[1].has_span()); |
989 | assert!(errors[2].has_span()); |
990 | } |
991 | |
992 | #[test ] |
993 | fn hash_map_ident_succeeds() { |
994 | use std::collections::HashMap; |
995 | use syn::parse_quote; |
996 | |
997 | let comparison = { |
998 | let mut c = HashMap::<syn::Ident, bool>::new(); |
999 | c.insert(parse_quote!(first), true); |
1000 | c.insert(parse_quote!(second), false); |
1001 | c |
1002 | }; |
1003 | |
1004 | assert_eq!( |
1005 | fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))), |
1006 | comparison |
1007 | ); |
1008 | } |
1009 | |
1010 | #[test ] |
1011 | fn hash_map_ident_rejects_non_idents() { |
1012 | use std::collections::HashMap; |
1013 | |
1014 | let err: Result<HashMap<syn::Ident, bool>> = |
1015 | FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap()); |
1016 | |
1017 | err.unwrap_err(); |
1018 | } |
1019 | |
1020 | #[test ] |
1021 | fn hash_map_path_succeeds() { |
1022 | use std::collections::HashMap; |
1023 | use syn::parse_quote; |
1024 | |
1025 | let comparison = { |
1026 | let mut c = HashMap::<syn::Path, bool>::new(); |
1027 | c.insert(parse_quote!(first), true); |
1028 | c.insert(parse_quote!(the::second), false); |
1029 | c |
1030 | }; |
1031 | |
1032 | assert_eq!( |
1033 | fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))), |
1034 | comparison |
1035 | ); |
1036 | } |
1037 | |
1038 | #[test ] |
1039 | fn btree_map_succeeds() { |
1040 | use std::collections::BTreeMap; |
1041 | |
1042 | let comparison = { |
1043 | let mut c = BTreeMap::new(); |
1044 | c.insert("hello" .to_string(), true); |
1045 | c.insert("world" .to_string(), false); |
1046 | c.insert("there" .to_string(), true); |
1047 | c |
1048 | }; |
1049 | |
1050 | assert_eq!( |
1051 | fm::<BTreeMap<String, bool>>(quote!(ignore(hello, world = false, there = "true" ))), |
1052 | comparison |
1053 | ); |
1054 | } |
1055 | |
1056 | /// Check that a `HashMap` cannot have duplicate keys, and that the generated error |
1057 | /// is assigned a span to correctly target the diagnostic message. |
1058 | #[test ] |
1059 | fn btree_map_duplicate() { |
1060 | use std::collections::BTreeMap; |
1061 | |
1062 | let err: Result<BTreeMap<String, bool>> = |
1063 | FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap()); |
1064 | |
1065 | let err = err.expect_err("Duplicate keys in BTreeMap should error" ); |
1066 | |
1067 | assert!(err.has_span()); |
1068 | assert_eq!(err.to_string(), Error::duplicate_field("hello" ).to_string()); |
1069 | } |
1070 | |
1071 | #[test ] |
1072 | fn btree_map_multiple_errors() { |
1073 | use std::collections::BTreeMap; |
1074 | |
1075 | let err = BTreeMap::<String, bool>::from_meta( |
1076 | &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(), |
1077 | ) |
1078 | .expect_err("Duplicates and bad values should error" ); |
1079 | |
1080 | assert_eq!(err.len(), 3); |
1081 | let errors = err.into_iter().collect::<Vec<_>>(); |
1082 | assert!(errors[0].has_span()); |
1083 | assert!(errors[1].has_span()); |
1084 | assert!(errors[2].has_span()); |
1085 | } |
1086 | |
1087 | #[test ] |
1088 | fn btree_map_ident_succeeds() { |
1089 | use std::collections::BTreeMap; |
1090 | use syn::parse_quote; |
1091 | |
1092 | let comparison = { |
1093 | let mut c = BTreeMap::<syn::Ident, bool>::new(); |
1094 | c.insert(parse_quote!(first), true); |
1095 | c.insert(parse_quote!(second), false); |
1096 | c |
1097 | }; |
1098 | |
1099 | assert_eq!( |
1100 | fm::<BTreeMap<syn::Ident, bool>>(quote!(ignore(first, second = false))), |
1101 | comparison |
1102 | ); |
1103 | } |
1104 | |
1105 | #[test ] |
1106 | fn btree_map_ident_rejects_non_idents() { |
1107 | use std::collections::BTreeMap; |
1108 | |
1109 | let err: Result<BTreeMap<syn::Ident, bool>> = |
1110 | FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap()); |
1111 | |
1112 | err.unwrap_err(); |
1113 | } |
1114 | |
1115 | #[test ] |
1116 | fn btree_map_expr_values_succeed() { |
1117 | use std::collections::BTreeMap; |
1118 | use syn::parse_quote; |
1119 | |
1120 | let comparison: BTreeMap<String, syn::Expr> = vec![ |
1121 | ("hello" , parse_quote!(2 + 2)), |
1122 | ("world" , parse_quote!(x.foo())), |
1123 | ] |
1124 | .into_iter() |
1125 | .map(|(k, v)| (k.to_string(), v)) |
1126 | .collect(); |
1127 | |
1128 | assert_eq!( |
1129 | fm::<BTreeMap<String, syn::Expr>>(quote!(ignore(hello = 2 + 2, world = x.foo()))), |
1130 | comparison |
1131 | ); |
1132 | } |
1133 | |
1134 | /// Tests that fallible parsing will always produce an outer `Ok` (from `fm`), |
1135 | /// and will accurately preserve the inner contents. |
1136 | #[test ] |
1137 | fn darling_result_succeeds() { |
1138 | fm::<Result<()>>(quote!(ignore)).unwrap(); |
1139 | fm::<Result<()>>(quote!(ignore(world))).unwrap_err(); |
1140 | } |
1141 | |
1142 | /// Test punctuated |
1143 | #[test ] |
1144 | fn test_punctuated() { |
1145 | fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!( |
1146 | ignore = "a: u8, b: Type" |
1147 | )); |
1148 | fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c" )); |
1149 | } |
1150 | |
1151 | #[test ] |
1152 | fn test_expr_array() { |
1153 | fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]" )); |
1154 | fm::<syn::ExprArray>(quote!(ignore = "[ \"Hello World \", \"Test Array \"]" )); |
1155 | } |
1156 | |
1157 | #[test ] |
1158 | fn test_expr() { |
1159 | fm::<syn::Expr>(quote!(ignore = "x + y" )); |
1160 | fm::<syn::Expr>(quote!(ignore = "an_object.method_call()" )); |
1161 | fm::<syn::Expr>(quote!(ignore = "{ a_statement(); in_a_block }" )); |
1162 | } |
1163 | |
1164 | #[test ] |
1165 | fn test_expr_without_quotes() { |
1166 | fm::<syn::Expr>(quote!(ignore = x + y)); |
1167 | fm::<syn::Expr>(quote!(ignore = an_object.method_call())); |
1168 | fm::<syn::Expr>(quote!( |
1169 | ignore = { |
1170 | a_statement(); |
1171 | in_a_block |
1172 | } |
1173 | )); |
1174 | } |
1175 | |
1176 | #[test ] |
1177 | fn test_expr_path() { |
1178 | fm::<syn::ExprPath>(quote!(ignore = "std::mem::replace" )); |
1179 | fm::<syn::ExprPath>(quote!(ignore = "x" )); |
1180 | fm::<syn::ExprPath>(quote!(ignore = "example::<Test>" )); |
1181 | } |
1182 | |
1183 | #[test ] |
1184 | fn test_expr_path_without_quotes() { |
1185 | fm::<syn::ExprPath>(quote!(ignore = std::mem::replace)); |
1186 | fm::<syn::ExprPath>(quote!(ignore = x)); |
1187 | fm::<syn::ExprPath>(quote!(ignore = example::<Test>)); |
1188 | } |
1189 | |
1190 | #[test ] |
1191 | fn test_path_without_quotes() { |
1192 | fm::<syn::Path>(quote!(ignore = std::mem::replace)); |
1193 | fm::<syn::Path>(quote!(ignore = x)); |
1194 | fm::<syn::Path>(quote!(ignore = example::<Test>)); |
1195 | } |
1196 | |
1197 | #[test ] |
1198 | fn test_number_array() { |
1199 | assert_eq!(fm::<Vec<u8>>(quote!(ignore = [16, 0xff])), vec![0x10, 0xff]); |
1200 | assert_eq!( |
1201 | fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]" )), |
1202 | vec![0x20, 0xffff] |
1203 | ); |
1204 | assert_eq!( |
1205 | fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]" )), |
1206 | vec![0x30, 0xffffffff] |
1207 | ); |
1208 | assert_eq!( |
1209 | fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]" )), |
1210 | vec![0x40, 0xffffffffffffffff] |
1211 | ); |
1212 | assert_eq!( |
1213 | fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]" )), |
1214 | vec![0x50, 0xffffffff] |
1215 | ); |
1216 | } |
1217 | |
1218 | #[test ] |
1219 | fn test_lit_array() { |
1220 | fm::<Vec<syn::LitStr>>(quote!(ignore = "[ \"Hello World \", \"Test Array \"]" )); |
1221 | fm::<Vec<syn::LitStr>>(quote!(ignore = ["Hello World" , "Test Array" ])); |
1222 | fm::<Vec<syn::LitChar>>(quote!(ignore = "['a', 'b', 'c']" )); |
1223 | fm::<Vec<syn::LitBool>>(quote!(ignore = "[true]" )); |
1224 | fm::<Vec<syn::LitStr>>(quote!(ignore = "[]" )); |
1225 | fm::<Vec<syn::LitStr>>(quote!(ignore = [])); |
1226 | fm::<Vec<syn::LitBool>>(quote!(ignore = [true, false])); |
1227 | } |
1228 | |
1229 | #[test ] |
1230 | fn expr_range_without_quotes() { |
1231 | fm::<syn::ExprRange>(quote!(ignore = 0..5)); |
1232 | fm::<syn::ExprRange>(quote!(ignore = 0..=5)); |
1233 | fm::<syn::ExprRange>(quote!(ignore = ..5)); |
1234 | fm::<syn::ExprRange>(quote!(ignore = ..(x + y))); |
1235 | } |
1236 | } |
1237 | |