Skip to main content

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}