use std::fmt;
use std::mem;
use crate::call_stack::CallStack;
use crate::codemap::CodeMap;
use crate::codemap::FileSpan;
use crate::codemap::Span;
use crate::diagnostic::diagnostic_display;
use crate::diagnostic::WithDiagnostic;
pub struct Error(pub(crate) WithDiagnostic<ErrorKind>);
const _: () = assert!(mem::size_of::<Error>() == mem::size_of::<usize>());
impl Error {
#[cold]
pub fn new_kind(kind: ErrorKind) -> Self {
Self(WithDiagnostic::new_empty(kind))
}
#[cold]
pub fn new_spanned(kind: ErrorKind, span: Span, codemap: &CodeMap) -> Self {
Self(WithDiagnostic::new_spanned(kind, span, codemap))
}
#[cold]
pub fn new_other(e: impl Into<anyhow::Error>) -> Self {
Self(WithDiagnostic::new_empty(ErrorKind::Other(e.into())))
}
#[cold]
pub fn new_native(e: impl Into<anyhow::Error>) -> Self {
Self(WithDiagnostic::new_empty(ErrorKind::Native(e.into())))
}
#[cold]
pub fn new_value(e: impl Into<anyhow::Error>) -> Self {
Self(WithDiagnostic::new_empty(ErrorKind::Value(e.into())))
}
pub fn kind(&self) -> &ErrorKind {
self.0.inner()
}
pub fn into_kind(self) -> ErrorKind {
self.0.into_inner()
}
pub fn has_diagnostic(&self) -> bool {
self.0.span().is_some() || !self.0.call_stack().is_empty()
}
#[cold]
pub fn into_anyhow(self) -> anyhow::Error {
struct Wrapped(Error);
impl fmt::Display for Wrapped {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl fmt::Debug for Wrapped {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl std::error::Error for Wrapped {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.0.kind().source()
}
}
anyhow::Error::new(Wrapped(self))
}
pub fn without_diagnostic<'a>(&'a self) -> impl fmt::Debug + fmt::Display + 'a {
self.0.inner()
}
pub fn span(&self) -> Option<&FileSpan> {
self.0.span()
}
pub fn call_stack(&self) -> &CallStack {
self.0.call_stack()
}
pub fn set_span(&mut self, span: Span, codemap: &CodeMap) {
self.0.set_span(span, codemap);
}
pub fn set_call_stack(&mut self, call_stack: impl FnOnce() -> CallStack) {
self.0.set_call_stack(call_stack);
}
pub fn eprint(&self) {
if self.has_diagnostic() {
let mut stderr = String::new();
diagnostic_display(&self.0, true, &mut stderr, true).unwrap();
eprint!("{}", stderr);
} else {
eprintln!("{:#}", self)
}
}
pub fn into_internal_error(self) -> Error {
if let ErrorKind::Internal(_) = self.kind() {
self
} else {
Error(self.0.map(ErrorKind::into_internal_error))
}
}
}
fn fmt_impl(this: &Error, is_debug: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if this.has_diagnostic() {
let with_context = (f.alternate() || is_debug) && this.kind().source().is_some();
diagnostic_display(&this.0, false, f, with_context)
} else {
fmt::Display::fmt(&this.without_diagnostic(), f)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_impl(self, false, f)
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_impl(self, true, f)
}
}
#[non_exhaustive]
pub enum ErrorKind {
Fail(anyhow::Error),
StackOverflow(anyhow::Error),
Value(anyhow::Error),
Function(anyhow::Error),
Scope(anyhow::Error),
Parser(anyhow::Error),
Freeze(anyhow::Error),
Internal(anyhow::Error),
Native(anyhow::Error),
Other(anyhow::Error),
}
impl ErrorKind {
pub fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Fail(_) => None,
Self::StackOverflow(_) => None,
Self::Value(_) => None,
Self::Function(_) => None,
Self::Scope(_) => None,
Self::Freeze(_) => None,
Self::Parser(_) => None,
Self::Internal(_) => None,
Self::Native(e) => e.source(),
Self::Other(e) => e.source(),
}
}
pub(crate) fn into_internal_error(self) -> ErrorKind {
match self {
ErrorKind::Internal(e)
| ErrorKind::Fail(e)
| ErrorKind::Value(e)
| ErrorKind::Function(e)
| ErrorKind::Scope(e)
| ErrorKind::Freeze(e)
| ErrorKind::Parser(e)
| ErrorKind::StackOverflow(e)
| ErrorKind::Native(e)
| ErrorKind::Other(e) => ErrorKind::Internal(e),
}
}
}
impl fmt::Debug for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fail(s) => write!(f, "fail:{}", s),
Self::Value(e) => fmt::Debug::fmt(e, f),
Self::StackOverflow(e) => fmt::Debug::fmt(e, f),
Self::Function(e) => fmt::Debug::fmt(e, f),
Self::Scope(e) => fmt::Debug::fmt(e, f),
Self::Freeze(e) => fmt::Debug::fmt(e, f),
Self::Parser(e) => fmt::Debug::fmt(e, f),
Self::Internal(e) => write!(f, "Internal error: {}", e),
Self::Native(e) => fmt::Debug::fmt(e, f),
Self::Other(e) => fmt::Debug::fmt(e, f),
}
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fail(s) => write!(f, "fail:{}", s),
Self::StackOverflow(e) => fmt::Display::fmt(e, f),
Self::Value(e) => fmt::Display::fmt(e, f),
Self::Function(e) => fmt::Display::fmt(e, f),
Self::Scope(e) => fmt::Display::fmt(e, f),
Self::Freeze(e) => fmt::Display::fmt(e, f),
Self::Parser(e) => fmt::Display::fmt(e, f),
Self::Internal(e) => write!(f, "Internal error: {}", e),
Self::Native(e) => fmt::Display::fmt(e, f),
Self::Other(e) => fmt::Display::fmt(e, f),
}
}
}
impl From<anyhow::Error> for Error {
#[cold]
fn from(e: anyhow::Error) -> Self {
Self(WithDiagnostic::new_empty(ErrorKind::Other(e)))
}
}
pub trait StarlarkResultExt<T> {
fn into_anyhow_result(self) -> anyhow::Result<T>;
}
impl<T> StarlarkResultExt<T> for crate::Result<T> {
#[inline]
fn into_anyhow_result(self) -> anyhow::Result<T> {
self.map_err(Error::into_anyhow)
}
}
#[doc(hidden)]
#[cold]
pub fn internal_error_impl(args: fmt::Arguments<'_>) -> Error {
Error::new_kind(ErrorKind::Internal(anyhow::anyhow!("{}", args)))
}
#[doc(hidden)]
#[cold]
pub fn other_error_impl(args: fmt::Arguments<'_>) -> Error {
Error::new_kind(ErrorKind::Other(anyhow::anyhow!("{}", args)))
}
#[doc(hidden)]
#[cold]
pub fn value_error_impl(args: fmt::Arguments<'_>) -> Error {
Error::new_kind(ErrorKind::Value(anyhow::anyhow!("{}", args)))
}
#[doc(hidden)]
#[cold]
pub fn function_error_impl(args: fmt::Arguments<'_>) -> Error {
Error::new_kind(ErrorKind::Function(anyhow::anyhow!("{}", args)))
}
#[macro_export]
macro_rules! internal_error {
($format:literal) => {
internal_error!($format,)
};
($format:literal, $($args:tt)*) => {
$crate::error::internal_error_impl(format_args!($format, $($args)*))
};
}
#[macro_export]
macro_rules! other_error {
($format:literal) => {
other_error!($format,)
};
($format:literal, $($args:tt)*) => {
$crate::error::other_error_impl(format_args!($format, $($args)*))
};
}
#[macro_export]
macro_rules! value_error {
($format:literal) => {
value_error!($format,)
};
($format:literal, $($args:tt)*) => {
$crate::error::value_error_impl(format_args!($format, $($args)*))
};
}
#[macro_export]
macro_rules! function_error {
($format:literal) => {
function_error!($format,)
};
($format:literal, $($args:tt)*) => {
$crate::error::function_error_impl(format_args!($format, $($args)*))
};
}