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}