use crate::{error::CompileError, warning::CompileWarning};
use std::collections::HashMap;
use core::cell::RefCell;
#[derive(Default, Debug)]
pub struct Handler {
inner: RefCell<HandlerInner>,
}
#[derive(Default, Debug)]
struct HandlerInner {
errors: Vec<CompileError>,
warnings: Vec<CompileWarning>,
}
impl Handler {
pub fn from_parts(errors: Vec<CompileError>, warnings: Vec<CompileWarning>) -> Self {
Self {
inner: RefCell::new(HandlerInner { errors, warnings }),
}
}
pub fn emit_err(&self, err: CompileError) -> ErrorEmitted {
self.inner.borrow_mut().errors.push(err);
ErrorEmitted { _priv: () }
}
pub fn emit_warn(&self, warn: CompileWarning) {
self.inner.borrow_mut().warnings.push(warn);
}
pub fn has_errors(&self) -> bool {
!self.inner.borrow().errors.is_empty()
}
pub fn has_warnings(&self) -> bool {
!self.inner.borrow().warnings.is_empty()
}
pub fn scope<T>(
&self,
f: impl FnOnce(&Handler) -> Result<T, ErrorEmitted>,
) -> Result<T, ErrorEmitted> {
let scoped_handler = Handler::default();
let closure_res = f(&scoped_handler);
let had_errors = scoped_handler.has_errors();
self.append(scoped_handler);
if had_errors {
Err(ErrorEmitted { _priv: () })
} else {
closure_res
}
}
pub fn consume(self) -> (Vec<CompileError>, Vec<CompileWarning>) {
let inner = self.inner.into_inner();
(inner.errors, inner.warnings)
}
pub fn append(&self, other: Handler) {
let (errors, warnings) = other.consume();
for warn in warnings {
self.emit_warn(warn);
}
for err in errors {
self.emit_err(err);
}
}
pub fn dedup(&self) {
let mut inner = self.inner.borrow_mut();
inner.errors = dedup_unsorted(inner.errors.clone());
inner.warnings = dedup_unsorted(inner.warnings.clone());
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ErrorEmitted {
_priv: (),
}
fn dedup_unsorted<T: PartialEq + std::hash::Hash>(mut data: Vec<T>) -> Vec<T> {
use smallvec::SmallVec;
use std::collections::hash_map::{DefaultHasher, Entry};
use std::hash::Hasher;
let mut write_index = 0;
let mut indexes: HashMap<u64, SmallVec<[usize; 1]>> = HashMap::with_capacity(data.len());
for read_index in 0..data.len() {
let hash = {
let mut hasher = DefaultHasher::new();
data[read_index].hash(&mut hasher);
hasher.finish()
};
let index_vec = match indexes.entry(hash) {
Entry::Occupied(oe) => {
if oe
.get()
.iter()
.any(|index| data[*index] == data[read_index])
{
continue;
}
oe.into_mut()
}
Entry::Vacant(ve) => ve.insert(SmallVec::new()),
};
data.swap(write_index, read_index);
index_vec.push(write_index);
write_index += 1;
}
data.truncate(write_index);
data
}