use std::cell::Cell;
use alba_syntax::Span;
use crate::model::SourceId;
pub(crate) const ROOT_SOURCE_ID: SourceId = SourceId(0);
thread_local! {
static CURRENT_SOURCE: Cell<SourceId> = const { Cell::new(ROOT_SOURCE_ID) };
}
pub(crate) fn current_source_id() -> SourceId {
CURRENT_SOURCE.with(Cell::get)
}
pub(crate) struct SourceIdScope {
previous: SourceId,
}
impl SourceIdScope {
pub(crate) fn enter(id: SourceId) -> Self {
let previous = CURRENT_SOURCE.with(|cell| cell.replace(id));
Self { previous }
}
}
impl Drop for SourceIdScope {
fn drop(&mut self) {
CURRENT_SOURCE.with(|cell| cell.set(self.previous));
}
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
#[error("{message}")]
pub struct CoreError {
pub message: String,
pub span: Option<Span>,
pub help: Option<String>,
pub source_id: SourceId,
}
impl CoreError {
pub(crate) fn new(message: impl Into<String>, span: Span) -> Self {
Self {
message: message.into(),
span: Some(span),
help: None,
source_id: current_source_id(),
}
}
pub(crate) fn unlocated(message: impl Into<String>) -> Self {
Self {
message: message.into(),
span: None,
help: None,
source_id: current_source_id(),
}
}
pub(crate) fn with_help(mut self, help: impl Into<String>) -> Self {
self.help = Some(help.into());
self
}
pub fn with_source_id(mut self, source_id: SourceId) -> Self {
self.source_id = source_id;
self
}
pub fn into_diagnostic(self) -> alba_syntax::Diagnostic {
alba_syntax::Diagnostic::new(self.message, self.span, self.help)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alba_syntax::render_diagnostic;
#[test]
fn into_diagnostic_preserves_message_span_and_help() {
let err = CoreError::new("boom", Span::new(2, 6)).with_help("try this");
let rendered = render_diagnostic("xx boom xx", "Beamfile", &err.into_diagnostic());
assert!(rendered.contains("boom"));
assert!(rendered.contains("try this"));
}
#[test]
fn an_unlocated_error_renders_without_a_source_position() {
let err = CoreError::unlocated("boom").with_help("try this");
let rendered = render_diagnostic("xx boom xx", "Beamfile", &err.into_diagnostic());
assert!(rendered.contains("boom"));
assert!(rendered.contains("try this"));
assert!(!rendered.contains("Beamfile:"));
}
}