spade/
error_handling.rs

1use std::sync::{Arc, RwLock};
2
3use spade_codespan_reporting::term::termcolor::Buffer;
4use spade_diagnostics::{
5    diag_list::DiagList, CodeBundle, CompilationError, DiagHandler, Diagnostic,
6};
7
8pub struct ErrorHandler<'a> {
9    failed: bool,
10    failed_now: bool,
11    pub error_buffer: &'a mut Buffer,
12    pub diag_handler: DiagHandler,
13    /// Using a RW lock here is just a lazy way of managing the ownership of code to
14    /// be able to report errors even while modifying CodeBundle
15    pub code: Arc<RwLock<CodeBundle>>,
16}
17
18impl<'a> ErrorHandler<'a> {
19    pub fn new(
20        error_buffer: &'a mut Buffer,
21        diag_handler: DiagHandler,
22        code: Arc<RwLock<CodeBundle>>,
23    ) -> Self {
24        ErrorHandler {
25            failed: false,
26            failed_now: false,
27            error_buffer,
28            diag_handler,
29            code: Arc::clone(&code),
30        }
31    }
32
33    pub fn set_failed(&mut self) {
34        self.failed = true;
35        self.failed_now = true;
36    }
37
38    pub fn errors_are_recoverable(&mut self) {
39        self.failed_now = false;
40    }
41
42    pub fn failed(&self) -> bool {
43        self.failed
44    }
45
46    pub fn failed_now(&mut self) -> bool {
47        let result = self.failed_now;
48        self.failed_now = false;
49        result
50    }
51
52    pub fn report(&mut self, err: &impl CompilationError) {
53        let is_fatal = match err.severity() {
54            spade_diagnostics::diagnostic::DiagnosticLevel::Bug => true,
55            spade_diagnostics::diagnostic::DiagnosticLevel::Error => true,
56            spade_diagnostics::diagnostic::DiagnosticLevel::Warning => false,
57        };
58        if is_fatal {
59            self.failed = true;
60            self.failed_now = true;
61        }
62        err.report(
63            self.error_buffer,
64            &self.code.read().unwrap(),
65            &mut self.diag_handler,
66        );
67    }
68
69    pub fn drain_diag_list(&mut self, diag_list: &mut DiagList) {
70        for diag in diag_list.drain() {
71            self.report(&diag)
72        }
73    }
74}
75
76pub trait Reportable<T> {
77    /// Report the error, then discard the error, returning Some if it was Ok
78    fn or_report(self, errors: &mut ErrorHandler) -> Option<T>;
79
80    // Report the error and continue without modifying the result
81    fn report(self, errors: &mut ErrorHandler) -> Self;
82
83    fn or_do_report<'a>(self, errors: impl FnOnce() -> &'a mut ErrorHandler<'a>) -> Option<T>;
84}
85
86impl<T, E> Reportable<T> for Result<T, E>
87where
88    E: CompilationError,
89{
90    fn report(self, errors: &mut ErrorHandler) -> Self {
91        if let Err(e) = &self {
92            errors.report(e);
93        }
94        self
95    }
96
97    fn or_report(self, errors: &mut ErrorHandler) -> Option<T> {
98        self.report(errors).ok()
99    }
100
101    fn or_do_report<'a>(self, errors: impl FnOnce() -> &'a mut ErrorHandler<'a>) -> Option<T> {
102        if self.is_err() {
103            let errors = (errors)();
104            self.or_report(errors)
105        } else {
106            self.ok()
107        }
108    }
109}
110
111impl Reportable<()> for Diagnostic {
112    fn or_report(self, errors: &mut ErrorHandler) -> Option<()> {
113        errors.report(&self);
114        None
115    }
116
117    fn report(self, errors: &mut ErrorHandler) -> Self {
118        errors.report(&self);
119        self
120    }
121
122    fn or_do_report<'a>(self, errors: impl FnOnce() -> &'a mut ErrorHandler<'a>) -> Option<()> {
123        let errors = (errors)();
124        self.or_report(errors);
125        None
126    }
127}