sway_error/
handler.rs

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