| //! This module contains data types and functions to be used for single-error reporting. |
| //! |
| //! These are supposed to be used through [`abort!`] and [`abort_call_site!`], |
| //! see [crate level documentation](crate). |
| |
| use crate::{ |
| multi::{abort_now, push_error}, |
| ResultExt, |
| }; |
| |
| use proc_macro2::{Span, TokenStream}; |
| use quote::{quote_spanned, ToTokens}; |
| |
| use std::{ |
| fmt::{Display, Formatter}, |
| ops::{Deref, DerefMut}, |
| }; |
| |
| /// Shortcut for `MacroError::new($span.into(), format!($fmt, $args...))` |
| #[macro_export] |
| macro_rules! macro_error { |
| ($span:expr, $fmt:expr, $($args:expr),+) => {{ |
| let msg = format!($fmt, $($args),*); |
| let span = $span.into(); |
| $crate::MacroError::new(span, msg) |
| }}; |
| |
| ($span:expr, $msg:expr) => {{ |
| $crate::MacroError::new($span.into(), $msg.to_string()) |
| }}; |
| } |
| |
| /// Makes a [`MacroError`] instance from provided arguments and aborts showing it. |
| /// |
| /// # Syntax |
| /// |
| /// This macro is meant to be a `panic!` drop-in replacement so its |
| /// syntax is very similar to `panic!`, but it has three forms instead of two: |
| /// |
| /// 1. "panic-format-like" form: `abort!(span_expr, format_str_literal [, format_args...]) |
| /// |
| /// First argument is a span, all the rest is passed to [`format!`] to build the error message. |
| /// |
| /// 2. "panic-single-arg-like" form: `abort!(span_expr, error_expr)` |
| /// |
| /// First argument is a span, the second is the error message, it must implement [`ToString`]. |
| /// |
| /// 3. `MacroError::abort`-like form: `abort!(error_expr)` |
| /// |
| /// Literally `MacroError::from(arg).abort()`. It's here just for convenience so [`abort!`] |
| /// can be used with instances of [`syn::Error`], [`MacroError`], [`&str`], [`String`] |
| /// and so on... |
| /// |
| #[macro_export] |
| macro_rules! abort { |
| ($span:expr, $fmt:expr, $($args:expr),*) => {{ |
| use $crate::macro_error; |
| macro_error!($span, $fmt, $($args),*).abort() |
| }}; |
| |
| ($span:expr, $msg:expr) => {{ |
| use $crate::macro_error; |
| macro_error!($span, $msg).abort() |
| }}; |
| |
| ($err:expr) => { $crate::MacroError::from($err).abort() }; |
| } |
| |
| /// Shortcut for `abort!(Span::call_site(), msg...)`. This macro |
| /// is still preferable over plain panic, see [Motivation](#motivation) |
| #[macro_export] |
| macro_rules! abort_call_site { |
| ($fmt:expr, $($args:expr),*) => {{ |
| use $crate::abort; |
| |
| let span = $crate::proc_macro2::Span::call_site(); |
| abort!(span, $fmt, $($args),*) |
| }}; |
| |
| ($msg:expr) => {{ |
| use $crate::abort; |
| |
| let span = $crate::proc_macro2::Span::call_site(); |
| abort!(span, $msg) |
| }}; |
| } |
| |
| /// An single error message in a proc macro with span info attached. |
| #[derive(Debug)] |
| pub struct MacroError { |
| pub(crate) span: Span, |
| pub(crate) msg: String, |
| } |
| |
| impl MacroError { |
| /// Create an error with the span and message provided. |
| pub fn new(span: Span, msg: String) -> Self { |
| MacroError { span, msg } |
| } |
| |
| /// A shortcut for `MacroError::new(Span::call_site(), message)` |
| pub fn call_site(msg: String) -> Self { |
| MacroError::new(Span::call_site(), msg) |
| } |
| |
| /// Replace the span info with `span`. Returns old span. |
| pub fn set_span(&mut self, span: Span) -> Span { |
| std::mem::replace(&mut self.span, span) |
| } |
| |
| /// Get the span contained. |
| pub fn span(&self) -> Span { |
| self.span |
| } |
| |
| /// Abort the proc-macro's execution and show the error. |
| /// |
| /// You're not supposed to use this function directly. |
| /// Use [`abort!`] instead. |
| pub fn abort(self) -> ! { |
| push_error(self); |
| abort_now() |
| } |
| } |
| |
| impl From<syn::Error> for MacroError { |
| fn from(e: syn::Error) -> Self { |
| MacroError::new(e.span(), e.to_string()) |
| } |
| } |
| |
| impl From<String> for MacroError { |
| fn from(msg: String) -> Self { |
| MacroError::call_site(msg) |
| } |
| } |
| |
| impl From<&str> for MacroError { |
| fn from(msg: &str) -> Self { |
| MacroError::call_site(msg.into()) |
| } |
| } |
| |
| impl ToTokens for MacroError { |
| fn to_tokens(&self, ts: &mut TokenStream) { |
| let MacroError { ref msg, ref span } = *self; |
| let msg = syn::LitStr::new(msg, *span); |
| ts.extend(quote_spanned!(*span=> compile_error!(#msg); )); |
| } |
| } |
| |
| impl Display for MacroError { |
| fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { |
| Display::fmt(&self.msg, f) |
| } |
| } |
| |
| impl<T, E: Into<MacroError>> ResultExt for Result<T, E> { |
| type Ok = T; |
| |
| fn unwrap_or_abort(self) -> T { |
| match self { |
| Ok(res) => res, |
| Err(e) => e.into().abort(), |
| } |
| } |
| |
| fn expect_or_abort(self, message: &str) -> T { |
| match self { |
| Ok(res) => res, |
| Err(e) => { |
| let MacroError { msg, span } = e.into(); |
| abort!(span, "{}: {}", message, msg); |
| } |
| } |
| } |
| } |
| |
| impl Deref for MacroError { |
| type Target = str; |
| |
| fn deref(&self) -> &str { |
| &self.msg |
| } |
| } |
| |
| impl DerefMut for MacroError { |
| fn deref_mut(&mut self) -> &mut str { |
| &mut self.msg |
| } |
| } |