use proc_macro2::{Span, TokenStream};
use std::error::Error as StdError;
use std::fmt;
use std::iter::{self, Iterator};
use std::string::ToString;
use std::vec;
use syn::spanned::Spanned;
use syn::{Expr, Lit, LitStr, Path};
#[cfg(feature = "diagnostics")]
mod child;
mod kind;
mod util;
use crate::util::path_to_string;
use self::kind::{ErrorKind, ErrorUnknownValue, UnknownValuePosition};
pub type Result<T> = ::std::result::Result<T, Error>;
#[derive(Debug, Clone)]
pub struct Error {
kind: ErrorKind,
locations: Vec<String>,
span: Option<Span>,
#[cfg(feature = "diagnostics")]
children: Vec<child::ChildDiagnostic>,
}
impl Error {
pub(in crate::error) fn new(kind: ErrorKind) -> Self {
Error {
kind,
locations: Vec::new(),
span: None,
#[cfg(feature = "diagnostics")]
children: vec![],
}
}
pub fn custom<T: fmt::Display>(msg: T) -> Self {
Error::new(ErrorKind::Custom(msg.to_string()))
}
pub fn duplicate_field(name: &str) -> Self {
Error::new(ErrorKind::DuplicateField(name.into()))
}
pub fn duplicate_field_path(path: &Path) -> Self {
Error::duplicate_field(&path_to_string(path))
}
pub fn missing_field(name: &str) -> Self {
Error::new(ErrorKind::MissingField(name.into()))
}
pub fn unknown_field(name: &str) -> Self {
Error::new(ErrorUnknownValue::new(UnknownValuePosition::Field, name).into())
}
pub fn unknown_field_path(path: &Path) -> Self {
Error::unknown_field(&path_to_string(path))
}
pub fn unknown_field_with_alts<'a, T, I>(field: &str, alternates: I) -> Self
where
T: AsRef<str> + 'a,
I: IntoIterator<Item = &'a T>,
{
Error::new(
ErrorUnknownValue::with_alts(UnknownValuePosition::Field, field, alternates).into(),
)
}
pub fn unknown_field_path_with_alts<'a, T, I>(field: &Path, alternates: I) -> Self
where
T: AsRef<str> + 'a,
I: IntoIterator<Item = &'a T>,
{
Error::new(ErrorKind::UnknownField(
ErrorUnknownValue::with_alts(
UnknownValuePosition::Field,
&path_to_string(field),
alternates,
)
.into(),
))
}
pub fn unsupported_shape(shape: &str) -> Self {
Error::new(ErrorKind::UnsupportedShape {
observed: shape.into(),
expected: None,
})
}
pub fn unsupported_shape_with_expected<T: fmt::Display>(shape: &str, expected: &T) -> Self {
Error::new(ErrorKind::UnsupportedShape {
observed: shape.into(),
expected: Some(expected.to_string()),
})
}
pub fn unsupported_format(format: &str) -> Self {
Error::new(ErrorKind::UnexpectedFormat(format.into()))
}
pub fn unexpected_type(ty: &str) -> Self {
Error::new(ErrorKind::UnexpectedType(ty.into()))
}
pub fn unexpected_expr_type(expr: &Expr) -> Self {
Error::unexpected_type(match *expr {
Expr::Array(_) => "array",
Expr::Assign(_) => "assign",
Expr::Async(_) => "async",
Expr::Await(_) => "await",
Expr::Binary(_) => "binary",
Expr::Block(_) => "block",
Expr::Break(_) => "break",
Expr::Call(_) => "call",
Expr::Cast(_) => "cast",
Expr::Closure(_) => "closure",
Expr::Const(_) => "const",
Expr::Continue(_) => "continue",
Expr::Field(_) => "field",
Expr::ForLoop(_) => "for_loop",
Expr::Group(_) => "group",
Expr::If(_) => "if",
Expr::Index(_) => "index",
Expr::Infer(_) => "infer",
Expr::Let(_) => "let",
Expr::Lit(_) => "lit",
Expr::Loop(_) => "loop",
Expr::Macro(_) => "macro",
Expr::Match(_) => "match",
Expr::MethodCall(_) => "method_call",
Expr::Paren(_) => "paren",
Expr::Path(_) => "path",
Expr::Range(_) => "range",
Expr::Reference(_) => "reference",
Expr::Repeat(_) => "repeat",
Expr::Return(_) => "return",
Expr::Struct(_) => "struct",
Expr::Try(_) => "try",
Expr::TryBlock(_) => "try_block",
Expr::Tuple(_) => "tuple",
Expr::Unary(_) => "unary",
Expr::Unsafe(_) => "unsafe",
Expr::Verbatim(_) => "verbatim",
Expr::While(_) => "while",
Expr::Yield(_) => "yield",
_ => "unknown",
})
.with_span(expr)
}
pub fn unexpected_lit_type(lit: &Lit) -> Self {
Error::unexpected_type(match *lit {
Lit::Str(_) => "string",
Lit::ByteStr(_) => "byte string",
Lit::Byte(_) => "byte",
Lit::Char(_) => "char",
Lit::Int(_) => "int",
Lit::Float(_) => "float",
Lit::Bool(_) => "bool",
Lit::Verbatim(_) => "verbatim",
_ => "unknown",
})
.with_span(lit)
}
pub fn unknown_value(value: &str) -> Self {
Error::new(ErrorUnknownValue::new(UnknownValuePosition::Value, value).into())
}
pub fn unknown_value_with_alts<'a, T, I>(value: &str, alternates: I) -> Self
where
T: AsRef<str> + 'a,
I: IntoIterator<Item = &'a T>,
{
Error::new(
ErrorUnknownValue::with_alts(UnknownValuePosition::Value, value, alternates).into(),
)
}
pub fn too_few_items(min: usize) -> Self {
Error::new(ErrorKind::TooFewItems(min))
}
pub fn too_many_items(max: usize) -> Self {
Error::new(ErrorKind::TooManyItems(max))
}
pub fn multiple(mut errors: Vec<Error>) -> Self {
match errors.len() {
1 => errors
.pop()
.expect("Error array of length 1 has a first item"),
0 => panic!("Can't deal with 0 errors"),
_ => Error::new(ErrorKind::Multiple(errors)),
}
}
pub fn accumulator() -> Accumulator {
Default::default()
}
}
impl Error {
pub(crate) fn unknown_lit_str_value(value: &LitStr) -> Self {
Error::unknown_value(&value.value()).with_span(value)
}
}
#[allow(clippy::len_without_is_empty)] impl Error {
pub fn has_span(&self) -> bool {
self.span.is_some()
}
pub fn with_span<T: Spanned>(mut self, node: &T) -> Self {
if !self.has_span() {
self.span = Some(node.span());
}
self
}
pub fn span(&self) -> Span {
self.span.unwrap_or_else(Span::call_site)
}
pub fn explicit_span(&self) -> Option<Span> {
self.span
}
pub fn flatten(self) -> Self {
Error::multiple(self.into_vec())
}
fn into_vec(self) -> Vec<Self> {
if let ErrorKind::Multiple(errors) = self.kind {
let locations = self.locations;
#[cfg(feature = "diagnostics")]
let children = self.children;
errors
.into_iter()
.flat_map(|error| {
#[allow(unused_mut)]
let mut error = error.prepend_at(locations.clone());
#[cfg(feature = "diagnostics")]
error.children.extend(children.iter().cloned());
error.into_vec()
})
.collect()
} else {
vec![self]
}
}
pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
self.locations.insert(0, location.to_string());
self
}
pub fn at_path(self, path: &Path) -> Self {
self.at(path_to_string(path))
}
pub fn len(&self) -> usize {
self.kind.len()
}
pub fn add_sibling_alts_for_unknown_field<'a, T, I>(mut self, alternates: I) -> Self
where
T: AsRef<str> + 'a,
I: IntoIterator<Item = &'a T>,
{
if !self.locations.is_empty() {
return self;
}
let collect_alts = || {
alternates
.into_iter()
.map(|s| s.as_ref())
.collect::<Vec<_>>()
};
if let ErrorKind::UnknownField(unknown_field) = &mut self.kind {
unknown_field.add_alts(&collect_alts());
} else if let ErrorKind::Multiple(errors) = self.kind {
let alts = collect_alts();
self.kind = ErrorKind::Multiple(
errors
.into_iter()
.map(|err| err.add_sibling_alts_for_unknown_field_internal(&alts))
.collect(),
)
}
self
}
fn add_sibling_alts_for_unknown_field_internal(mut self, alternates: &[&str]) -> Self {
if !self.locations.is_empty() {
return self;
}
if let ErrorKind::UnknownField(unknown_field) = &mut self.kind {
unknown_field.add_alts(alternates);
} else if let ErrorKind::Multiple(errors) = self.kind {
self.kind = ErrorKind::Multiple(
errors
.into_iter()
.map(|err| err.add_sibling_alts_for_unknown_field_internal(alternates))
.collect(),
)
}
self
}
fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
if !locations.is_empty() {
locations.extend(self.locations);
self.locations = locations;
}
self
}
#[cfg(test)]
pub(crate) fn location(&self) -> Vec<&str> {
self.locations.iter().map(|i| i.as_str()).collect()
}
pub fn write_errors(self) -> TokenStream {
#[cfg(feature = "diagnostics")]
{
self.emit();
TokenStream::default()
}
#[cfg(not(feature = "diagnostics"))]
{
syn::Error::from(self).into_compile_error()
}
}
#[cfg(feature = "diagnostics")]
fn single_to_diagnostic(self) -> ::proc_macro::Diagnostic {
use proc_macro::{Diagnostic, Level};
let diagnostic = match self.kind {
ErrorKind::UnknownField(euf) => euf.into_diagnostic(self.span),
ErrorKind::UnknownValue(euv) => euv.into_diagnostic(self.span),
_ => match self.span {
Some(span) => span.unwrap().error(self.kind.to_string()),
None => Diagnostic::new(Level::Error, self.to_string()),
},
};
self.children
.into_iter()
.fold(diagnostic, |out, child| child.append_to(out))
}
#[cfg(feature = "diagnostics")]
pub fn emit(self) {
for error in self.flatten() {
error.single_to_diagnostic().emit()
}
}
#[cfg(feature = "diagnostics")]
#[allow(dead_code)]
fn emit_with_macro_help_span(self) {
use proc_macro::Diagnostic;
for error in self.flatten() {
let needs_help = error.has_span();
let diagnostic = error.single_to_diagnostic();
Diagnostic::emit(if needs_help {
diagnostic.span_help(
Span::call_site().unwrap(),
"Encountered as part of this derive-mode-macro",
)
} else {
diagnostic
})
}
}
}
#[cfg(feature = "diagnostics")]
macro_rules! add_child {
($unspanned:ident, $spanned:ident, $level:ident) => {
#[doc = concat!("Add a child ", stringify!($unspanned), " message to this error.")]
#[doc = "# Example"]
#[doc = "```rust"]
#[doc = "# use darling_core::Error;"]
#[doc = concat!(r#"Error::custom("Example")."#, stringify!($unspanned), r#"("message content");"#)]
#[doc = "```"]
pub fn $unspanned<T: fmt::Display>(mut self, message: T) -> Self {
self.children.push(child::ChildDiagnostic::new(
child::Level::$level,
None,
message.to_string(),
));
self
}
#[doc = concat!("Add a child ", stringify!($unspanned), " message to this error with its own span.")]
#[doc = "# Example"]
#[doc = "```rust"]
#[doc = "# use darling_core::Error;"]
#[doc = "# let item_to_span = proc_macro2::Span::call_site();"]
#[doc = concat!(r#"Error::custom("Example")."#, stringify!($spanned), r#"(&item_to_span, "message content");"#)]
#[doc = "```"]
pub fn $spanned<S: Spanned, T: fmt::Display>(mut self, span: &S, message: T) -> Self {
self.children.push(child::ChildDiagnostic::new(
child::Level::$level,
Some(span.span()),
message.to_string(),
));
self
}
};
}
#[cfg(feature = "diagnostics")]
impl Error {
add_child!(error, span_error, Error);
add_child!(warning, span_warning, Warning);
add_child!(note, span_note, Note);
add_child!(help, span_help, Help);
}
impl StdError for Error {
fn cause(&self) -> Option<&dyn StdError> {
None
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)?;
if !self.locations.is_empty() {
write!(f, " at {}", self.locations.join("/"))?;
}
Ok(())
}
}
impl From<syn::Error> for Error {
fn from(e: syn::Error) -> Self {
Self {
span: Some(e.span()),
..Self::custom(e)
}
}
}
impl From<Error> for syn::Error {
fn from(e: Error) -> Self {
if e.len() == 1 {
if let Some(span) = e.explicit_span() {
syn::Error::new(span, e.kind)
} else {
syn::Error::new(e.span(), e)
}
} else {
let mut syn_errors = e.flatten().into_iter().map(syn::Error::from);
let mut error = syn_errors
.next()
.expect("darling::Error can never be empty");
for next_error in syn_errors {
error.combine(next_error);
}
error
}
}
}
#[cfg(test)]
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind && self.locations == other.locations
}
}
#[cfg(test)]
impl Eq for Error {}
impl IntoIterator for Error {
type Item = Error;
type IntoIter = IntoIter;
fn into_iter(self) -> IntoIter {
if let ErrorKind::Multiple(errors) = self.kind {
IntoIter {
inner: IntoIterEnum::Multiple(errors.into_iter()),
}
} else {
IntoIter {
inner: IntoIterEnum::Single(iter::once(self)),
}
}
}
}
enum IntoIterEnum {
Single(iter::Once<Error>),
Multiple(vec::IntoIter<Error>),
}
impl Iterator for IntoIterEnum {
type Item = Error;
fn next(&mut self) -> Option<Self::Item> {
match *self {
IntoIterEnum::Single(ref mut content) => content.next(),
IntoIterEnum::Multiple(ref mut content) => content.next(),
}
}
}
pub struct IntoIter {
inner: IntoIterEnum,
}
impl Iterator for IntoIter {
type Item = Error;
fn next(&mut self) -> Option<Error> {
self.inner.next()
}
}
#[derive(Debug)]
#[must_use = "Accumulator will panic on drop if not defused."]
pub struct Accumulator(Option<Vec<Error>>);
impl Accumulator {
pub fn handle_in<T, F: FnOnce() -> Result<T>>(&mut self, f: F) -> Option<T> {
self.handle(f())
}
pub fn handle<T>(&mut self, result: Result<T>) -> Option<T> {
match result {
Ok(y) => Some(y),
Err(e) => {
self.push(e);
None
}
}
}
pub fn finish(self) -> Result<()> {
self.finish_with(())
}
pub fn finish_with<T>(self, success: T) -> Result<T> {
let errors = self.into_inner();
if errors.is_empty() {
Ok(success)
} else {
Err(Error::multiple(errors))
}
}
fn errors(&mut self) -> &mut Vec<Error> {
match &mut self.0 {
Some(errors) => errors,
None => panic!("darling internal error: Accumulator accessed after defuse"),
}
}
#[must_use = "Accumulated errors should be handled or propagated to the caller"]
pub fn into_inner(mut self) -> Vec<Error> {
match self.0.take() {
Some(errors) => errors,
None => panic!("darling internal error: Accumulator accessed after defuse"),
}
}
pub fn push(&mut self, error: Error) {
self.errors().push(error)
}
pub fn checkpoint(self) -> Result<Accumulator> {
self.finish()?;
Ok(Self::default())
}
}
impl Default for Accumulator {
fn default() -> Self {
Accumulator(Some(vec![]))
}
}
impl Extend<Error> for Accumulator {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Error>,
{
self.errors().extend(iter)
}
}
impl Drop for Accumulator {
fn drop(&mut self) {
if !std::thread::panicking() {
if let Some(errors) = &mut self.0 {
match errors.len() {
0 => panic!("darling::error::Accumulator dropped without being finished"),
error_count => panic!("darling::error::Accumulator dropped without being finished. {} errors were lost.", error_count)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::Error;
#[test]
fn flatten_noop() {
let err = Error::duplicate_field("hello").at("world");
assert_eq!(err.clone().flatten(), err);
}
#[test]
fn flatten_simple() {
let err = Error::multiple(vec![
Error::unknown_field("hello").at("world"),
Error::missing_field("hell_no").at("world"),
])
.at("foo")
.flatten();
assert!(err.location().is_empty());
let mut err_iter = err.into_iter();
let first = err_iter.next();
assert!(first.is_some());
assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
let second = err_iter.next();
assert!(second.is_some());
assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
assert!(err_iter.next().is_none());
}
#[test]
fn len_single() {
let err = Error::duplicate_field("hello");
assert_eq!(1, err.len());
}
#[test]
fn len_multiple() {
let err = Error::multiple(vec![
Error::duplicate_field("hello"),
Error::missing_field("hell_no"),
]);
assert_eq!(2, err.len());
}
#[test]
fn len_nested() {
let err = Error::multiple(vec![
Error::duplicate_field("hello"),
Error::multiple(vec![
Error::duplicate_field("hi"),
Error::missing_field("bye"),
Error::multiple(vec![Error::duplicate_field("whatsup")]),
]),
]);
assert_eq!(4, err.len());
}
#[test]
fn accum_ok() {
let errs = Error::accumulator();
assert_eq!("test", errs.finish_with("test").unwrap());
}
#[test]
fn accum_errr() {
let mut errs = Error::accumulator();
errs.push(Error::custom("foo!"));
errs.finish().unwrap_err();
}
#[test]
fn accum_into_inner() {
let mut errs = Error::accumulator();
errs.push(Error::custom("foo!"));
let errs: Vec<_> = errs.into_inner();
assert_eq!(errs.len(), 1);
}
#[test]
#[should_panic(expected = "Accumulator dropped")]
fn accum_drop_panic() {
let _errs = Error::accumulator();
}
#[test]
#[should_panic(expected = "2 errors")]
fn accum_drop_panic_with_error_count() {
let mut errors = Error::accumulator();
errors.push(Error::custom("first"));
errors.push(Error::custom("second"));
}
#[test]
fn accum_checkpoint_error() {
let mut errs = Error::accumulator();
errs.push(Error::custom("foo!"));
errs.checkpoint().unwrap_err();
}
#[test]
#[should_panic(expected = "Accumulator dropped")]
fn accum_checkpoint_drop_panic() {
let mut errs = Error::accumulator();
errs = errs.checkpoint().unwrap();
let _ = errs;
}
}