| use quote::ToTokens; |
| use std::cell::RefCell; |
| use std::fmt::Display; |
| use std::thread; |
| |
| /// A type to collect errors together and format them. |
| /// |
| /// Dropping this object will cause a panic. It must be consumed using `check`. |
| /// |
| /// References can be shared since this type uses run-time exclusive mut checking. |
| #[derive(Default)] |
| pub struct Ctxt { |
| // The contents will be set to `None` during checking. This is so that checking can be |
| // enforced. |
| errors: RefCell<Option<Vec<syn::Error>>>, |
| } |
| |
| impl Ctxt { |
| /// Create a new context object. |
| /// |
| /// This object contains no errors, but will still trigger a panic if it is not `check`ed. |
| pub fn new() -> Self { |
| Ctxt { |
| errors: RefCell::new(Some(Vec::new())), |
| } |
| } |
| |
| /// Add an error to the context object with a tokenenizable object. |
| /// |
| /// The object is used for spanning in error messages. |
| pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) { |
| self.errors |
| .borrow_mut() |
| .as_mut() |
| .unwrap() |
| // Curb monomorphization from generating too many identical methods. |
| .push(syn::Error::new_spanned(obj.into_token_stream(), msg)); |
| } |
| |
| /// Add one of Syn's parse errors. |
| pub fn syn_error(&self, err: syn::Error) { |
| self.errors.borrow_mut().as_mut().unwrap().push(err); |
| } |
| |
| /// Consume this object, producing a formatted error string if there are errors. |
| pub fn check(self) -> syn::Result<()> { |
| let mut errors = self.errors.borrow_mut().take().unwrap().into_iter(); |
| |
| let mut combined = match errors.next() { |
| Some(first) => first, |
| None => return Ok(()), |
| }; |
| |
| for rest in errors { |
| combined.combine(rest); |
| } |
| |
| Err(combined) |
| } |
| } |
| |
| impl Drop for Ctxt { |
| fn drop(&mut self) { |
| if !thread::panicking() && self.errors.borrow().is_some() { |
| panic!("forgot to check for errors"); |
| } |
| } |
| } |