darling_core/error/mod.rs
1//! The `darling::Error` type, the multiple error `Accumulator`, and their internals.
2//!
3//! Error handling is one of the core values of `darling`; creating great errors is hard and
4//! never the reason that a proc-macro author started writing their crate. As a result, the
5//! `Error` type in `darling` tries to make adding span information, suggestions, and other
6//! help content easy when manually implementing `darling` traits, and automatic when deriving
7//! them.
8
9use proc_macro2::{Span, TokenStream};
10use std::error::Error as StdError;
11use std::fmt;
12use std::iter::{self, Iterator};
13use std::string::ToString;
14use std::vec;
15use syn::spanned::Spanned;
16use syn::{Expr, Lit, LitStr, Path};
17
18#[cfg(feature = "diagnostics")]
19mod child;
20mod kind;
21mod util;
22
23use crate::util::path_to_string;
24
25use self::kind::{ErrorKind, ErrorUnknownValue, UnknownValuePosition};
26
27/// An alias of `Result` specific to attribute parsing.
28pub type Result<T> = ::std::result::Result<T, Error>;
29
30/// An error encountered during attribute parsing.
31///
32/// Given that most errors darling encounters represent code bugs in dependent crates,
33/// the internal structure of the error is deliberately opaque.
34///
35/// # Usage
36/// Proc-macro expansion happens very infrequently compared to runtime tasks such as
37/// deserialization, and it happens in the context of an expensive compilation taks.
38/// For that reason, darling prefers not to fail on the first error it encounters, instead
39/// doing as much work as it can, accumulating errors into a single report.
40///
41/// As a result, `darling::Error` is more of guaranteed-non-empty error collection
42/// than a single problem. These errors also have some notion of hierarchy, stemming from
43/// the hierarchical nature of darling's input.
44///
45/// These characteristics make for great experiences when using darling-powered crates,
46/// provided crates using darling adhere to some best practices:
47///
48/// 1. Do not attempt to simplify a `darling::Error` into some other error type, such as
49/// `syn::Error`. To surface compile errors, instead use `darling::Error::write_errors`.
50/// This preserves all span information, suggestions, etc. Wrapping a `darling::Error` in
51/// a custom error enum works as-expected and does not force any loss of fidelity.
52/// 2. Do not use early return (e.g. the `?` operator) for custom validations. Instead,
53/// create an [`error::Accumulator`](Accumulator) to collect errors as they are encountered. Then use
54/// [`Accumulator::finish`] to return your validated result; it will give `Ok` if and only if
55/// no errors were encountered. This can create very complex custom validation functions;
56/// in those cases, split independent "validation chains" out into their own functions to
57/// keep the main validator manageable.
58/// 3. Use `darling::Error::custom` to create additional errors as-needed, then call `with_span`
59/// to ensure those errors appear in the right place. Use `darling::util::SpannedValue` to keep
60/// span information around on parsed fields so that custom diagnostics can point to the correct
61/// parts of the input AST.
62#[derive(Debug, Clone)]
63pub struct Error {
64 kind: ErrorKind,
65 locations: Vec<String>,
66 /// The span to highlight in the emitted diagnostic.
67 span: Option<Span>,
68 /// Additional diagnostic messages to show with the error.
69 #[cfg(feature = "diagnostics")]
70 children: Vec<child::ChildDiagnostic>,
71}
72
73/// Error creation functions
74impl Error {
75 pub(in crate::error) fn new(kind: ErrorKind) -> Self {
76 Error {
77 kind,
78 locations: Vec::new(),
79 span: None,
80 #[cfg(feature = "diagnostics")]
81 children: vec![],
82 }
83 }
84
85 /// Creates a new error with a custom message.
86 pub fn custom<T: fmt::Display>(msg: T) -> Self {
87 Error::new(ErrorKind::Custom(msg.to_string()))
88 }
89
90 /// Creates a new error for a field that appears twice in the input.
91 pub fn duplicate_field(name: &str) -> Self {
92 Error::new(ErrorKind::DuplicateField(name.into()))
93 }
94
95 /// Creates a new error for a field that appears twice in the input. Helper to avoid repeating
96 /// the syn::Path to String conversion.
97 pub fn duplicate_field_path(path: &Path) -> Self {
98 Error::duplicate_field(&path_to_string(path))
99 }
100
101 /// Creates a new error for a non-optional field that does not appear in the input.
102 pub fn missing_field(name: &str) -> Self {
103 Error::new(ErrorKind::MissingField(name.into()))
104 }
105
106 /// Creates a new error for a field name that appears in the input but does not correspond
107 /// to a known field.
108 pub fn unknown_field(name: &str) -> Self {
109 Error::new(ErrorUnknownValue::new(UnknownValuePosition::Field, name).into())
110 }
111
112 /// Creates a new error for a field name that appears in the input but does not correspond
113 /// to a known field. Helper to avoid repeating the syn::Path to String conversion.
114 pub fn unknown_field_path(path: &Path) -> Self {
115 Error::unknown_field(&path_to_string(path))
116 }
117
118 /// Creates a new error for a field name that appears in the input but does not correspond to
119 /// a known attribute. The second argument is the list of known attributes; if a similar name
120 /// is found that will be shown in the emitted error message.
121 pub fn unknown_field_with_alts<'a, T, I>(field: &str, alternates: I) -> Self
122 where
123 T: AsRef<str> + 'a,
124 I: IntoIterator<Item = &'a T>,
125 {
126 Error::new(
127 ErrorUnknownValue::with_alts(UnknownValuePosition::Field, field, alternates).into(),
128 )
129 }
130
131 /// Creates a new error for a field name that appears in the input but does not correspond to
132 /// a known attribute. The second argument is the list of known attributes; if a similar name
133 /// is found that will be shown in the emitted error message.
134 pub fn unknown_field_path_with_alts<'a, T, I>(field: &Path, alternates: I) -> Self
135 where
136 T: AsRef<str> + 'a,
137 I: IntoIterator<Item = &'a T>,
138 {
139 Error::new(ErrorKind::UnknownField(
140 ErrorUnknownValue::with_alts(
141 UnknownValuePosition::Field,
142 &path_to_string(field),
143 alternates,
144 )
145 .into(),
146 ))
147 }
148
149 /// Creates a new error for a struct or variant that does not adhere to the supported shape.
150 pub fn unsupported_shape(shape: &str) -> Self {
151 Error::new(ErrorKind::UnsupportedShape {
152 observed: shape.into(),
153 expected: None,
154 })
155 }
156
157 pub fn unsupported_shape_with_expected<T: fmt::Display>(shape: &str, expected: &T) -> Self {
158 Error::new(ErrorKind::UnsupportedShape {
159 observed: shape.into(),
160 expected: Some(expected.to_string()),
161 })
162 }
163
164 pub fn unsupported_format(format: &str) -> Self {
165 Error::new(ErrorKind::UnexpectedFormat(format.into()))
166 }
167
168 /// Creates a new error for a field which has an unexpected literal type.
169 pub fn unexpected_type(ty: &str) -> Self {
170 Error::new(ErrorKind::UnexpectedType(ty.into()))
171 }
172
173 pub fn unexpected_expr_type(expr: &Expr) -> Self {
174 Error::unexpected_type(match *expr {
175 Expr::Array(_) => "array",
176 Expr::Assign(_) => "assign",
177 Expr::Async(_) => "async",
178 Expr::Await(_) => "await",
179 Expr::Binary(_) => "binary",
180 Expr::Block(_) => "block",
181 Expr::Break(_) => "break",
182 Expr::Call(_) => "call",
183 Expr::Cast(_) => "cast",
184 Expr::Closure(_) => "closure",
185 Expr::Const(_) => "const",
186 Expr::Continue(_) => "continue",
187 Expr::Field(_) => "field",
188 Expr::ForLoop(_) => "for_loop",
189 Expr::Group(_) => "group",
190 Expr::If(_) => "if",
191 Expr::Index(_) => "index",
192 Expr::Infer(_) => "infer",
193 Expr::Let(_) => "let",
194 Expr::Lit(_) => "lit",
195 Expr::Loop(_) => "loop",
196 Expr::Macro(_) => "macro",
197 Expr::Match(_) => "match",
198 Expr::MethodCall(_) => "method_call",
199 Expr::Paren(_) => "paren",
200 Expr::Path(_) => "path",
201 Expr::Range(_) => "range",
202 Expr::Reference(_) => "reference",
203 Expr::Repeat(_) => "repeat",
204 Expr::Return(_) => "return",
205 Expr::Struct(_) => "struct",
206 Expr::Try(_) => "try",
207 Expr::TryBlock(_) => "try_block",
208 Expr::Tuple(_) => "tuple",
209 Expr::Unary(_) => "unary",
210 Expr::Unsafe(_) => "unsafe",
211 Expr::Verbatim(_) => "verbatim",
212 Expr::While(_) => "while",
213 Expr::Yield(_) => "yield",
214 // non-exhaustive enum
215 _ => "unknown",
216 })
217 .with_span(expr)
218 }
219
220 /// Creates a new error for a field which has an unexpected literal type. This will automatically
221 /// extract the literal type name from the passed-in `Lit` and set the span to encompass only the
222 /// literal value.
223 ///
224 /// # Usage
225 /// This is most frequently used in overrides of the `FromMeta::from_value` method.
226 ///
227 /// ```rust
228 /// # // pretend darling_core is darling so the doc example looks correct.
229 /// # extern crate darling_core as darling;
230 /// # extern crate syn;
231 ///
232 /// use darling::{FromMeta, Error, Result};
233 /// use syn::{Lit, LitStr};
234 ///
235 /// pub struct Foo(String);
236 ///
237 /// impl FromMeta for Foo {
238 /// fn from_value(value: &Lit) -> Result<Self> {
239 /// if let Lit::Str(ref lit_str) = *value {
240 /// Ok(Foo(lit_str.value()))
241 /// } else {
242 /// Err(Error::unexpected_lit_type(value))
243 /// }
244 /// }
245 /// }
246 ///
247 /// # fn main() {}
248 /// ```
249 pub fn unexpected_lit_type(lit: &Lit) -> Self {
250 Error::unexpected_type(match *lit {
251 Lit::Str(_) => "string",
252 Lit::ByteStr(_) => "byte string",
253 Lit::Byte(_) => "byte",
254 Lit::Char(_) => "char",
255 Lit::Int(_) => "int",
256 Lit::Float(_) => "float",
257 Lit::Bool(_) => "bool",
258 Lit::Verbatim(_) => "verbatim",
259 // non-exhaustive enum
260 _ => "unknown",
261 })
262 .with_span(lit)
263 }
264
265 /// Creates a new error for a value which doesn't match a set of expected literals.
266 pub fn unknown_value(value: &str) -> Self {
267 Error::new(ErrorUnknownValue::new(UnknownValuePosition::Value, value).into())
268 }
269
270 pub fn unknown_value_with_alts<'a, T, I>(value: &str, alternates: I) -> Self
271 where
272 T: AsRef<str> + 'a,
273 I: IntoIterator<Item = &'a T>,
274 {
275 Error::new(
276 ErrorUnknownValue::with_alts(UnknownValuePosition::Value, value, alternates).into(),
277 )
278 }
279
280 /// Creates a new error for a list which did not get enough items to proceed.
281 pub fn too_few_items(min: usize) -> Self {
282 Error::new(ErrorKind::TooFewItems(min))
283 }
284
285 /// Creates a new error when a list got more items than it supports. The `max` argument
286 /// is the largest number of items the receiver could accept.
287 pub fn too_many_items(max: usize) -> Self {
288 Error::new(ErrorKind::TooManyItems(max))
289 }
290
291 /// Bundle a set of multiple errors into a single `Error` instance.
292 ///
293 /// Usually it will be more convenient to use an [`error::Accumulator`](Accumulator).
294 ///
295 /// # Panics
296 /// This function will panic if `errors.is_empty() == true`.
297 pub fn multiple(mut errors: Vec<Error>) -> Self {
298 match errors.len() {
299 1 => errors
300 .pop()
301 .expect("Error array of length 1 has a first item"),
302 0 => panic!("Can't deal with 0 errors"),
303 _ => Error::new(ErrorKind::Multiple(errors)),
304 }
305 }
306
307 /// Creates an error collector, for aggregating multiple errors
308 ///
309 /// See [`Accumulator`] for details.
310 pub fn accumulator() -> Accumulator {
311 Default::default()
312 }
313}
314
315impl Error {
316 /// Create a new error about a literal string that doesn't match a set of known
317 /// or permissible values. This function can be made public if the API proves useful
318 /// beyond impls for `syn` types.
319 pub(crate) fn unknown_lit_str_value(value: &LitStr) -> Self {
320 Error::unknown_value(&value.value()).with_span(value)
321 }
322}
323
324/// Error instance methods
325#[allow(clippy::len_without_is_empty)] // Error can never be empty
326impl Error {
327 /// Check if this error is associated with a span in the token stream.
328 pub fn has_span(&self) -> bool {
329 self.span.is_some()
330 }
331
332 /// Tie a span to the error if none is already present. This is used in `darling::FromMeta`
333 /// and other traits to attach errors to the most specific possible location in the input
334 /// source code.
335 ///
336 /// All `darling`-built impls, either from the crate or from the proc macro, will call this
337 /// when appropriate during parsing, so it should not be necessary to call this unless you have
338 /// overridden:
339 ///
340 /// * `FromMeta::from_meta`
341 /// * `FromMeta::from_nested_meta`
342 /// * `FromMeta::from_value`
343 pub fn with_span<T: Spanned>(mut self, node: &T) -> Self {
344 if !self.has_span() {
345 self.span = Some(node.span());
346 }
347
348 self
349 }
350
351 /// Get a span for the error.
352 ///
353 /// # Return Value
354 /// This function will return [`Span::call_site()`](proc_macro2::Span) if [`Self::has_span`] is `false`.
355 /// To get the span only if one has been explicitly set for `self`, instead use [`Error::explicit_span`].
356 pub fn span(&self) -> Span {
357 self.span.unwrap_or_else(Span::call_site)
358 }
359
360 /// Get the span for `self`, if one has been set.
361 pub fn explicit_span(&self) -> Option<Span> {
362 self.span
363 }
364
365 /// Recursively converts a tree of errors to a flattened list.
366 ///
367 /// # Child Diagnostics
368 /// If the `diagnostics` feature is enabled, any child diagnostics on `self`
369 /// will be cloned down to all the errors within `self`.
370 pub fn flatten(self) -> Self {
371 Error::multiple(self.into_vec())
372 }
373
374 fn into_vec(self) -> Vec<Self> {
375 if let ErrorKind::Multiple(errors) = self.kind {
376 let locations = self.locations;
377
378 #[cfg(feature = "diagnostics")]
379 let children = self.children;
380
381 errors
382 .into_iter()
383 .flat_map(|error| {
384 // This is mutated if the diagnostics feature is enabled
385 #[allow(unused_mut)]
386 let mut error = error.prepend_at(locations.clone());
387
388 // Any child diagnostics in `self` are cloned down to all the distinct
389 // errors contained in `self`.
390 #[cfg(feature = "diagnostics")]
391 error.children.extend(children.iter().cloned());
392
393 error.into_vec()
394 })
395 .collect()
396 } else {
397 vec![self]
398 }
399 }
400
401 /// Adds a location to the error, such as a field or variant.
402 /// Locations must be added in reverse order of specificity.
403 pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
404 self.locations.insert(0, location.to_string());
405 self
406 }
407
408 /// Adds a location to the error, such as a field or variant.
409 /// Locations must be added in reverse order of specificity. This is a helper function to avoid
410 /// repeating path to string logic.
411 pub fn at_path(self, path: &Path) -> Self {
412 self.at(path_to_string(path))
413 }
414
415 /// Gets the number of individual errors in this error.
416 ///
417 /// This function never returns `0`, as it's impossible to construct
418 /// a multi-error from an empty `Vec`.
419 pub fn len(&self) -> usize {
420 self.kind.len()
421 }
422
423 /// Consider additional field names as "did you mean" suggestions for
424 /// unknown field errors **if and only if** the caller appears to be operating
425 /// at error's origin (meaning no calls to [`Self::at`] have yet taken place).
426 ///
427 /// # Usage
428 /// `flatten` fields in derived trait implementations rely on this method to offer correct
429 /// "did you mean" suggestions in errors.
430 ///
431 /// Because the `flatten` field receives _all_ unknown fields, if a user mistypes a field name
432 /// that is present on the outer struct but not the flattened struct, they would get an incomplete
433 /// or inferior suggestion unless this method was invoked.
434 pub fn add_sibling_alts_for_unknown_field<'a, T, I>(mut self, alternates: I) -> Self
435 where
436 T: AsRef<str> + 'a,
437 I: IntoIterator<Item = &'a T>,
438 {
439 // The error may have bubbled up before this method was called,
440 // and in those cases adding alternates would be incorrect.
441 if !self.locations.is_empty() {
442 return self;
443 }
444
445 // This is a callback because we don't want to use the iterator
446 // if we don't need to.
447 let collect_alts = || {
448 alternates
449 .into_iter()
450 .map(|s| s.as_ref())
451 .collect::<Vec<_>>()
452 };
453
454 if let ErrorKind::UnknownField(unknown_field) = &mut self.kind {
455 unknown_field.add_alts(&collect_alts());
456 } else if let ErrorKind::Multiple(errors) = self.kind {
457 // Gather up the alternates to avoid unnecessary allocations
458 // on a per-contained-error basis.
459 let alts = collect_alts();
460 self.kind = ErrorKind::Multiple(
461 errors
462 .into_iter()
463 .map(|err| err.add_sibling_alts_for_unknown_field_internal(&alts))
464 .collect(),
465 )
466 }
467
468 self
469 }
470
471 fn add_sibling_alts_for_unknown_field_internal(mut self, alternates: &[&str]) -> Self {
472 // The error may have bubbled up before this method was called,
473 // and in those cases adding alternates would be incorrect.
474 if !self.locations.is_empty() {
475 return self;
476 }
477
478 if let ErrorKind::UnknownField(unknown_field) = &mut self.kind {
479 unknown_field.add_alts(alternates);
480 } else if let ErrorKind::Multiple(errors) = self.kind {
481 self.kind = ErrorKind::Multiple(
482 errors
483 .into_iter()
484 .map(|err| err.add_sibling_alts_for_unknown_field_internal(alternates))
485 .collect(),
486 )
487 }
488
489 self
490 }
491
492 /// Adds a location chain to the head of the error's existing locations.
493 fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
494 if !locations.is_empty() {
495 locations.extend(self.locations);
496 self.locations = locations;
497 }
498
499 self
500 }
501
502 /// Gets the location slice.
503 #[cfg(test)]
504 pub(crate) fn location(&self) -> Vec<&str> {
505 self.locations.iter().map(|i| i.as_str()).collect()
506 }
507
508 /// Write this error and any children as compile errors into a `TokenStream` to
509 /// be returned by the proc-macro.
510 ///
511 /// The behavior of this method will be slightly different if the `diagnostics` feature
512 /// is enabled: In that case, the diagnostics will be emitted immediately by this call,
513 /// and an empty `TokenStream` will be returned.
514 ///
515 /// Return these tokens unmodified to avoid disturbing the attached span information.
516 ///
517 /// # Usage
518 /// ```rust,ignore
519 /// // in your proc-macro function
520 /// let opts = match MyOptions::from_derive_input(&ast) {
521 /// Ok(val) => val,
522 /// Err(err) => {
523 /// return err.write_errors();
524 /// }
525 /// }
526 /// ```
527 pub fn write_errors(self) -> TokenStream {
528 #[cfg(feature = "diagnostics")]
529 {
530 self.emit();
531 TokenStream::default()
532 }
533
534 #[cfg(not(feature = "diagnostics"))]
535 {
536 syn::Error::from(self).into_compile_error()
537 }
538 }
539
540 #[cfg(feature = "diagnostics")]
541 fn single_to_diagnostic(self) -> ::proc_macro::Diagnostic {
542 use proc_macro::{Diagnostic, Level};
543
544 // Delegate to dedicated error formatters when applicable.
545 //
546 // If span information is available, don't include the error property path
547 // since it's redundant and not consistent with native compiler diagnostics.
548 let diagnostic = match self.kind {
549 ErrorKind::UnknownField(euf) => euf.into_diagnostic(self.span),
550 ErrorKind::UnknownValue(euv) => euv.into_diagnostic(self.span),
551 _ => match self.span {
552 Some(span) => span.unwrap().error(self.kind.to_string()),
553 None => Diagnostic::new(Level::Error, self.to_string()),
554 },
555 };
556
557 self.children
558 .into_iter()
559 .fold(diagnostic, |out, child| child.append_to(out))
560 }
561
562 /// Transform this error and its children into a list of compiler diagnostics
563 /// and emit them. If the `Error` has associated span information, the diagnostics
564 /// will identify the correct location in source code automatically.
565 ///
566 /// # Stability
567 /// This is only available on `nightly` until the compiler `proc_macro_diagnostic`
568 /// feature stabilizes. Until then, it may break at any time.
569 #[cfg(feature = "diagnostics")]
570 pub fn emit(self) {
571 for error in self.flatten() {
572 error.single_to_diagnostic().emit()
573 }
574 }
575
576 /// Transform the error into a compiler diagnostic and - if the diagnostic points to
577 /// a specific code location - add a spanned help child diagnostic that points to the
578 /// parent derived trait.
579 ///
580 /// This is experimental and therefore not exposed outside the crate.
581 #[cfg(feature = "diagnostics")]
582 #[allow(dead_code)]
583 fn emit_with_macro_help_span(self) {
584 use proc_macro::Diagnostic;
585
586 for error in self.flatten() {
587 let needs_help = error.has_span();
588 let diagnostic = error.single_to_diagnostic();
589 Diagnostic::emit(if needs_help {
590 diagnostic.span_help(
591 Span::call_site().unwrap(),
592 "Encountered as part of this derive-mode-macro",
593 )
594 } else {
595 diagnostic
596 })
597 }
598 }
599}
600
601#[cfg(feature = "diagnostics")]
602macro_rules! add_child {
603 ($unspanned:ident, $spanned:ident, $level:ident) => {
604 #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error.")]
605 #[doc = "# Example"]
606 #[doc = "```rust"]
607 #[doc = "# use darling_core::Error;"]
608 #[doc = concat!(r#"Error::custom("Example")."#, stringify!($unspanned), r#"("message content");"#)]
609 #[doc = "```"]
610 pub fn $unspanned<T: fmt::Display>(mut self, message: T) -> Self {
611 self.children.push(child::ChildDiagnostic::new(
612 child::Level::$level,
613 None,
614 message.to_string(),
615 ));
616 self
617 }
618
619 #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error with its own span.")]
620 #[doc = "# Example"]
621 #[doc = "```rust"]
622 #[doc = "# use darling_core::Error;"]
623 #[doc = "# let item_to_span = proc_macro2::Span::call_site();"]
624 #[doc = concat!(r#"Error::custom("Example")."#, stringify!($spanned), r#"(&item_to_span, "message content");"#)]
625 #[doc = "```"]
626 pub fn $spanned<S: Spanned, T: fmt::Display>(mut self, span: &S, message: T) -> Self {
627 self.children.push(child::ChildDiagnostic::new(
628 child::Level::$level,
629 Some(span.span()),
630 message.to_string(),
631 ));
632 self
633 }
634 };
635}
636
637/// Add child diagnostics to the error.
638///
639/// # Example
640///
641/// ## Code
642///
643/// ```rust
644/// # use darling_core::Error;
645/// # let struct_ident = proc_macro2::Span::call_site();
646/// Error::custom("this is a demo")
647/// .with_span(&struct_ident)
648/// .note("we wrote this")
649/// .help("try doing this instead");
650/// ```
651/// ## Output
652///
653/// ```text
654/// error: this is a demo
655/// --> my_project/my_file.rs:3:5
656/// |
657/// 13 | FooBar { value: String },
658/// | ^^^^^^
659/// |
660/// = note: we wrote this
661/// = help: try doing this instead
662/// ```
663#[cfg(feature = "diagnostics")]
664impl Error {
665 add_child!(error, span_error, Error);
666 add_child!(warning, span_warning, Warning);
667 add_child!(note, span_note, Note);
668 add_child!(help, span_help, Help);
669}
670
671impl StdError for Error {
672 fn cause(&self) -> Option<&dyn StdError> {
673 None
674 }
675}
676
677impl fmt::Display for Error {
678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679 write!(f, "{}", self.kind)?;
680 if !self.locations.is_empty() {
681 write!(f, " at {}", self.locations.join("/"))?;
682 }
683
684 Ok(())
685 }
686}
687
688impl From<syn::Error> for Error {
689 fn from(e: syn::Error) -> Self {
690 // This impl assumes there is nothing but the message and span that needs to be preserved
691 // from the passed-in error. If this changes at some point, a new ErrorKind should be made
692 // to hold the syn::Error, and this impl should preserve it unmodified while setting its own
693 // span to be a copy of the passed-in error.
694 Self {
695 span: Some(e.span()),
696 ..Self::custom(e)
697 }
698 }
699}
700
701impl From<Error> for syn::Error {
702 fn from(e: Error) -> Self {
703 if e.len() == 1 {
704 if let Some(span) = e.explicit_span() {
705 // Don't include the location path if the error has an explicit span,
706 // since it will be redundant and isn't consistent with how rustc
707 // exposes errors.
708 syn::Error::new(span, e.kind)
709 } else {
710 // If the error's span is going to be the macro call site, include
711 // the location information to try and help the user pinpoint the issue.
712 syn::Error::new(e.span(), e)
713 }
714 } else {
715 let mut syn_errors = e.flatten().into_iter().map(syn::Error::from);
716 let mut error = syn_errors
717 .next()
718 .expect("darling::Error can never be empty");
719
720 for next_error in syn_errors {
721 error.combine(next_error);
722 }
723
724 error
725 }
726 }
727}
728
729// Don't want to publicly commit to Error supporting equality yet, but
730// not having it makes testing very difficult. Note that spans are not
731// considered for equality since that would break testing in most cases.
732#[cfg(test)]
733impl PartialEq for Error {
734 fn eq(&self, other: &Self) -> bool {
735 self.kind == other.kind && self.locations == other.locations
736 }
737}
738
739#[cfg(test)]
740impl Eq for Error {}
741
742impl IntoIterator for Error {
743 type Item = Error;
744 type IntoIter = IntoIter;
745
746 fn into_iter(self) -> IntoIter {
747 if let ErrorKind::Multiple(errors) = self.kind {
748 IntoIter {
749 inner: IntoIterEnum::Multiple(errors.into_iter()),
750 }
751 } else {
752 IntoIter {
753 inner: IntoIterEnum::Single(iter::once(self)),
754 }
755 }
756 }
757}
758
759enum IntoIterEnum {
760 Single(iter::Once<Error>),
761 Multiple(vec::IntoIter<Error>),
762}
763
764impl Iterator for IntoIterEnum {
765 type Item = Error;
766
767 fn next(&mut self) -> Option<Self::Item> {
768 match *self {
769 IntoIterEnum::Single(ref mut content) => content.next(),
770 IntoIterEnum::Multiple(ref mut content) => content.next(),
771 }
772 }
773}
774
775/// An iterator that moves out of an `Error`.
776pub struct IntoIter {
777 inner: IntoIterEnum,
778}
779
780impl Iterator for IntoIter {
781 type Item = Error;
782
783 fn next(&mut self) -> Option<Error> {
784 self.inner.next()
785 }
786}
787
788/// Accumulator for errors, for helping call [`Error::multiple`].
789///
790/// See the docs for [`darling::Error`](Error) for more discussion of error handling with darling.
791///
792/// # Panics
793///
794/// `Accumulator` panics on drop unless [`finish`](Self::finish), [`finish_with`](Self::finish_with),
795/// or [`into_inner`](Self::into_inner) has been called, **even if it contains no errors**.
796/// If you want to discard an `Accumulator` that you know to be empty, use `accumulator.finish().unwrap()`.
797///
798/// # Example
799///
800/// ```
801/// # extern crate darling_core as darling;
802/// # struct Thing;
803/// # struct Output;
804/// # impl Thing { fn validate(self) -> darling::Result<Output> { Ok(Output) } }
805/// fn validate_things(inputs: Vec<Thing>) -> darling::Result<Vec<Output>> {
806/// let mut errors = darling::Error::accumulator();
807///
808/// let outputs = inputs
809/// .into_iter()
810/// .filter_map(|thing| errors.handle_in(|| thing.validate()))
811/// .collect::<Vec<_>>();
812///
813/// errors.finish()?;
814/// Ok(outputs)
815/// }
816/// ```
817#[derive(Debug)]
818#[must_use = "Accumulator will panic on drop if not defused."]
819pub struct Accumulator(Option<Vec<Error>>);
820
821impl Accumulator {
822 /// Runs a closure, returning the successful value as `Some`, or collecting the error
823 ///
824 /// The closure's return type is `darling::Result`, so inside it one can use `?`.
825 pub fn handle_in<T, F: FnOnce() -> Result<T>>(&mut self, f: F) -> Option<T> {
826 self.handle(f())
827 }
828
829 /// Handles a possible error.
830 ///
831 /// Returns a successful value as `Some`, or collects the error and returns `None`.
832 pub fn handle<T>(&mut self, result: Result<T>) -> Option<T> {
833 match result {
834 Ok(y) => Some(y),
835 Err(e) => {
836 self.push(e);
837 None
838 }
839 }
840 }
841
842 /// Stop accumulating errors, producing `Ok` if there are no errors or producing
843 /// an error with all those encountered by the accumulator.
844 pub fn finish(self) -> Result<()> {
845 self.finish_with(())
846 }
847
848 /// Bundles the collected errors if there were any, or returns the success value
849 ///
850 /// Call this at the end of your input processing.
851 ///
852 /// If there were no errors recorded, returns `Ok(success)`.
853 /// Otherwise calls [`Error::multiple`] and returns the result as an `Err`.
854 pub fn finish_with<T>(self, success: T) -> Result<T> {
855 let errors = self.into_inner();
856 if errors.is_empty() {
857 Ok(success)
858 } else {
859 Err(Error::multiple(errors))
860 }
861 }
862
863 fn errors(&mut self) -> &mut Vec<Error> {
864 match &mut self.0 {
865 Some(errors) => errors,
866 None => panic!("darling internal error: Accumulator accessed after defuse"),
867 }
868 }
869
870 /// Returns the accumulated errors as a `Vec`.
871 ///
872 /// This function defuses the drop bomb.
873 #[must_use = "Accumulated errors should be handled or propagated to the caller"]
874 pub fn into_inner(mut self) -> Vec<Error> {
875 match self.0.take() {
876 Some(errors) => errors,
877 None => panic!("darling internal error: Accumulator accessed after defuse"),
878 }
879 }
880
881 /// Add one error to the collection.
882 pub fn push(&mut self, error: Error) {
883 self.errors().push(error)
884 }
885
886 /// Finish the current accumulation, and if there are no errors create a new `Self` so processing may continue.
887 ///
888 /// This is shorthand for:
889 ///
890 /// ```rust,ignore
891 /// errors.finish()?;
892 /// errors = Error::accumulator();
893 /// ```
894 ///
895 /// # Drop Behavior
896 /// This function returns a new [`Accumulator`] in the success case.
897 /// This new accumulator is "armed" and will detonate if dropped without being finished.
898 ///
899 /// # Example
900 ///
901 /// ```
902 /// # extern crate darling_core as darling;
903 /// # struct Thing;
904 /// # struct Output;
905 /// # impl Thing { fn validate(&self) -> darling::Result<Output> { Ok(Output) } }
906 /// fn validate(lorem_inputs: &[Thing], ipsum_inputs: &[Thing])
907 /// -> darling::Result<(Vec<Output>, Vec<Output>)> {
908 /// let mut errors = darling::Error::accumulator();
909 ///
910 /// let lorems = lorem_inputs.iter().filter_map(|l| {
911 /// errors.handle(l.validate())
912 /// }).collect();
913 ///
914 /// errors = errors.checkpoint()?;
915 ///
916 /// let ipsums = ipsum_inputs.iter().filter_map(|l| {
917 /// errors.handle(l.validate())
918 /// }).collect();
919 ///
920 /// errors.finish_with((lorems, ipsums))
921 /// }
922 /// # validate(&[], &[]).unwrap();
923 /// ```
924 pub fn checkpoint(self) -> Result<Accumulator> {
925 // The doc comment says on success we "return the Accumulator for future use".
926 // Actually, we have consumed it by feeding it to finish so we make a fresh one.
927 // This is OK since by definition of the success path, it was empty on entry.
928 self.finish()?;
929 Ok(Self::default())
930 }
931}
932
933impl Default for Accumulator {
934 fn default() -> Self {
935 Accumulator(Some(vec![]))
936 }
937}
938
939impl Extend<Error> for Accumulator {
940 fn extend<I>(&mut self, iter: I)
941 where
942 I: IntoIterator<Item = Error>,
943 {
944 self.errors().extend(iter)
945 }
946}
947
948impl Drop for Accumulator {
949 fn drop(&mut self) {
950 // don't try to panic if we are currently unwinding a panic
951 // otherwise we end up with an unhelful "thread panicked while panicking. aborting." message
952 if !std::thread::panicking() {
953 if let Some(errors) = &mut self.0 {
954 match errors.len() {
955 0 => panic!("darling::error::Accumulator dropped without being finished"),
956 error_count => panic!("darling::error::Accumulator dropped without being finished. {} errors were lost.", error_count)
957 }
958 }
959 }
960 }
961}
962
963#[cfg(test)]
964mod tests {
965 use super::Error;
966
967 #[test]
968 fn flatten_noop() {
969 let err = Error::duplicate_field("hello").at("world");
970 assert_eq!(err.clone().flatten(), err);
971 }
972
973 #[test]
974 fn flatten_simple() {
975 let err = Error::multiple(vec![
976 Error::unknown_field("hello").at("world"),
977 Error::missing_field("hell_no").at("world"),
978 ])
979 .at("foo")
980 .flatten();
981
982 assert!(err.location().is_empty());
983
984 let mut err_iter = err.into_iter();
985
986 let first = err_iter.next();
987 assert!(first.is_some());
988 assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
989
990 let second = err_iter.next();
991 assert!(second.is_some());
992
993 assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
994
995 assert!(err_iter.next().is_none());
996 }
997
998 #[test]
999 fn len_single() {
1000 let err = Error::duplicate_field("hello");
1001 assert_eq!(1, err.len());
1002 }
1003
1004 #[test]
1005 fn len_multiple() {
1006 let err = Error::multiple(vec![
1007 Error::duplicate_field("hello"),
1008 Error::missing_field("hell_no"),
1009 ]);
1010 assert_eq!(2, err.len());
1011 }
1012
1013 #[test]
1014 fn len_nested() {
1015 let err = Error::multiple(vec![
1016 Error::duplicate_field("hello"),
1017 Error::multiple(vec![
1018 Error::duplicate_field("hi"),
1019 Error::missing_field("bye"),
1020 Error::multiple(vec![Error::duplicate_field("whatsup")]),
1021 ]),
1022 ]);
1023
1024 assert_eq!(4, err.len());
1025 }
1026
1027 #[test]
1028 fn accum_ok() {
1029 let errs = Error::accumulator();
1030 assert_eq!("test", errs.finish_with("test").unwrap());
1031 }
1032
1033 #[test]
1034 fn accum_errr() {
1035 let mut errs = Error::accumulator();
1036 errs.push(Error::custom("foo!"));
1037 errs.finish().unwrap_err();
1038 }
1039
1040 #[test]
1041 fn accum_into_inner() {
1042 let mut errs = Error::accumulator();
1043 errs.push(Error::custom("foo!"));
1044 let errs: Vec<_> = errs.into_inner();
1045 assert_eq!(errs.len(), 1);
1046 }
1047
1048 #[test]
1049 #[should_panic(expected = "Accumulator dropped")]
1050 fn accum_drop_panic() {
1051 let _errs = Error::accumulator();
1052 }
1053
1054 #[test]
1055 #[should_panic(expected = "2 errors")]
1056 fn accum_drop_panic_with_error_count() {
1057 let mut errors = Error::accumulator();
1058 errors.push(Error::custom("first"));
1059 errors.push(Error::custom("second"));
1060 }
1061
1062 #[test]
1063 fn accum_checkpoint_error() {
1064 let mut errs = Error::accumulator();
1065 errs.push(Error::custom("foo!"));
1066 errs.checkpoint().unwrap_err();
1067 }
1068
1069 #[test]
1070 #[should_panic(expected = "Accumulator dropped")]
1071 fn accum_checkpoint_drop_panic() {
1072 let mut errs = Error::accumulator();
1073 errs = errs.checkpoint().unwrap();
1074 let _ = errs;
1075 }
1076}