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