leo_errors/emitter/
mod.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use crate::LeoWarning;
18
19use super::LeoError;
20
21use itertools::Itertools as _;
22use std::{cell::RefCell, fmt, rc::Rc};
23
24/// Types that are sinks for compiler errors.
25pub trait Emitter {
26    /// Emit the error `err`.
27    fn emit_err(&mut self, err: LeoError);
28
29    /// Tracks last emitted error.
30    fn last_emitted_err_code(&self) -> Option<i32>;
31
32    /// Emit the warning.
33    fn emit_warning(&mut self, warning: LeoWarning);
34}
35
36/// A trivial `Emitter` using the standard error.
37pub struct StderrEmitter {
38    /// Exit code of the last emitted error.
39    last_error_code: Option<i32>,
40}
41
42impl Emitter for StderrEmitter {
43    fn emit_err(&mut self, err: LeoError) {
44        self.last_error_code = Some(err.exit_code());
45        eprintln!("{err}");
46    }
47
48    fn last_emitted_err_code(&self) -> Option<i32> {
49        self.last_error_code
50    }
51
52    fn emit_warning(&mut self, warning: LeoWarning) {
53        eprintln!("{warning}");
54    }
55}
56
57/// A buffer of `T`s.
58#[derive(Debug)]
59pub struct Buffer<T>(Vec<T>);
60
61impl<T> Default for Buffer<T> {
62    fn default() -> Self {
63        Self(Vec::new())
64    }
65}
66
67impl<T> Buffer<T> {
68    /// Push `x` to the buffer.
69    pub fn push(&mut self, x: T) {
70        self.0.push(x);
71    }
72
73    /// Extract the underlying list of Ts.
74    pub fn into_inner(self) -> Vec<T> {
75        self.0
76    }
77
78    /// Last entry to the buffer.
79    pub fn last_entry(&self) -> Option<&T> {
80        self.0.last()
81    }
82
83    // How many items in the buffer?
84    pub fn len(&self) -> usize {
85        self.0.len()
86    }
87
88    // Is the buffer empty?
89    pub fn is_empty(&self) -> bool {
90        self.0.is_empty()
91    }
92}
93
94impl<T: fmt::Display> fmt::Display for Buffer<T> {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        self.0.iter().format("").fmt(f)
97    }
98}
99
100/// A buffer of `LeoError`s.
101pub type ErrBuffer = Buffer<LeoError>;
102/// A buffer of `LeoWarning`s.
103pub type WarningBuffer = Buffer<LeoWarning>;
104
105/// An `Emitter` that collects into a list.
106#[derive(Default, Clone)]
107pub struct BufferEmitter(Rc<RefCell<ErrBuffer>>, Rc<RefCell<WarningBuffer>>);
108
109impl BufferEmitter {
110    /// Returns a new buffered emitter.
111    pub fn new() -> Self {
112        BufferEmitter(<_>::default(), <_>::default())
113    }
114
115    /// Extracts all the errors collected in this emitter.
116    pub fn extract_errs(&self) -> ErrBuffer {
117        self.0.take()
118    }
119
120    /// Extracts all the errors collected in this emitter.
121    pub fn extract_warnings(&self) -> WarningBuffer {
122        self.1.take()
123    }
124}
125
126impl Emitter for BufferEmitter {
127    fn emit_err(&mut self, err: LeoError) {
128        self.0.borrow_mut().push(err);
129    }
130
131    fn last_emitted_err_code(&self) -> Option<i32> {
132        let temp = &*self.0.borrow();
133        temp.last_entry().map(|entry| entry.exit_code())
134    }
135
136    fn emit_warning(&mut self, warning: LeoWarning) {
137        self.1.borrow_mut().push(warning);
138    }
139}
140
141/// A handler deals with errors and other compiler output.
142#[derive(Clone)]
143pub struct Handler {
144    inner: Rc<RefCell<HandlerInner>>,
145}
146
147pub struct HandlerInner {
148    /// Number of errors emitted thus far.
149    err_count: usize,
150    /// Number of warnings emitted thus far.
151    warn_count: usize,
152    /// The Emitter used.
153    emitter: Box<dyn Emitter>,
154}
155
156impl Default for Handler {
157    fn default() -> Self {
158        Self::new(StderrEmitter { last_error_code: None })
159    }
160}
161
162impl Handler {
163    /// Construct a `Handler` using the given `emitter`.
164    pub fn new<T: 'static + Emitter>(emitter: T) -> Self {
165        Handler {
166            inner: Rc::new(RefCell::new(HandlerInner { err_count: 0, warn_count: 0, emitter: Box::new(emitter) })),
167        }
168    }
169
170    /// Construct a `Handler` that will append to `buf`.
171    pub fn new_with_buf() -> (Self, BufferEmitter) {
172        let buf = BufferEmitter::default();
173        let handler = Self::new(buf.clone());
174        (handler, buf)
175    }
176
177    /// Runs `logic` provided a handler that collects all errors into the `String`,
178    /// or if there were none, returns some `T`.
179    pub fn with<T>(logic: impl for<'a> FnOnce(&'a Handler) -> Result<T, LeoError>) -> Result<T, ErrBuffer> {
180        let (handler, buf) = Handler::new_with_buf();
181        handler.extend_if_error(logic(&handler)).map_err(|_| buf.extract_errs())
182    }
183
184    /// Gets the last emitted error's exit code.
185    fn last_emitted_err_code(&self) -> Option<i32> {
186        self.inner.borrow().emitter.last_emitted_err_code()
187    }
188
189    /// Emit the error `err`.
190    pub fn emit_err<E: Into<LeoError>>(&self, err: E) {
191        let mut inner = self.inner.borrow_mut();
192        inner.err_count = inner.err_count.saturating_add(1);
193        inner.emitter.emit_err(err.into());
194    }
195
196    /// Emit the error `err`.
197    pub fn emit_warning(&self, warning: LeoWarning) {
198        let mut inner = self.inner.borrow_mut();
199        inner.warn_count = inner.warn_count.saturating_add(1);
200        inner.emitter.emit_warning(warning);
201    }
202
203    /// The number of errors thus far.
204    pub fn err_count(&self) -> usize {
205        self.inner.borrow().err_count
206    }
207
208    /// The number of warnings thus far.
209    pub fn warning_count(&self) -> usize {
210        self.inner.borrow().warn_count
211    }
212
213    /// Did we have any errors thus far?
214    pub fn had_errors(&self) -> bool {
215        self.err_count() > 0
216    }
217
218    /// Gets the last emitted error's exit code if it exists.
219    /// Then exits the program with it if it did exist.
220    pub fn last_err(&self) -> Result<(), Box<LeoError>> {
221        if let Some(code) = self.last_emitted_err_code() {
222            Err(Box::new(LeoError::LastErrorCode(code)))
223        } else {
224            Ok(())
225        }
226    }
227
228    /// Extend handler with `error` given `res = Err(error)`.
229    #[allow(clippy::result_unit_err)]
230    pub fn extend_if_error<T>(&self, res: Result<T, LeoError>) -> Result<T, ()> {
231        match res {
232            Ok(_) if self.had_errors() => Err(()),
233            Ok(x) => Ok(x),
234            Err(e) => {
235                self.emit_err(e);
236                Err(())
237            }
238        }
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245    use crate::ParserError;
246    use leo_span::{Span, create_session_if_not_set_then};
247
248    #[test]
249    fn fresh_no_errors() {
250        let handler = Handler::new(BufferEmitter::new());
251        assert_eq!(handler.err_count(), 0);
252        assert!(!handler.had_errors());
253    }
254
255    #[test]
256    fn buffer_works() {
257        create_session_if_not_set_then(|_| {
258            let res: Result<(), _> = Handler::with(|h| {
259                let s = Span::default();
260                assert_eq!(h.err_count(), 0);
261                h.emit_err(ParserError::invalid_import_list(s));
262                assert_eq!(h.err_count(), 1);
263                h.emit_err(ParserError::unexpected_eof(s));
264                assert_eq!(h.err_count(), 2);
265                Err(ParserError::spread_in_array_init(s).into())
266            });
267
268            assert_eq!(res.unwrap_err().len(), 3);
269
270            let res: Result<(), _> = Handler::with(|h| {
271                let s = Span::default();
272                h.emit_err(ParserError::invalid_import_list(s));
273                h.emit_err(ParserError::unexpected_eof(s));
274                Ok(())
275            });
276            assert_eq!(res.unwrap_err().len(), 2);
277
278            Handler::with(|_| Ok(())).unwrap();
279        })
280    }
281}