blueprint_starlark_syntax/
error.rs1use std::fmt;
19use std::mem;
20
21use crate::call_stack::CallStack;
22use crate::codemap::CodeMap;
23use crate::codemap::FileSpan;
24use crate::codemap::Span;
25use crate::diagnostic::WithDiagnostic;
26use crate::diagnostic::diagnostic_display;
27
28pub struct Error(pub(crate) WithDiagnostic<ErrorKind>);
36
37const _: () = assert!(mem::size_of::<Error>() == mem::size_of::<usize>());
38
39impl Error {
40 #[cold]
42 pub fn new_kind(kind: ErrorKind) -> Self {
43 Self(WithDiagnostic::new_empty(kind))
44 }
45
46 #[cold]
48 pub fn new_spanned(kind: ErrorKind, span: Span, codemap: &CodeMap) -> Self {
49 Self(WithDiagnostic::new_spanned(kind, span, codemap))
50 }
51
52 #[cold]
54 pub fn new_other(e: impl Into<anyhow::Error>) -> Self {
55 Self(WithDiagnostic::new_empty(ErrorKind::Other(e.into())))
56 }
57
58 #[cold]
60 pub fn new_native(e: impl Into<anyhow::Error>) -> Self {
61 Self(WithDiagnostic::new_empty(ErrorKind::Native(e.into())))
62 }
63
64 #[cold]
66 pub fn new_value(e: impl Into<anyhow::Error>) -> Self {
67 Self(WithDiagnostic::new_empty(ErrorKind::Value(e.into())))
68 }
69
70 pub fn kind(&self) -> &ErrorKind {
72 self.0.inner()
73 }
74
75 pub fn into_kind(self) -> ErrorKind {
77 self.0.into_inner()
78 }
79
80 pub fn has_diagnostic(&self) -> bool {
81 self.0.span().is_some() || !self.0.call_stack().is_empty()
82 }
83
84 #[cold]
86 pub fn into_anyhow(self) -> anyhow::Error {
87 struct Wrapped(Error);
88
89 impl fmt::Display for Wrapped {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 fmt::Display::fmt(&self.0, f)
92 }
93 }
94
95 impl fmt::Debug for Wrapped {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 fmt::Debug::fmt(&self.0, f)
98 }
99 }
100
101 impl std::error::Error for Wrapped {
102 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
103 self.0.kind().source()
104 }
105 }
106
107 anyhow::Error::new(Wrapped(self))
108 }
109
110 pub fn without_diagnostic<'a>(&'a self) -> impl fmt::Debug + fmt::Display + 'a {
115 self.0.inner()
116 }
117
118 pub fn span(&self) -> Option<&FileSpan> {
119 self.0.span()
120 }
121
122 pub fn call_stack(&self) -> &CallStack {
123 self.0.call_stack()
124 }
125
126 pub fn set_span(&mut self, span: Span, codemap: &CodeMap) {
128 self.0.set_span(span, codemap);
129 }
130
131 pub fn set_call_stack(&mut self, call_stack: impl FnOnce() -> CallStack) {
133 self.0.set_call_stack(call_stack);
134 }
135
136 pub fn eprint(&self) {
143 if self.has_diagnostic() {
144 let mut stderr = String::new();
145 diagnostic_display(&self.0, true, &mut stderr, true).unwrap();
146 eprint!("{stderr}");
147 } else {
148 eprintln!("{self:#}")
149 }
150 }
151
152 pub fn into_internal_error(self) -> Error {
154 if let ErrorKind::Internal(_) = self.kind() {
155 self
156 } else {
157 Error(self.0.map(ErrorKind::into_internal_error))
158 }
159 }
160}
161
162fn fmt_impl(this: &Error, is_debug: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 if this.has_diagnostic() {
164 let with_context = (f.alternate() || is_debug) && this.kind().source().is_some();
166 diagnostic_display(&this.0, false, f, with_context)
167 } else {
168 fmt::Display::fmt(&this.without_diagnostic(), f)
169 }
170}
171
172impl fmt::Display for Error {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 fmt_impl(self, false, f)
175 }
176}
177
178impl fmt::Debug for Error {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 fmt_impl(self, true, f)
181 }
182}
183
184#[non_exhaustive]
186pub enum ErrorKind {
187 Fail(anyhow::Error),
189 StackOverflow(anyhow::Error),
191 Value(anyhow::Error),
195 Function(anyhow::Error),
197 Scope(anyhow::Error),
199 Parser(anyhow::Error),
201 Freeze(anyhow::Error),
203 Internal(anyhow::Error),
205 Native(anyhow::Error),
210 Other(anyhow::Error),
214}
215
216impl ErrorKind {
217 pub fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
219 match self {
220 Self::Fail(_) => None,
221 Self::StackOverflow(_) => None,
222 Self::Value(_) => None,
223 Self::Function(_) => None,
224 Self::Scope(_) => None,
225 Self::Freeze(_) => None,
226 Self::Parser(_) => None,
227 Self::Internal(_) => None,
228 Self::Native(e) => e.source(),
229 Self::Other(e) => e.source(),
230 }
231 }
232
233 pub(crate) fn into_internal_error(self) -> ErrorKind {
235 match self {
236 ErrorKind::Internal(e)
237 | ErrorKind::Fail(e)
238 | ErrorKind::Value(e)
239 | ErrorKind::Function(e)
240 | ErrorKind::Scope(e)
241 | ErrorKind::Freeze(e)
242 | ErrorKind::Parser(e)
243 | ErrorKind::StackOverflow(e)
244 | ErrorKind::Native(e)
245 | ErrorKind::Other(e) => ErrorKind::Internal(e),
246 }
247 }
248}
249
250impl fmt::Debug for ErrorKind {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 match self {
253 Self::Fail(s) => write!(f, "fail:{s}"),
254 Self::Value(e) => fmt::Debug::fmt(e, f),
255 Self::StackOverflow(e) => fmt::Debug::fmt(e, f),
256 Self::Function(e) => fmt::Debug::fmt(e, f),
257 Self::Scope(e) => fmt::Debug::fmt(e, f),
258 Self::Freeze(e) => fmt::Debug::fmt(e, f),
259 Self::Parser(e) => fmt::Debug::fmt(e, f),
260 Self::Internal(e) => write!(f, "Internal error: {e}"),
261 Self::Native(e) => fmt::Debug::fmt(e, f),
262 Self::Other(e) => fmt::Debug::fmt(e, f),
263 }
264 }
265}
266
267impl fmt::Display for ErrorKind {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 match self {
270 Self::Fail(s) => write!(f, "fail:{s}"),
271 Self::StackOverflow(e) => fmt::Display::fmt(e, f),
272 Self::Value(e) => fmt::Display::fmt(e, f),
273 Self::Function(e) => fmt::Display::fmt(e, f),
274 Self::Scope(e) => fmt::Display::fmt(e, f),
275 Self::Freeze(e) => fmt::Display::fmt(e, f),
276 Self::Parser(e) => fmt::Display::fmt(e, f),
277 Self::Internal(e) => write!(f, "Internal error: {e}"),
278 Self::Native(e) => fmt::Display::fmt(e, f),
279 Self::Other(e) => fmt::Display::fmt(e, f),
280 }
281 }
282}
283
284impl From<anyhow::Error> for Error {
285 #[cold]
286 fn from(e: anyhow::Error) -> Self {
287 Self(WithDiagnostic::new_empty(ErrorKind::Other(e)))
288 }
289}
290
291pub trait StarlarkResultExt<T> {
292 fn into_anyhow_result(self) -> anyhow::Result<T>;
293}
294
295impl<T> StarlarkResultExt<T> for crate::Result<T> {
296 #[inline]
297 fn into_anyhow_result(self) -> anyhow::Result<T> {
298 self.map_err(Error::into_anyhow)
299 }
300}
301
302#[doc(hidden)]
303#[cold]
304pub fn internal_error_impl(args: fmt::Arguments<'_>) -> Error {
305 Error::new_kind(ErrorKind::Internal(anyhow::anyhow!("{}", args)))
306}
307
308#[doc(hidden)]
309#[cold]
310pub fn other_error_impl(args: fmt::Arguments<'_>) -> Error {
311 Error::new_kind(ErrorKind::Other(anyhow::anyhow!("{}", args)))
312}
313
314#[doc(hidden)]
315#[cold]
316pub fn value_error_impl(args: fmt::Arguments<'_>) -> Error {
317 Error::new_kind(ErrorKind::Value(anyhow::anyhow!("{}", args)))
318}
319
320#[doc(hidden)]
321#[cold]
322pub fn function_error_impl(args: fmt::Arguments<'_>) -> Error {
323 Error::new_kind(ErrorKind::Function(anyhow::anyhow!("{}", args)))
324}
325
326#[macro_export]
328macro_rules! internal_error {
329 ($format:literal) => {
330 internal_error!($format,)
331 };
332 ($format:literal, $($args:tt)*) => {
333 $crate::error::internal_error_impl(format_args!($format, $($args)*))
334 };
335}
336
337#[macro_export]
338macro_rules! other_error {
339 ($format:literal) => {
340 other_error!($format,)
341 };
342 ($format:literal, $($args:tt)*) => {
343 $crate::error::other_error_impl(format_args!($format, $($args)*))
344 };
345}
346
347#[macro_export]
348macro_rules! value_error {
349 ($format:literal) => {
350 value_error!($format,)
351 };
352 ($format:literal, $($args:tt)*) => {
353 $crate::error::value_error_impl(format_args!($format, $($args)*))
354 };
355}
356
357#[macro_export]
358macro_rules! function_error {
359 ($format:literal) => {
360 function_error!($format,)
361 };
362 ($format:literal, $($args:tt)*) => {
363 $crate::error::function_error_impl(format_args!($format, $($args)*))
364 };
365}