formally_support/diagnostics.rs
1//
2// ::formally - the open-source formal methods toolchain
3//
4// Copyright (c) 2025 Nicola Gigante
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23//
24
25use crate::*;
26
27use std::{
28 cell::RefCell,
29 convert::Infallible,
30 fmt::{self, Debug, Display},
31 ops::Deref,
32 sync::{Arc, LazyLock, Mutex},
33};
34
35use thiserror::Error;
36
37/// Severity level of a diagnostic.
38///
39/// Note that [Level::Internal] is supposed to indicate some internal error (e.g. a violated
40/// precondition).
41#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
42pub enum Level {
43 /// Debug messages that should be usually hidden from the user
44 Debug = 0,
45 /// Non-erroneus conditions that the user should pay attention to
46 Warning = 1,
47 /// Errors
48 #[default]
49 Error = 2,
50 /// Internal errors (e.g., a violated precondition).
51 Internal = 3,
52}
53
54impl Display for Level {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 let msg = match self {
57 Level::Debug => "debug",
58 Level::Warning => "warning",
59 Level::Error => "error",
60 Level::Internal => "internal error",
61 };
62 f.write_str(msg)
63 }
64}
65
66/// Kind of note to emit in [Emitter::note]
67#[derive(Debug, Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
68pub enum NoteKind {
69 /// Note with any kind of additional information attached to a diagnostic
70 #[default]
71 Note = 0,
72 /// Note carrying backtrace information on the provenance of a diagnostic
73 Trace = 1,
74}
75
76/// Tag error type to signal that a diagnostic has been emitted
77///
78/// See the [Result] documentation for details.
79#[derive(Debug, Clone, Copy, Error)]
80pub struct DiagnosticEmitted;
81
82impl Display for DiagnosticEmitted {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "see previous error messages")
85 }
86}
87
88/// Trait for types that can emit themselves as diagnostics.
89///
90/// Even in the error handling framework set up in `formally`, in some cases the best design for a
91/// function is to return a classic `Result<T, E>` for some specific error type `E` and postpone the
92/// emission of diagnostics. In this case one can embedd in the error type all the necessary
93/// information to generate the diagnostics later, and implement [Emit] for the error type.
94///
95/// Then, the error type can be used as any other, but in addition, [DiagnosticEmitted] implements
96/// `From<E>` for any `E` that implement [Emit], and the conversion emits the diagnostics. This
97/// means that one can very conveniently apply to `E` the `?` operator in a function returning
98/// `Result<T, DiagnosticEmitted>`, if and when one desires to finally emit the diagnostics.
99///
100/// A suggested way to declare error types is using the [thiserror] crate. In this case, to inherit
101/// a sensible implementation of [Emit] for common error types one can implement [Diagnosable]
102/// instead. For an example see `formally::smt::backends::BackendError` type, and the documentation
103/// of [Diagnosable].
104pub trait Emit {
105 /// Emit the object as a diagnostic.
106 fn emit(&self) -> DiagnosticEmitted;
107}
108
109impl Emit for Infallible {
110 fn emit(&self) -> DiagnosticEmitted {
111 DiagnosticEmitted
112 }
113}
114
115impl Emit for Diagnostic {
116 fn emit(&self) -> DiagnosticEmitted {
117 Diagnostic::emitter().emit(Level::Error, self.clone());
118 DiagnosticEmitted
119 }
120}
121
122/// Trait for types that contain all the necessary information to implement the [Emit] trait.
123///
124/// Implementing [Diagnosable] for an error type requires it to be [Display] (required by
125/// [Error](std::error::Error) as well anyway) to know what to write to the diagnostic, and
126/// [Located] to obtain a [Span] for the diagnostic. In addition, the [Diagnosable::level()] method
127/// (which by default returns [Level::Error]) tells the level of the diagnostic. With all these
128/// information, [Emit] can be implemented automatically. In addition, the [Diagnosable::notes()]
129/// method, empty by default, can be implemented to emit further notes after the main diagnostic.
130///
131/// The following example uses the [thiserror] crate to declare a "diagnosable" error type very
132/// easily for a hypothetical parsing function.
133///
134/// ```rust,no_run
135/// # mod formally {
136/// # pub extern crate formally_support as support;
137/// # }
138/// use formally::support::{
139/// Span, Located, Diagnosable, DiagnosticEmitted
140/// };
141/// use thiserror::Error;
142///
143/// #[derive(Debug, Error)]
144/// pub enum ParsingErrorKind {
145/// #[error("syntax error: {0}")]
146/// SyntaxError(String),
147/// #[error("type error: {0}")]
148/// TypeError(String),
149/// #[error(transparent)]
150/// IO(std::io::Error)
151/// }
152///
153/// #[derive(Debug, Error, Located)]
154/// #[error("parsing error: {kind}")]
155/// pub struct ParsingError {
156/// kind: ParsingErrorKind,
157/// span: Option<Span>
158/// }
159///
160/// // this is sufficient to implement `Emit` as well.
161/// impl Diagnosable for ParsingError { }
162///
163/// pub enum AST {
164/// // ...
165/// }
166///
167/// pub fn parse(input: &str) -> Result<AST, ParsingError> {
168/// // ...
169/// # todo!()
170/// }
171///
172/// fn main() -> Result<(), DiagnosticEmitted> {
173/// // here, the diagnostic is emitted when `?` converts `ParsingError` to `DiagnosticEmitted`.
174/// let ast = parse("...")?;
175///
176/// // ...
177///
178/// Ok(())
179/// }
180/// ```
181pub trait Diagnosable: Display + Located {
182 /// The level at which the diagnostic has to be emitted.
183 fn level(&self) -> Level {
184 Level::Error
185 }
186
187 /// Emit additional notes after the main diagnostic.
188 fn notes(&self) -> DiagnosticEmitted {
189 DiagnosticEmitted
190 }
191}
192
193impl<T: Diagnosable> Emit for T {
194 fn emit(&self) -> DiagnosticEmitted {
195 Diagnostic::emitter().emit(self.level(), Diagnostic::new(self.span(), self));
196 self.notes()
197 }
198}
199
200impl<T: Emit> From<T> for DiagnosticEmitted {
201 fn from(err: T) -> Self {
202 err.emit()
203 }
204}
205
206/// Error type for usage in `formally`.
207///
208/// `formally` uses common Rust error handling strategies involving the [Result] type, but the
209/// actual content of errors is not contained in the [Result] `Err` variant. Instead, errors are
210/// emitted as diagnostics using [Emitter] and just the existence of the failure is propagated back
211/// to the called with an `Err(DiagnosticEmitted)` value.
212///
213/// As the name says, when returning `Err(DiagnosticEmitted)` one should before emit a diagnostic
214/// (usually an error) through an [Emitter], usually with the [error!] macro.
215pub type Result<T, E = DiagnosticEmitted> = std::result::Result<T, E>;
216
217/// Trait for objects capable of emitting (or routing) diagnostics.
218///
219/// [Emitter] is the core of `formally`'s error handling strategy. Diagnostics, in the form of
220/// [Diagnostic] objects, are just messages sent to the user possibly associated with a source span.
221/// Emitters can potentially do whatever they wish with a diagnostic, including rendering them to
222/// the user, ignoring them, delaying them (as with [BatchEmitter]), or relaying them to other
223/// emitters, possibly after some processing.
224///
225/// The emission of a diagnostic can be followed by one or more notes that allow to specify
226/// additional information associated with different source spans.
227///
228/// For consistency of the user experience, it is important that everybody uses the same [Emitter].
229/// For this reason, a global [Emitter] is always available from [Diagnostic::emitter()], which
230/// returns a global emitter object whose default value can be changed with
231/// [Diagnostic::set_default_global_emitter()]. The global emitter can be temporarily changed in a
232/// thread-local and scoped way with [Diagnostic::with()].
233///
234/// Different implementations of [Emitter] are provided and more will be added.
235/// Currently, we have:
236/// - [StdErrEmitter], to direct formatted messages to the standard error stream. This is currently
237/// the default global emitter.
238/// - [BatchEmitter], to group diagnostics and emitting them all at once when requested
239/// - [NullEmitter], to suppress any diagnostic.
240///
241/// [Diagnostic] objects are usually not constructed and emitted directly but using the [debug],
242/// [warning] and [error] macros. Similarly, notes are usually emitted with the [note] and [trace]
243/// macros. These macros use the global [Emitter].
244///
245/// Example:
246/// ```
247/// # use formally_support::*;
248/// # let span = Span::Span {
249/// # origin: Default::default(),
250/// # begin: Default::default(),
251/// # end: Default::default()
252/// # };
253/// # let ident = "";
254/// error!(span, "unable to parse identifier: {}", ident);
255/// ```
256pub trait Emitter {
257 /// Emit a diagnostic.
258 fn emit(&self, level: Level, diag: Diagnostic);
259
260 /// Emit a note.
261 fn note(&self, kind: NoteKind, note: Diagnostic);
262}
263
264impl<E: Deref<Target: Emitter>> Emitter for Mutex<E> {
265 fn emit(&self, level: Level, diag: Diagnostic) {
266 self.lock().unwrap().emit(level, diag)
267 }
268
269 fn note(&self, kind: NoteKind, note: Diagnostic) {
270 self.lock().unwrap().note(kind, note)
271 }
272}
273
274struct DefaultGlobalEmitter;
275struct GlobalEmitter;
276
277impl Emitter for DefaultGlobalEmitter {
278 fn emit(&self, level: Level, diag: Diagnostic) {
279 DEFAULT_GLOBAL_EMITTER.emit(level, diag)
280 }
281
282 fn note(&self, kind: NoteKind, note: Diagnostic) {
283 DEFAULT_GLOBAL_EMITTER.note(kind, note)
284 }
285}
286
287impl Emitter for GlobalEmitter {
288 fn emit(&self, level: Level, diag: Diagnostic) {
289 GLOBAL_EMITTER.with_borrow(|e| e.emit(level, diag))
290 }
291
292 fn note(&self, kind: NoteKind, note: Diagnostic) {
293 GLOBAL_EMITTER.with_borrow(|e| e.note(kind, note))
294 }
295}
296
297static DEFAULT_GLOBAL_EMITTER: LazyLock<Mutex<Arc<dyn Send + Sync + Emitter>>> =
298 LazyLock::new(|| Mutex::new(Arc::new(StdErrEmitter::new())));
299
300thread_local! {
301 static GLOBAL_EMITTER: RefCell<Arc<dyn Emitter>> = RefCell::new(Arc::new(DefaultGlobalEmitter));
302}
303
304/// Information attached to a diagnostic.
305///
306/// See the [Emitter] trait for general information on the error reporting strategy of `formally`.
307///
308/// See the [emitter()](Diagnostic::emitter) function for how to reach the global [Emitter] object.
309#[derive(Debug, Clone)]
310pub struct Diagnostic {
311 /// The source span associated with this diagnostic.
312 pub span: Option<Span>,
313
314 /// The message attached to this diagnostic.
315 pub msg: String,
316}
317
318impl Diagnostic {
319 /// Constructs a diagnostic using any [Display] type as the source of the message.
320 pub fn new(span: Option<Span>, msg: impl Display) -> Diagnostic {
321 Diagnostic {
322 span,
323 msg: format!("{msg}"),
324 }
325 }
326
327 /// Return a reference to the current default global [Emitter].
328 ///
329 /// This is the emitter used as the global emitter (returned by [Diagnostic::emitter()]) unless
330 /// a call of [Diagnostic::with()] is ongoing in the current thread.
331 pub fn default_global_emitter() -> &'static dyn Emitter {
332 &DefaultGlobalEmitter
333 }
334
335 /// Set the default global [Emitter].
336 ///
337 /// This is the emitter used as the global emitter (returned by [Diagnostic::emitter()]) unless
338 /// a call of [Diagnostic::with()] is ongoing in the current thread.
339 pub fn set_default_global_emitter(emitter: impl 'static + Send + Sync + Emitter) {
340 *DEFAULT_GLOBAL_EMITTER.lock().unwrap() = Arc::new(emitter);
341 }
342
343 /// Return the current global [Emitter].
344 pub fn emitter() -> &'static dyn Emitter {
345 &GlobalEmitter
346 }
347
348 /// Temporarily changes the global [Emitter].
349 ///
350 /// Changes the global emitter for the current thread, executes the given function, and then
351 /// restores the old global emitter. If the function panics, the old emitter is restored and the
352 /// panic propagated.
353 pub fn with<E, F, R>(emitter: E, f: F) -> R
354 where
355 E: 'static + Emitter,
356 F: std::panic::UnwindSafe + FnOnce() -> R,
357 {
358 let old = GLOBAL_EMITTER.with_borrow(|e| e.clone());
359 GLOBAL_EMITTER.set(Arc::new(emitter));
360
361 let result = std::panic::catch_unwind(f);
362
363 GLOBAL_EMITTER.set(old);
364
365 match result {
366 Ok(v) => v,
367 Err(e) => std::panic::resume_unwind(e),
368 }
369 }
370}
371
372/// [Emitter] that throws away any diagnostic.
373///
374/// Any diagnostic emitted through this [NullEmitter] is completely discarded. This is useful when
375/// we are interested in the success or failure of something that may emit diagnostics but not
376/// in the diagnostics themselves. This is also equivalent to use a [BatchEmitter] without ever
377/// calling the [commit()](BatchEmitter::commit) method, but is more efficient.
378pub struct NullEmitter;
379
380impl Emitter for NullEmitter {
381 fn emit(&self, _level: Level, _diag: Diagnostic) {}
382
383 fn note(&self, _kind: NoteKind, _note: Diagnostic) {}
384}
385
386/// [Emitter] that delays the emission of diagnostics until an explicit commit.
387///
388/// [BatchEmitter] is useful whenever any sort of *error recovery* is possible, thus allowing to
389/// ignore the diagnostics associated with the recovered error.
390///
391/// [BatchEmitter] works by relaying all the received diagnostics to an inner [Emitter] instance,
392/// but only after a call to [BatchEmitter::commit]. If the object is destroyed before a call to
393/// [BatchEmitter::commit], the pending dianostics are discarded.
394pub struct BatchEmitter<'e> {
395 emitter: &'e dyn Emitter,
396 batched: RefCell<Vec<Batched>>,
397}
398
399enum Batched {
400 Diagnostic(Level, Diagnostic),
401 Note(NoteKind, Diagnostic),
402}
403
404impl<'e> BatchEmitter<'e> {
405 /// Constructs a new [BatchEmitter] relaying to the provided [Emitter] instance.
406 pub fn new(emitter: &'e dyn Emitter) -> BatchEmitter<'e> {
407 BatchEmitter {
408 emitter,
409 batched: RefCell::default(),
410 }
411 }
412
413 /// Flushes the pending diagnostics to the underlying [Emitter]
414 pub fn commit(self) {
415 for batched in self.batched.into_inner() {
416 match batched {
417 Batched::Diagnostic(level, diag) => self.emitter.emit(level, diag),
418 Batched::Note(kind, diag) => self.emitter.note(kind, diag),
419 }
420 }
421 }
422}
423
424impl Emitter for BatchEmitter<'_> {
425 /// Emit a diagnostic.
426 ///
427 /// Note that diagnostics and notes are relayed to the underlying [Emitter] only after a call to
428 /// [BatchEmitter::commit].
429 fn emit(&self, level: Level, diag: Diagnostic) {
430 self.batched
431 .borrow_mut()
432 .push(Batched::Diagnostic(level, diag));
433 }
434
435 /// Emit a note.
436 ///
437 /// Note that diagnostics and notes are relayed to the underlying [Emitter] only after a call to
438 /// [BatchEmitter::commit].
439 fn note(&self, kind: NoteKind, note: Diagnostic) {
440 self.batched.borrow_mut().push(Batched::Note(kind, note));
441 }
442}
443
444///
445/// An [Emitter] rendering diagnositcs on the standard error stream.
446///
447/// This is a simple [Emitter] to just print diagnostic messages to the standard error stream of the
448/// process with attached the source span information.
449///
450/// This type is currently the default global [Emitter].
451pub struct StdErrEmitter {
452 trace_emitted: Mutex<bool>,
453 note_emitted: Mutex<bool>,
454}
455
456impl StdErrEmitter {
457 /// Constructs a new [StdErrEmitter].
458 pub fn new() -> StdErrEmitter {
459 StdErrEmitter {
460 trace_emitted: Mutex::new(false),
461 note_emitted: Mutex::new(false),
462 }
463 }
464
465 fn print(&self, preamble: impl Display, span: Option<Span>, msg: impl Display) {
466 if let Some(span) = span {
467 eprint!("{span}:")
468 }
469
470 eprintln!("{preamble}: {msg}");
471 }
472}
473
474impl Default for StdErrEmitter {
475 fn default() -> Self {
476 StdErrEmitter::new()
477 }
478}
479
480impl Emitter for StdErrEmitter {
481 fn emit(&self, level: Level, diag: Diagnostic) {
482 let mut note_emitted = self.note_emitted.lock().unwrap();
483 let mut trace_emitted = self.trace_emitted.lock().unwrap();
484
485 if *note_emitted {
486 eprintln!()
487 }
488 self.print(level, diag.span, diag.msg);
489 *trace_emitted = false;
490 *note_emitted = false;
491 }
492
493 fn note(&self, kind: NoteKind, note: Diagnostic) {
494 if kind == NoteKind::Trace {
495 let mut trace_emitted = self.trace_emitted.lock().unwrap();
496 if *trace_emitted {
497 return;
498 } else {
499 *trace_emitted = true;
500 }
501 }
502 *self.note_emitted.lock().unwrap() = true;
503 self.print("note", note.span, note.msg);
504 }
505}
506
507#[doc(hidden)]
508#[macro_export]
509macro_rules! diagnose_impl {
510 ($ty:ident::$level:ident, $emitter:expr, $span:expr, $arg:literal) => {
511 $crate::diagnose_impl!(
512 impl $ty, $level, $emitter, $span.clone().into(), format!($arg)
513 )
514 };
515 ($ty:ident::$level:ident, $emitter:expr, $span:expr, $arg:expr) => {
516 $crate::diagnose_impl!(
517 impl $ty, $level, $emitter, $span.clone().into(), $arg
518 )
519 };
520 ($ty:ident::$level:ident, $emitter:expr, $span:expr, $($args:tt)+) => {
521 $crate::diagnose_impl!(
522 impl $ty, $level, $emitter, $span.clone().into(), format!($($args)*)
523 )
524 };
525 (impl NoteKind, $level:ident, $emitter:expr, $span:expr, $arg:expr) => {
526 $crate::Emitter::note(
527 $emitter,
528 $crate::NoteKind::$level,
529 $crate::Diagnostic::new($span, $arg),
530 )
531 };
532 (impl Level, $level:ident, $emitter:expr, $span:expr, $arg:expr) => {
533 $crate::Emitter::emit(
534 $emitter,
535 $crate::Level::$level,
536 $crate::Diagnostic::new($span, $arg),
537 )
538 };
539}
540
541/// Generic and more flexible version of the [error!], [warning!], etc. macros.
542///
543/// The [error!], [warning!], [debug!], [internal!], [note!], and [trace!] macro are based on
544/// invocations of [diagnose!] which is the most flexible and generic of the set.
545///
546/// It accepts as a first argument a literal value of [Level] or [NoteKind], depending on which it
547/// emits a diagnostic or a note. It accepts then an emitter, a span, and a message, optionally
548/// formatted à la [format!] followed by the format arguments.
549#[macro_export]
550macro_rules! diagnose {
551 ($ty:ident::$level:ident, $emitter:expr, $span:expr, $($args:tt)+) => {
552 $crate::diagnose_impl!($ty::$level, $emitter, $span, $($args)*)
553 }
554}
555
556/// Emit a note of kind [NoteKind::Note] with a formatted message.
557///
558/// Notes are meant to be attached to diagnostics, so one should be sure that a diagnostic (e.g., an
559/// error) has been emitted before emitting a note. Notes are used to provide additional
560/// information, possibly attached to different source spans, to a previously emitted diagnostic.
561///
562/// Example:
563/// ```
564/// # use formally_support::*;
565/// # let span = Span::default();
566/// # let ident = "";
567/// error!(span, "unable to parse identifier: {}", ident);
568/// note!(span, "it seems to be a number instead");
569#[macro_export]
570macro_rules! note {
571 ($($args:tt)+) => {
572 $crate::diagnose!(NoteKind::Note, $crate::Diagnostic::emitter(), $($args)*)
573 };
574}
575
576/// Emit a note of kind [NoteKind::Trace] with a formatted message.
577///
578/// Trace notes are meant to provide information about the execution history that led to the
579/// emission of the previous diagnostic. This is usally done by matching on a [Result] and emit the
580/// trace note in the `Err` case.
581///
582/// Example:
583/// ```rust,no_run
584/// # use formally_support::*;
585/// # fn parse_message(s: &str) -> Result<i32> {
586/// # let span = Span::default();
587/// # fn parse_int(s: &str) -> Result<i32> { Ok(0) }
588/// # fn something(i: i32) -> i32 { i }
589/// match parse_int(s) {
590/// Ok(value) => Ok(something(value)),
591/// Err(_) => {
592/// trace!(span, "while parsing a message");
593/// Err(DiagnosticEmitted)
594/// }
595/// }
596/// # }
597/// ```
598#[macro_export]
599macro_rules! trace {
600 ($($args:tt)+) => {
601 $crate::diagnose!(NoteKind::Trace, $crate::Diagnostic::emitter(), $($args)*)
602 };
603}
604
605/// Emit a diagnostic of level [Level::Internal] with a formatted message.
606///
607/// Example:
608/// ```
609/// # use formally_support::*;
610/// # let span = Span::default();
611/// internal!(span, "violated precondition: index out of bounds");
612/// ```
613#[macro_export]
614macro_rules! internal {
615 ($($args:tt)+) => {
616 $crate::diagnose!(Level::Internal, $crate::Diagnostic::emitter(), $($args)*)
617 };
618}
619
620/// Emit a diagnostic of level [Level::Error] with a formatted message.
621///
622/// Example:
623/// ```
624/// # use formally_support::*;
625/// # let span = Span::default();
626/// # let ident = "";
627/// error!(span, "unable to parse identifier: {}", ident);
628/// ```
629#[macro_export]
630macro_rules! error {
631 ($($args:tt)+) => {
632 $crate::diagnose!(Level::Error, $crate::Diagnostic::emitter(), $($args)*)
633 };
634}
635
636/// Emits a diagnostic of level [Level::Warning] with a formatted message.
637///
638/// Warnings are conditions that do not harm the correct execution of the task at hand but that the
639/// user should pay attention to.
640///
641/// Example:
642/// ```
643/// # use formally_support::*;
644/// # let span = Span::default();
645/// # let ident = "";
646/// warning!(span, "misleading identifier: {}", ident);
647/// ```
648#[macro_export]
649macro_rules! warning {
650 ($($args:tt)+) => {
651 $crate::diagnose!(Level::Warning, $crate::Diagnostic::emitter(), $($args)*)
652 };
653}
654
655/// Emits a diagnostic of level [Level::Debug] with a formatted message.
656///
657/// Debug messages are meant to be hidden from the user by default and shown only when requested
658/// (although this behavior is completely in the hands of the selected [Emitter]), so they should
659/// not contain information that cannot be ignored.
660///
661/// Example:
662/// ```
663/// # use formally_support::*;
664/// # let span = Span::default();
665/// let x = 42;
666/// debug!(span, "variable x holds: {}", x);
667/// ```
668#[macro_export]
669macro_rules! debug {
670 ($($args:tt)+) => {
671 $crate::diagnose!(Level::Debug, $crate::Diagnostic::emitter(), $($args)*)
672 };
673}