sway_error/
handler.rs

1use crate::{
2    error::CompileError,
3    warning::{CompileInfo, CompileWarning},
4};
5use core::cell::RefCell;
6
7/// A handler with which you can emit diagnostics.
8#[derive(Default, Debug, Clone)]
9pub struct Handler {
10    /// The inner handler.
11    /// This construction is used to avoid `&mut` all over the compiler.
12    inner: RefCell<HandlerDiagnostics>,
13}
14
15/// Contains the actual data for `Handler`.
16/// Modelled this way to afford an API using interior mutability.
17#[derive(Default, Debug, Clone)]
18struct HandlerDiagnostics {
19    /// The sink through which errors will be emitted.
20    errors: Vec<CompileError>,
21    /// The sink through which warnings will be emitted.
22    warnings: Vec<CompileWarning>,
23    /// The sink through which infos will be emitted.
24    infos: Vec<CompileInfo>,
25}
26
27impl Handler {
28    pub fn from_parts(
29        errors: Vec<CompileError>,
30        warnings: Vec<CompileWarning>,
31        infos: Vec<CompileInfo>,
32    ) -> Self {
33        Self {
34            inner: RefCell::new(HandlerDiagnostics {
35                errors,
36                warnings,
37                infos,
38            }),
39        }
40    }
41
42    /// Emit the error `err`.
43    pub fn emit_err(&self, err: CompileError) -> ErrorEmitted {
44        self.inner.borrow_mut().errors.push(err);
45        ErrorEmitted { _priv: () }
46    }
47
48    // Compilation should be cancelled.
49    pub fn cancel(&self) -> ErrorEmitted {
50        ErrorEmitted { _priv: () }
51    }
52
53    /// Emit the warning `warn`.
54    pub fn emit_warn(&self, warn: CompileWarning) {
55        self.inner.borrow_mut().warnings.push(warn);
56    }
57
58    /// Emit the info `info`.
59    pub fn emit_info(&self, info: CompileInfo) {
60        self.inner.borrow_mut().infos.push(info);
61    }
62
63    pub fn has_errors(&self) -> bool {
64        !self.inner.borrow().errors.is_empty()
65    }
66
67    pub fn find_error(&self, f: impl FnMut(&&CompileError) -> bool) -> Option<CompileError> {
68        self.inner.borrow().errors.iter().find(f).cloned()
69    }
70
71    pub fn has_warnings(&self) -> bool {
72        !self.inner.borrow().warnings.is_empty()
73    }
74
75    pub fn scope<T>(
76        &self,
77        f: impl FnOnce(&Handler) -> Result<T, ErrorEmitted>,
78    ) -> Result<T, ErrorEmitted> {
79        let scoped_handler = Handler::default();
80        let closure_res = f(&scoped_handler);
81
82        match self.append(scoped_handler) {
83            Some(err) => Err(err),
84            None => closure_res,
85        }
86    }
87
88    /// Extract all the diagnostics from this handler.
89    pub fn consume(self) -> (Vec<CompileError>, Vec<CompileWarning>, Vec<CompileInfo>) {
90        let inner = self.inner.into_inner();
91        (inner.errors, inner.warnings, inner.infos)
92    }
93
94    pub fn append(&self, other: Handler) -> Option<ErrorEmitted> {
95        let other_has_errors = other.has_errors();
96
97        let (errors, warnings, infos) = other.consume();
98        for warn in warnings {
99            self.emit_warn(warn);
100        }
101        for err in errors {
102            self.emit_err(err);
103        }
104        for inf in infos {
105            self.emit_info(inf);
106        }
107
108        if other_has_errors {
109            Some(ErrorEmitted { _priv: () })
110        } else {
111            None
112        }
113    }
114
115    pub fn dedup(&self) {
116        let mut inner = self.inner.borrow_mut();
117        inner.errors = dedup_unsorted(inner.errors.clone());
118        inner.warnings = dedup_unsorted(inner.warnings.clone());
119    }
120
121    /// Retains only the elements specified by the predicate.
122    ///
123    /// In other words, remove all elements `e` for which `f(&e)` returns `false`.
124    /// This method operates in place, visiting each element exactly once in the
125    /// original order, and preserves the order of the retained elements.
126    pub fn retain_err<F>(&self, f: F)
127    where
128        F: FnMut(&CompileError) -> bool,
129    {
130        self.inner.borrow_mut().errors.retain(f)
131    }
132
133    // Map all errors from `other` into this handler. If any mapping returns `None` it is ignored. This
134    // method returns if any error was mapped or not.
135    pub fn map_and_emit_errors_from(
136        &self,
137        other: Handler,
138        mut f: impl FnMut(CompileError) -> Option<CompileError>,
139    ) -> Result<(), ErrorEmitted> {
140        let mut emitted = Ok(());
141
142        let (errs, _, _) = other.consume();
143        for err in errs {
144            if let Some(err) = (f)(err) {
145                emitted = Err(self.emit_err(err));
146            }
147        }
148
149        emitted
150    }
151}
152
153/// Proof that an error was emitted through a `Handler`.
154#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
155pub struct ErrorEmitted {
156    _priv: (),
157}
158
159/// We want compile errors and warnings to retain their ordering, since typically
160/// they are grouped by relevance. However, we want to deduplicate them.
161/// Stdlib dedup in Rust assumes sorted data for efficiency, but we don't want that.
162/// A hash set would also mess up the order, so this is just a brute force way of doing it
163/// with a vector.
164fn dedup_unsorted<T: PartialEq + std::hash::Hash + Clone + Eq>(mut data: Vec<T>) -> Vec<T> {
165    use std::collections::HashSet;
166
167    let mut seen = HashSet::new();
168    data.retain(|item| seen.insert(item.clone()));
169    data
170}