|
| 1 | +//! Unified error handling for vespera_macro. |
| 2 | +//! |
| 3 | +//! This module centralizes error handling for all proc-macro operations, |
| 4 | +//! ensuring consistent span-based error reporting at compile time. |
| 5 | +//! |
| 6 | +//! # Overview |
| 7 | +//! |
| 8 | +//! All proc-macro operations should return [`MacroResult<T>`] instead of panicking, |
| 9 | +//! allowing the Rust compiler to display user-friendly error messages with proper source locations. |
| 10 | +//! |
| 11 | +//! # Key Functions |
| 12 | +//! |
| 13 | +//! - [`err_call_site`] - Create an error at the macro call site |
| 14 | +//! - [`err_spanned`] - Create an error at a specific AST node location |
| 15 | +//! - [`IntoSynError`] - Convert other error types to syn::Error |
| 16 | +//! |
| 17 | +//! # Example |
| 18 | +//! |
| 19 | +//! ```ignore |
| 20 | +//! fn process_something(input: TokenStream) -> MacroResult<TokenStream> { |
| 21 | +//! let data = syn::parse2(input)?; |
| 22 | +//! // ... validation ... |
| 23 | +//! if invalid { |
| 24 | +//! return Err(err_call_site("invalid input format")); |
| 25 | +//! } |
| 26 | +//! Ok(quote! { /* ... */ }) |
| 27 | +//! } |
| 28 | +//! ``` |
| 29 | +
|
| 30 | +use proc_macro2::Span; |
| 31 | +use syn::Error; |
| 32 | + |
| 33 | +/// Result type for all macro operations. |
| 34 | +pub type MacroResult<T> = Result<T, Error>; |
| 35 | + |
| 36 | +/// Create an error at the call site. |
| 37 | +#[inline] |
| 38 | +pub fn err_call_site<M: std::fmt::Display>(message: M) -> Error { |
| 39 | + Error::new(Span::call_site(), message) |
| 40 | +} |
| 41 | + |
| 42 | +// The following helpers are provided for future use when we need |
| 43 | +// span-based errors or error conversion from other types. |
| 44 | + |
| 45 | +/// Create an error at the given span. |
| 46 | +#[allow(dead_code)] |
| 47 | +#[inline] |
| 48 | +pub fn err_spanned<T: quote::ToTokens, M: std::fmt::Display>(tokens: T, message: M) -> Error { |
| 49 | + Error::new_spanned(tokens, message) |
| 50 | +} |
| 51 | + |
| 52 | +/// Trait for converting other error types to syn::Error. |
| 53 | +#[allow(dead_code)] |
| 54 | +pub trait IntoSynError: Sized { |
| 55 | + fn into_syn_error(self, span: Span) -> Error; |
| 56 | + fn into_syn_error_call_site(self) -> Error { |
| 57 | + self.into_syn_error(Span::call_site()) |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +impl IntoSynError for std::io::Error { |
| 62 | + fn into_syn_error(self, span: Span) -> Error { |
| 63 | + Error::new(span, self.to_string()) |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +impl IntoSynError for String { |
| 68 | + fn into_syn_error(self, span: Span) -> Error { |
| 69 | + Error::new(span, self) |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +impl IntoSynError for &str { |
| 74 | + fn into_syn_error(self, span: Span) -> Error { |
| 75 | + Error::new(span, self) |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +impl IntoSynError for serde_json::Error { |
| 80 | + fn into_syn_error(self, span: Span) -> Error { |
| 81 | + Error::new(span, self.to_string()) |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +/// Extension trait for Result to convert errors with spans. |
| 86 | +#[allow(dead_code)] |
| 87 | +pub trait ResultExt<T, E> { |
| 88 | + fn map_syn_err(self, span: Span) -> MacroResult<T>; |
| 89 | + fn map_syn_err_call_site(self) -> MacroResult<T>; |
| 90 | +} |
| 91 | + |
| 92 | +impl<T, E: IntoSynError> ResultExt<T, E> for Result<T, E> { |
| 93 | + fn map_syn_err(self, span: Span) -> MacroResult<T> { |
| 94 | + self.map_err(|e| e.into_syn_error(span)) |
| 95 | + } |
| 96 | + fn map_syn_err_call_site(self) -> MacroResult<T> { |
| 97 | + self.map_err(|e| e.into_syn_error_call_site()) |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +/// Extension trait for Option to convert to syn::Error. |
| 102 | +#[allow(dead_code)] |
| 103 | +pub trait OptionExt<T> { |
| 104 | + fn ok_or_syn_err<M: std::fmt::Display>(self, span: Span, message: M) -> MacroResult<T>; |
| 105 | + fn ok_or_syn_err_call_site<M: std::fmt::Display>(self, message: M) -> MacroResult<T>; |
| 106 | +} |
| 107 | + |
| 108 | +impl<T> OptionExt<T> for Option<T> { |
| 109 | + fn ok_or_syn_err<M: std::fmt::Display>(self, span: Span, message: M) -> MacroResult<T> { |
| 110 | + self.ok_or_else(|| Error::new(span, message)) |
| 111 | + } |
| 112 | + fn ok_or_syn_err_call_site<M: std::fmt::Display>(self, message: M) -> MacroResult<T> { |
| 113 | + self.ok_or_else(|| err_call_site(message)) |
| 114 | + } |
| 115 | +} |
0 commit comments