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