proc_macro_util/
ctxt.rs

1// from serde_derive
2
3use quote::ToTokens;
4use std::cell::RefCell;
5use std::fmt::Display;
6use std::thread;
7use syn;
8
9/// A type to collect errors together and format them.
10///
11/// Dropping this object will cause a panic. It must be consumed using `check`.
12///
13/// References can be shared since this type uses run-time exclusive mut checking.
14#[derive(Default)]
15pub struct Ctxt {
16    // The contents will be set to `None` during checking. This is so that checking can be
17    // enforced.
18    errors: RefCell<Option<Vec<syn::Error>>>,
19}
20
21impl Ctxt {
22    /// Create a new context object.
23    ///
24    /// This object contains no errors, but will still trigger a panic if it is not `check`ed.
25    pub fn new() -> Self {
26        Ctxt {
27            errors: RefCell::new(Some(Vec::new())),
28        }
29    }
30
31    /// Add an error to the context object with a tokenenizable object.
32    ///
33    /// The object is used for spanning in error messages.
34    pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) {
35        self.errors
36            .borrow_mut()
37            .as_mut()
38            .unwrap()
39            // Curb monomorphization from generating too many identical methods.
40            .push(syn::Error::new_spanned(obj.into_token_stream(), msg));
41    }
42
43    /// Consume this object, producing a formatted error string if there are errors.
44    pub fn check(self) -> Result<(), Vec<syn::Error>> {
45        let errors = self.errors.borrow_mut().take().unwrap();
46        match errors.len() {
47            0 => Ok(()),
48            _ => Err(errors),
49        }
50    }
51}
52
53impl Drop for Ctxt {
54    fn drop(&mut self) {
55        if !thread::panicking() && self.errors.borrow().is_some() {
56            panic!("forgot to check for errors");
57        }
58    }
59}