#[cfg(doc)]
use proc_macro2::Span;
#[cfg(doc)]
use quote::ToTokens;
#[cfg(doc)]
use crate::{Emitter, Error, ErrorMessage, SpanRanged};
#[doc(hidden)]
#[macro_export]
macro_rules! __error_message_internal {
((cs($($fmt:tt)*)$(.$fn:ident($($fmt_fn:tt)*))*), (), ()) => {
$crate::ErrorMessage::call_site($($fmt)*)
$(.attachment(::core::stringify!($fn), $($fmt_fn)*))*
};
((new($span:expr)($($fmt:tt)*)$(.$fn:ident($($fmt_fn:tt)*))*), (), ()) => {
$crate::ErrorMessage::new(
$crate::span_range!($span),
$($fmt)*
)
$(.attachment(::core::stringify!($fn), $($fmt_fn)*))*
};
($head:tt, ($($fmt:tt)*), (, $ident:ident = $expr:expr, $($tail:tt)*)) => {
$crate::__error_message_internal!($head, ($($fmt)*, $ident = $expr), (, $($tail)*))
};
($head:tt, ($($fmt:tt)*), (, $ident:ident = $expr:expr; $($tail:tt)*)) => {
$crate::__error_message_internal!($head, ($($fmt)*, $ident = $expr), (; $($tail)*))
};
($head:tt, ($($fmt:tt)*), (, $ident:ident = $expr:expr)) => {
$crate::__error_message_internal!($head, ($($fmt)*, $ident = $expr), ())
};
($head:tt, ($($fmt:tt)*), (, $expr:expr, $($tail:tt)*)) => {
$crate::__error_message_internal!($head, ($($fmt)*, $expr), (, $($tail)*))
};
($head:tt, ($($fmt:tt)*), (, $expr:expr; $($tail:tt)*)) => {
$crate::__error_message_internal!($head, ($($fmt)*, $expr), (; $($tail)*))
};
($head:tt, ($($fmt:tt)*), (, $expr:expr)) => {
$crate::__error_message_internal!($head, ($($fmt)*, $expr), ())
};
(($($head:tt)*), $fmt:tt, ($(,)?$(;)?)) => {
$crate::__error_message_internal!(($($head)*(::core::format_args!$fmt)), (), ())
};
(($($head:tt)*), $fmt:tt, ($(,)?; $attachment:ident = $fmt_str:literal $($tail:tt)*)) => {
$crate::__error_message_internal!(($($head)*(::core::format_args!$fmt).$attachment), ($fmt_str), ($($tail)*))
};
}
#[macro_export]
macro_rules! error_message {
($fmt:literal $($tt:tt)*) => {
$crate::__error_message_internal!((cs), ($fmt), ($($tt)*))
};
($span:expr, $fmt:literal $($tt:tt)*) => {
$crate::__error_message_internal!((new($span)), ($fmt), ($($tt)*))
};
}
#[macro_export]
macro_rules! bail {
($msg:literal) => {
return ::core::result::Result::Err($crate::error_message!($msg).into());
};
($error:expr) => {
return ::core::result::Result::Err($error.into());
};
($($tt:tt)*) => {
return ::core::result::Result::Err($crate::error_message!($($tt)*).into());
};
}
#[macro_export]
macro_rules! ensure {
($cond:expr, $($bail_args:tt)*) => {
if !$cond {
$crate::bail!($($bail_args)*);
}
};
(let $pat:pat = $expr:expr, $($bail_args:tt)*) => {
let $pat = $expr else {
$crate::bail!($($bail_args)*);
};
};
}
#[macro_export]
macro_rules! emit {
($emitter:expr, $msg:literal) => {
$emitter.extend(::core::iter::once::<$crate::ErrorMessage>($crate::error_message!($msg)));
};
($emitter:expr, $error:expr) => {
$emitter.extend(::core::iter::once($error));
};
($emitter:expr, $($tt:tt)*) => {
$emitter.extend(::core::iter::once::<$crate::ErrorMessage>($crate::error_message!($($tt)*).into()));
};
}
#[cfg(test)]
mod test {
use proc_macro::Span;
use quote::quote;
use crate::{Emitter, ErrorMessage};
macro_rules! returned {
($ty:ty, $expr:expr) => {
#[allow(unreachable_code)]
(|| -> $ty {
$expr;
unreachable!();
})()
};
}
#[test]
fn bail() {
assert_eq!(
returned!(Result<(), ErrorMessage>, bail!("format"))
.unwrap_err()
.to_string(),
"format"
);
assert_eq!(
returned!(Result<(), ErrorMessage>, bail!("format {}", 1))
.unwrap_err()
.to_string(),
"format 1"
);
let b = "ho";
assert_eq!(
returned!(Result<(), ErrorMessage>, bail!("format {} {a} {} {b}", 1, 2, a = 4))
.unwrap_err()
.to_string(),
"format 1 4 2 ho"
);
}
#[test]
fn error_message() {
assert_eq!(error_message!("test").to_string(), "test");
assert_eq!(error_message!("test";).to_string(), "test");
assert_eq!(
error_message!(
"test";
error = "hello {} {a}", 1 + 4, a = ""
)
.to_string(),
"test\n\n = error: hello 5 \n"
);
assert_eq!(
error_message!(
"test";
error = "hello {} {a}", 1 + 4, a = "";
)
.to_string(),
"test\n\n = error: hello 5 \n"
);
assert_eq!(
error_message!(
"test";
error = "hello {} {a}", 1 + 4, a = "",;
hint = "a hint"
)
.to_string(),
"test\n\n = error: hello 5 \n = hint: a hint\n"
);
}
#[test]
fn emit() {
let mut emitter = Emitter::new();
emit!(emitter, "an error message"; error = "with attachments");
let span = proc_macro2::Span::call_site();
emit!(emitter, span, "error message");
#[cfg(feature = "syn2")]
{
let error = syn2::Error::new(proc_macro2::Span::call_site(), "an error");
emit!(emitter, error);
}
}
fn _error_message_spanned() {
let span = Span::call_site();
_ = error_message!(span, "test");
let span = proc_macro::Span::call_site();
_ = error_message!(span, "test");
let tokens = quote!(test);
_ = error_message!(tokens, "an error message",);
_ = error_message!(tokens, "an error message",;);
_ = error_message!(tokens, "an error message",; warning="and a warning";);
}
}