#![doc = include_str!("../README.md")]
#![doc = include_str!("../example.rs")]
#![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic))]
extern crate proc_macro;
#[cfg(not(feature = "nightly"))]
pub mod fallback;
#[cfg(feature = "nightly")]
pub type Diagnostic = proc_macro::Diagnostic;
#[cfg(not(feature = "nightly"))]
pub type Diagnostic = fallback::Diagnostic;
#[cfg(feature = "nightly")]
pub type Level = proc_macro::Level;
#[cfg(not(feature = "nightly"))]
pub type Level = fallback::Level;
pub trait ToSpans {
fn to_spans(self) -> Vec<proc_macro::Span>;
}
impl ToSpans for proc_macro::Span {
fn to_spans(self) -> Vec<proc_macro::Span> {
vec![self]
}
}
#[cfg(feature = "proc-macro2")]
impl ToSpans for proc_macro2::Span {
fn to_spans(self) -> Vec<proc_macro::Span> {
vec![self.unwrap()]
}
}
pub trait Emittable {
fn emit(level: Level, this: Self);
}
impl Emittable for &str {
fn emit(level: Level, this: Self) {
Diagnostic::spanned(proc_macro::Span::call_site(), level, this).emit();
}
}
impl Emittable for String {
fn emit(level: Level, this: Self) {
Diagnostic::spanned(proc_macro::Span::call_site(), level, this).emit();
}
}
#[cfg(feature = "syn")]
impl Emittable for syn::Error {
fn emit(level: Level, this: Self) {
for err in this.into_iter() {
Diagnostic::spanned(err.span().unwrap(), level, err.to_string()).emit();
}
}
}
#[cfg(feature = "venial")]
impl Emittable for venial::Error {
fn emit(level: Level, this: Self) {
emit_compile_error_tokens_as_diagnostics(level, this.to_compile_error().into());
}
}
#[cfg(feature = "darling")]
impl Emittable for darling_core::error::Error {
fn emit(level: Level, this: Self) {
emit_compile_error_tokens_as_diagnostics(level, this.write_errors().into());
}
}
#[macro_export]
macro_rules! emit {
($level:expr, $item:expr) => {{
$crate::Emittable::emit($level, $item);
}};
($level:expr, $spans:expr => $message:expr) => {
$crate::Diagnostic::spanned($crate::ToSpans::to_spans($spans), $level, $message).emit()
};
($level:expr, $spans:expr => $($message:tt)*) => {
$crate::Diagnostic::spanned($crate::ToSpans::to_spans($spans), $level, ::std::format!($($message)*)).emit()
};
}
#[macro_export]
macro_rules! help {
($($input:tt)*) => ($crate::emit!($crate::Level::Help, $($input)*))
}
#[macro_export]
macro_rules! note {
($($input:tt)*) => ($crate::emit!($crate::Level::Note, $($input)*))
}
#[macro_export]
macro_rules! warn {
($($input:tt)*) => ($crate::emit!($crate::Level::Warning, $($input)*))
}
#[macro_export]
macro_rules! error {
($($input:tt)*) => ($crate::emit!($crate::Level::Error, $($input)*))
}
#[macro_export]
macro_rules! bail {
($item:expr) => {{
$crate::Emittable::emit($crate::Level::Error, $item);
return ::proc_macro::TokenStream::new().into();
}};
($spans:expr => $message:expr) => {{
$crate::Diagnostic::spanned(
$crate::ToSpans::to_spans($spans),
$crate::Level::Error,
$message,
)
.emit();
return ::core::default::Default::default();
}};
}
#[macro_export]
macro_rules! fatal {
($item:expr) => {{
$crate::Emittable::emit($crate::Level::Error, $item);
$crate::quit();
}};
($spans:expr => $message:expr) => {{
$crate::Diagnostic::spanned(
$crate::ToSpans::to_spans($spans),
$crate::Level::Error,
$message,
)
.emit();
$crate::quit();
}};
}
#[macro_export]
macro_rules! mynt_assert {
($cond:expr $(,)?) => {{
if !($cond) {
$crate::fatal!(::core::stringify!($cond));
}
}};
($cond:expr, $($arg:tt)+) => {{
if !($cond) {
$crate::fatal!(::std::format!($($arg:tt)+));
}
}};
}
#[macro_export]
macro_rules! mynt_assert_eq {
($left:expr, $right:expr $(,)?) => {
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = $crate::AssertKind::Eq;
$crate::assert_failed(
kind,
&*left_val,
&*right_val,
::core::option::Option::None
);
}
}
}
};
($left:expr, $right:expr, $($arg:tt)+) => {
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = $crate::AssertKind::Eq;
$crate::assert_failed(
kind,
&*left_val,
&*right_val,
::core::option::Option::Some(::core::format_args!($($arg)+))
);
}
}
}
};
}
#[macro_export]
macro_rules! mynt_assert_ne {
($left:expr, $right:expr $(,)?) => {
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val != *right_val) {
let kind = $crate::AssertKind::Ne;
$crate::assert_failed(
kind,
&*left_val,
&*right_val,
::core::option::Option::None
);
}
}
}
};
($left:expr, $right:expr, $($arg:tt)+) => {
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val != *right_val) {
let kind = $crate::AssertKind::Ne;
$crate::assert_failed(
kind,
&*left_val,
&*right_val,
::core::option::Option::Some(::core::format_args!($($arg)+))
);
}
}
}
};
}
#[derive(Debug)]
#[doc(hidden)]
pub enum AssertKind {
Eq,
Ne,
Match, }
#[doc(hidden)]
pub fn assert_failed(
kind: AssertKind,
left: &dyn core::fmt::Debug,
right: &dyn core::fmt::Debug,
args: Option<core::fmt::Arguments<'_>>,
) -> ! {
let op = match kind {
AssertKind::Eq => "==",
AssertKind::Ne => "!=",
AssertKind::Match => "matches",
};
match args {
Some(args) => {
Diagnostic::spanned(
proc_macro::Span::call_site(),
Level::Error,
format!(
"assertion `left {op} right` failed: {args}\n left: {left:?}\n right: {right:?}"
),
)
.emit();
quit();
}
None => {
Diagnostic::spanned(
proc_macro::Span::call_site(),
Level::Error,
format!("assertion `left {op} right` failed:\n left: {left:?}\n right: {right:?}"),
)
.emit();
quit();
}
}
}
struct MyntPanicMarker;
pub fn quit() -> ! {
::std::panic::panic_any(MyntPanicMarker);
}
#[doc(hidden)]
pub fn quit_handler(err: Box<dyn std::any::Any + Send>) -> proc_macro::TokenStream {
if err.downcast_ref::<MyntPanicMarker>().is_some() {
proc_macro::TokenStream::new()
} else {
::std::panic::resume_unwind(err)
}
}
pub trait MyntResultExt<T, E>: Sized {
fn unwrap_or_quit(self) -> T
where
E: Emittable;
fn unwrap_or_compile_error<F, R>(self, f: F) -> T
where
F: FnOnce(&E) -> R,
R: Into<proc_macro::TokenStream>;
}
impl<T, E> MyntResultExt<T, E> for Result<T, E> {
fn unwrap_or_quit(self) -> T
where
E: Emittable,
{
match self {
Ok(val) => val,
Err(err) => {
Emittable::emit(Level::Error, err);
quit();
}
}
}
fn unwrap_or_compile_error<F, R>(self, f: F) -> T
where
F: FnOnce(&E) -> R,
R: Into<proc_macro::TokenStream>,
{
match self {
Ok(val) => val,
Err(err) => {
let tokens: proc_macro::TokenStream = f(&err).into();
emit_compile_error_tokens_as_diagnostics(Level::Error, tokens);
std::panic::panic_any(MyntPanicMarker);
}
}
}
}
fn emit_compile_error_tokens_as_diagnostics(level: Level, tokens: proc_macro::TokenStream) {
fn handle_tt(level: Level, tt: proc_macro::TokenTree) {
match tt {
proc_macro::TokenTree::Group(group) => {
group
.stream()
.into_iter()
.for_each(|tt| handle_tt(level, tt));
}
proc_macro::TokenTree::Literal(literal) => {
Diagnostic::spanned(literal.span(), level, literal.to_string()).emit();
}
_ => (),
}
}
tokens.into_iter().for_each(|tt| handle_tt(level, tt));
}
#[doc(hidden)]
pub fn enter_diagnostics() {
#[cfg(not(feature = "nightly"))]
fallback::enter_diagnostics();
}
#[doc(hidden)]
pub fn exit_diagnostics(tokens: &mut proc_macro::TokenStream) {
#[cfg(not(feature = "nightly"))]
fallback::exit_diagnostics(tokens);
}
#[doc(hidden)]
#[macro_export]
macro_rules! mynt_impl {
($($call:tt)*) => {
{
$crate::enter_diagnostics();
let result = ::std::panic::catch_unwind(|| $($call)*);
let mut tokens: ::proc_macro::TokenStream = match result {
Ok(value) => value.into(),
Err(err) => $crate::quit_handler(err),
};
$crate::exit_diagnostics(&mut tokens);
tokens
}
};
}
#[macro_export]
macro_rules! mynt {
{
$(#[$meta:meta])*
$vis:vis fn $name:ident
($($arg_name:ident : $arg_type:ty),*)
-> $ret:ty
$body:block
} => {
$(#[$meta])*
$vis fn $name($($arg_name: ::proc_macro::TokenStream),*) -> ::proc_macro::TokenStream {
fn f($($arg_name: $arg_type),*) -> $ret $body
$crate::mynt_impl!(f($($arg_name.into()),*))
}
};
}
#[macro_export]
macro_rules! mynt_macro {
($name:ident => $f:ident) => {
#[proc_macro]
pub fn $name(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
$crate::mynt_impl!($f(input.into()))
}
};
}
#[macro_export]
macro_rules! mynt_macro_attribute {
($name:ident => $f:ident) => {
#[proc_macro_attribute]
pub fn $name(
attr: ::proc_macro::TokenStream,
input: ::proc_macro::TokenStream,
) -> ::proc_macro::TokenStream {
$crate::mynt_impl!($f(attr.into(), input.into()))
}
};
}
#[macro_export]
macro_rules! mynt_macro_derive {
($name:ident for $trait:ident $((attributes($($attr:ident),*)))? => $f:ident) => {
#[proc_macro_derive($trait $(, attributes($($attr),*))?)]
pub fn $name(
input: ::proc_macro::TokenStream,
) -> ::proc_macro::TokenStream {
$crate::mynt_impl!($f(input.into()))
}
};
}