miden_diagnostics/
handler.rs

1use std::io::Write;
2use std::ops::Deref;
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::sync::Arc;
5
6use crate::term::termcolor::{Color, ColorSpec, WriteColor};
7use crate::*;
8
9/// [DiagnosticsHandler] acts as the nexus point for configuring and
10/// emitting diagnostics. It puts together many of the pieces provided
11/// by this crate to provide a useful and convenient interface for
12/// handling diagnostics throughout a compiler.
13///
14/// In order to construct a [DiagnosticsHandler], you will need a
15/// [CodeMap], an [Emitter], and a [DiagnosticsConfig] describing
16/// how the handler should behave.
17///
18/// [DiagnosticsHandler] is a thread-safe structure, and is intended
19/// to be passed around freely as needed throughout your project.
20pub struct DiagnosticsHandler {
21    emitter: Arc<dyn Emitter>,
22    pub(crate) codemap: Arc<CodeMap>,
23    err_count: AtomicUsize,
24    verbosity: Verbosity,
25    warnings_as_errors: bool,
26    no_warn: bool,
27    silent: bool,
28    pub(crate) display: crate::term::Config,
29}
30
31// We can safely implement these traits for DiagnosticsHandler,
32// as the only two non-atomic fields are read-only after creation
33unsafe impl Send for DiagnosticsHandler {}
34unsafe impl Sync for DiagnosticsHandler {}
35
36impl DiagnosticsHandler {
37    /// Create a new [DiagnosticsHandler] from the given [DiagnosticsConfig],
38    /// [CodeMap], and [Emitter] implementation.
39    pub fn new(
40        config: DiagnosticsConfig,
41        codemap: Arc<CodeMap>,
42        emitter: Arc<dyn Emitter>,
43    ) -> Self {
44        let no_warn = config.no_warn || config.verbosity > Verbosity::Warning;
45        Self {
46            emitter,
47            codemap,
48            err_count: AtomicUsize::new(0),
49            verbosity: config.verbosity,
50            warnings_as_errors: config.warnings_as_errors,
51            no_warn,
52            silent: config.verbosity == Verbosity::Silent,
53            display: config.display,
54        }
55    }
56
57    /// Get the [SourceId] corresponding to the given `filename`
58    pub fn lookup_file_id(&self, filename: impl Into<FileName>) -> Option<SourceId> {
59        let filename = filename.into();
60        self.codemap.get_file_id(&filename)
61    }
62
63    /// Returns true if the [DiagnosticsHandler] has emitted any error diagnostics
64    pub fn has_errors(&self) -> bool {
65        self.err_count.load(Ordering::Relaxed) > 0
66    }
67
68    /// Triggers a panic if the [DiagnosticsHandler] has emitted any error diagnostics
69    #[track_caller]
70    pub fn abort_if_errors(&self) {
71        if self.has_errors() {
72            FatalError.raise();
73        }
74    }
75
76    /// Emits an error message and produces a FatalError object
77    /// which can be used to terminate execution immediately
78    pub fn fatal(&self, err: impl ToString) -> FatalError {
79        self.error(err);
80        FatalError
81    }
82
83    /// Report an error diagnostic
84    pub fn error(&self, error: impl ToString) {
85        let diagnostic = Diagnostic::error().with_message(error.to_string());
86        self.emit(diagnostic);
87    }
88
89    /// Report a warning diagnostic
90    ///
91    /// If `warnings_as_errors` is set, it produces an error diagnostic instead.
92    pub fn warn(&self, warning: impl ToString) {
93        if self.warnings_as_errors {
94            return self.error(warning);
95        }
96        let diagnostic = Diagnostic::warning().with_message(warning.to_string());
97        self.emit(diagnostic);
98    }
99
100    /// Emits an informational diagnostic
101    pub fn info(&self, message: impl ToString) {
102        if self.verbosity > Verbosity::Info {
103            return;
104        }
105        let info_color = self.display.styles.header(Severity::Help);
106        let mut buffer = self.emitter.buffer();
107        buffer.set_color(info_color).ok();
108        buffer.write_all(b"info").unwrap();
109        buffer.set_color(&self.display.styles.header_message).ok();
110        writeln!(&mut buffer, ": {}", message.to_string()).unwrap();
111        buffer.reset().ok();
112        self.emitter.print(buffer).unwrap();
113    }
114
115    /// Emits a debug diagnostic
116    pub fn debug(&self, message: impl ToString) {
117        if self.verbosity > Verbosity::Debug {
118            return;
119        }
120        let mut debug_color = self.display.styles.header_message.clone();
121        debug_color.set_fg(Some(Color::Blue));
122        let mut buffer = self.emitter.buffer();
123        buffer.set_color(&debug_color).ok();
124        buffer.write_all(b"debug").unwrap();
125        buffer.set_color(&self.display.styles.header_message).ok();
126        writeln!(&mut buffer, ": {}", message.to_string()).unwrap();
127        buffer.reset().ok();
128        self.emitter.print(buffer).unwrap();
129    }
130
131    /// Emits a note diagnostic
132    pub fn note(&self, message: impl ToString) {
133        if self.verbosity > Verbosity::Info {
134            return;
135        }
136        self.emit(Diagnostic::note().with_message(message.to_string()));
137    }
138
139    /// Prints a warning-like message with the given prefix
140    ///
141    /// NOTE: This does not get promoted to an error if warnings-as-errors is set,
142    /// as it is intended for informational purposes, not issues with the code being compiled
143    pub fn notice(&self, prefix: &str, message: impl ToString) {
144        if self.verbosity > Verbosity::Info {
145            return;
146        }
147        self.write_prefixed(
148            self.display.styles.header(Severity::Warning),
149            prefix,
150            message,
151        );
152    }
153
154    /// Prints a success message with the given prefix
155    pub fn success(&self, prefix: &str, message: impl ToString) {
156        if self.silent {
157            return;
158        }
159        self.write_prefixed(self.display.styles.header(Severity::Note), prefix, message);
160    }
161
162    /// Prints an error message with the given prefix
163    pub fn failed(&self, prefix: &str, message: impl ToString) {
164        self.err_count.fetch_add(1, Ordering::Relaxed);
165        self.write_prefixed(self.display.styles.header(Severity::Error), prefix, message);
166    }
167
168    fn write_prefixed(&self, color: &ColorSpec, prefix: &str, message: impl ToString) {
169        let mut buffer = self.emitter.buffer();
170        buffer.set_color(color).ok();
171        write!(&mut buffer, "{:>12} ", prefix).unwrap();
172        buffer.reset().ok();
173        let message = message.to_string();
174        buffer.write_all(message.as_bytes()).unwrap();
175        self.emitter.print(buffer).unwrap();
176    }
177
178    /// Starts building an [InFlightDiagnostic] for rich compiler diagnostics.
179    ///
180    /// The caller is responsible for dropping/emitting the diagnostic using the
181    /// [InFlightDiagnostic] API.
182    pub fn diagnostic(&self, severity: Severity) -> InFlightDiagnostic<'_> {
183        InFlightDiagnostic::new(self, severity)
184    }
185
186    /// Emits the given diagnostic
187    #[inline(always)]
188    pub fn emit(&self, diagnostic: impl ToDiagnostic) {
189        if self.silent {
190            return;
191        }
192
193        let mut diagnostic = diagnostic.to_diagnostic();
194        match diagnostic.severity {
195            Severity::Note if self.verbosity > Verbosity::Info => return,
196            Severity::Warning if self.no_warn => return,
197            Severity::Warning if self.warnings_as_errors => {
198                diagnostic.severity = Severity::Error;
199            }
200            _ => (),
201        }
202
203        if diagnostic.severity == Severity::Error {
204            self.err_count.fetch_add(1, Ordering::Relaxed);
205        }
206
207        let mut buffer = self.emitter.buffer();
208        crate::term::emit(
209            &mut buffer,
210            &self.display,
211            self.codemap.deref(),
212            &diagnostic,
213        )
214        .unwrap();
215        self.emitter.print(buffer).unwrap();
216    }
217}