chandeliers_err/
lib.rs

1//! Error accumulator and panic helpers
2
3#![feature(lint_reasons)]
4#![warn(
5    missing_docs,
6    unused_crate_dependencies,
7    unused_macro_rules,
8    variant_size_differences,
9    clippy::allow_attributes,
10    clippy::allow_attributes_without_reason,
11    clippy::expect_used,
12    clippy::indexing_slicing,
13    clippy::missing_docs_in_private_items,
14    clippy::multiple_inherent_impl,
15    clippy::panic,
16    clippy::pedantic,
17    clippy::str_to_string,
18    clippy::unreachable,
19    clippy::unwrap_used,
20    clippy::use_debug
21)]
22
23pub mod transparent;
24pub use transparent::Transparent;
25
26pub mod error;
27pub use error::*; // reexporting all error message constructors
28
29/// Reexport of `Span`, but this time forgeable in tests and
30/// transparent to `Eq` and `Hash`.
31pub type Span = Transparent<proc_macro2::Span>;
32
33/// Repository of this project, to be displayed in error messages.
34#[macro_export]
35macro_rules! repo {
36    () => {
37        "https://github.com/Vanille-N/chandeliers"
38    };
39}
40
41/// Location in the source code.
42#[macro_export]
43macro_rules! here {
44    () => {
45        concat!(file!(), ":", line!(), ":", column!())
46    };
47}
48
49/// Generate an error message better than just "proc macro panicked".
50#[macro_export]
51macro_rules! abort {
52    ($($err:tt)*) => {{
53        std::panic!("
54Chandeliers panicked: \x1b[1;31m{}.\x1b[0m
55This error occured in \x1b[1;35m{}\x1b[0m
56
57If you are not a developper of Chandeliers and you see this message then this is a bug.
58I'd be grateful if you could report this error at \x1b[33m{}\x1b[0m
59with the code that produced it and the version of Chandeliers you are using.
60",
61            format!($($err)*),
62            $crate::here!(),
63            $crate::repo!(),
64        );
65    }};
66}
67
68/// Special instance of `panic` for code that should be trivially unreachable.
69#[macro_export]
70macro_rules! malformed {
71    () => {{
72        ::chandeliers_err::abort!("Entered unreachable code");
73    }};
74}
75
76/// Special instance of `panic` for assertions.
77#[macro_export]
78macro_rules! consistency {
79    ($cond:expr, $($msg:tt)*) => {{
80        if !$cond {
81            ::chandeliers_err::abort!($($msg)*);
82        }
83    }};
84}
85
86/// Error accumulator to be able to
87/// - emit several fatal errors at once
88/// - emit warnings
89#[derive(Default)]
90pub struct EAccum {
91    /// Errors encountered during the analysis.
92    err: Vec<Error>,
93    /// Non-fatal warnings encountered during the analysis.
94    warn: Vec<Error>,
95}
96
97/// Error scope to fail if one of several computations failed.
98///
99/// Usage example:
100/// ```skip
101/// for e in &es {
102///     foo(acc, e)?;
103/// }
104/// ```
105/// will only try to execute `foo` until it reaches the first error.
106/// Subsequent elements will not be handled.
107///
108/// If instead we wish to execute them all we can do
109///
110///
111pub struct EAccumScope<'a> {
112    /// Mutable diagnostics accumulator.
113    acc: &'a mut EAccum,
114    /// Whether any fatal error occured that would warrant aborting the compilation.
115    /// (i.e. should be set iff there exists an `error` in `acc`, and unset if `acc`
116    /// is only `warning`)
117    fatal: bool,
118}
119
120impl EAccum {
121    /// Push a fatal error to the accumulator.
122    /// Always returns a `None` so that you can use the construct
123    /// `acc.error(...)?;`
124    pub fn error<T, E: IntoError>(&mut self, e: E) -> Option<T> {
125        self.err.push(e.into_err());
126        None
127    }
128
129    /// Push a warning to the accumulator.
130    pub fn warning<E: IntoError>(&mut self, e: E) {
131        self.warn.push(e.into_err());
132    }
133
134    /// Determine if there were any errors inserted.
135    #[must_use]
136    pub fn is_fatal(&self) -> bool {
137        !self.err.is_empty()
138    }
139
140    /// Extract the errors and warnings to be emitted.
141    #[must_use]
142    pub fn fetch(self) -> (Vec<Error>, Vec<Error>) {
143        (self.err, self.warn)
144    }
145
146    /// Create a new scope to record the success of intermediate computation.
147    pub fn scope(&mut self) -> EAccumScope {
148        EAccumScope {
149            acc: self,
150            fatal: false,
151        }
152    }
153}
154
155impl<'a> EAccumScope<'a> {
156    /// Record the success of a new computation.
157    pub fn compute<F>(&mut self, f: F)
158    where
159        F: FnOnce(&mut EAccum) -> Option<()>,
160    {
161        let e = f(&mut *self.acc);
162        if e.is_none() {
163            self.fatal = true;
164        }
165    }
166
167    /// Consume the scope and emit an error if and only if one of the
168    /// recorded computations failed.
169    #[must_use]
170    pub fn close(self) -> Option<()> {
171        if self.fatal {
172            None
173        } else {
174            Some(())
175        }
176    }
177
178    /// Apply an error to the inner accumulator.
179    pub fn error<E: IntoError>(&mut self, e: E) {
180        self.compute(|acc| acc.error(e));
181    }
182
183    /// Apply a warning to the inner accumulator.
184    pub fn warning<E: IntoError>(&mut self, e: E) {
185        self.compute(|acc| {
186            acc.warning(e);
187            Some(())
188        });
189    }
190}