use core::fmt;
use arithmetic_parser::{Location, Spanned, UnsupportedType};
pub use self::{
kind::{ErrorKind, TupleContext},
op_errors::OpErrors,
path::ErrorPathFragment,
};
use crate::{
alloc::{vec, ToOwned, Vec},
arith::{BinaryOpContext, UnaryOpContext},
ast::AstConversionError,
visit::VisitMut,
PrimitiveType, Tuple, Type,
};
mod kind;
mod op_errors;
mod path;
#[derive(Debug, Clone)]
pub struct Error<Prim: PrimitiveType> {
inner: Location<ErrorKind<Prim>>,
root_location: Location,
context: ErrorContext<Prim>,
path: Vec<ErrorPathFragment>,
}
impl<Prim: PrimitiveType> fmt::Display for Error<Prim> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{}:{}: {}",
self.main_location().location_line(),
self.main_location().get_column(),
self.kind()
)
}
}
#[cfg(feature = "std")]
impl<Prim: PrimitiveType> std::error::Error for Error<Prim> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(self.kind())
}
}
impl<Prim: PrimitiveType> Error<Prim> {
pub(crate) fn unsupported<T>(
unsupported: impl Into<UnsupportedType>,
span: &Spanned<'_, T>,
) -> Self {
let kind = ErrorKind::unsupported(unsupported);
Self {
inner: span.copy_with_extra(kind).into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::None,
path: Vec::new(),
}
}
pub(crate) fn undefined_var<T>(span: &Spanned<'_, T>) -> Self {
let ident = (*span.fragment()).to_owned();
Self {
inner: span.copy_with_extra(ErrorKind::UndefinedVar(ident)).into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::None,
path: Vec::new(),
}
}
pub(crate) fn repeated_assignment(span: Spanned<'_>) -> Self {
let ident = (*span.fragment()).to_owned();
Self {
inner: span
.copy_with_extra(ErrorKind::RepeatedAssignment(ident))
.into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::None,
path: Vec::new(),
}
}
pub(crate) fn repeated_field(span: Spanned<'_>) -> Self {
let ident = (*span.fragment()).to_owned();
Self {
inner: span.copy_with_extra(ErrorKind::RepeatedField(ident)).into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::None,
path: Vec::new(),
}
}
pub(crate) fn conversion<T>(kind: AstConversionError, span: &Spanned<'_, T>) -> Self {
let kind = ErrorKind::AstConversion(kind);
Self {
inner: span.copy_with_extra(kind).into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::None,
path: Vec::new(),
}
}
pub(crate) fn invalid_field_name(span: Spanned<'_>) -> Self {
let ident = (*span.fragment()).to_owned();
Self {
inner: span
.copy_with_extra(ErrorKind::InvalidFieldName(ident))
.into(),
root_location: span.into(),
context: ErrorContext::None,
path: Vec::new(),
}
}
pub(crate) fn index_out_of_bounds<T>(
receiver: Tuple<Prim>,
span: &Spanned<'_, T>,
index: usize,
) -> Self {
Self {
inner: span
.copy_with_extra(ErrorKind::IndexOutOfBounds {
index,
len: receiver.len(),
})
.into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::TupleIndex {
ty: Type::Tuple(receiver),
},
path: Vec::new(),
}
}
pub(crate) fn cannot_index<T>(receiver: Type<Prim>, span: &Spanned<'_, T>) -> Self {
Self {
inner: span.copy_with_extra(ErrorKind::CannotIndex).into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::TupleIndex { ty: receiver },
path: Vec::new(),
}
}
pub(crate) fn unsupported_index<T>(receiver: Type<Prim>, span: &Spanned<'_, T>) -> Self {
Self {
inner: span.copy_with_extra(ErrorKind::UnsupportedIndex).into(),
root_location: span.with_no_extra().into(),
context: ErrorContext::TupleIndex { ty: receiver },
path: Vec::new(),
}
}
pub fn kind(&self) -> &ErrorKind<Prim> {
&self.inner.extra
}
pub fn main_location(&self) -> Location {
self.inner.with_no_extra()
}
pub fn root_location(&self) -> Location {
self.root_location
}
pub fn context(&self) -> &ErrorContext<Prim> {
&self.context
}
pub fn path(&self) -> &[ErrorPathFragment] {
&self.path
}
}
#[derive(Debug, Clone)]
pub struct Errors<Prim: PrimitiveType> {
inner: Vec<Error<Prim>>,
first_failing_statement: usize,
}
impl<Prim: PrimitiveType> Errors<Prim> {
pub(crate) fn new() -> Self {
Self {
inner: Vec::new(),
first_failing_statement: 0,
}
}
pub(crate) fn push(&mut self, err: Error<Prim>) {
self.inner.push(err);
}
pub(crate) fn extend(&mut self, errors: Vec<Error<Prim>>) {
self.inner.extend(errors);
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &Error<Prim>> + '_ {
self.inner.iter()
}
pub fn first_failing_statement(&self) -> usize {
self.first_failing_statement
}
pub(crate) fn set_first_failing_statement(&mut self, index: usize) {
self.first_failing_statement = index;
}
pub(crate) fn post_process(&mut self, type_resolver: &mut impl VisitMut<Prim>) {
for error in &mut self.inner {
error.context.map_types(type_resolver);
}
}
}
impl<Prim: PrimitiveType> fmt::Display for Errors<Prim> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, error) in self.inner.iter().enumerate() {
write!(formatter, "{error}")?;
if i + 1 < self.inner.len() {
formatter.write_str("\n")?;
}
}
Ok(())
}
}
#[cfg(feature = "std")]
impl<Prim: PrimitiveType> std::error::Error for Errors<Prim> {}
impl<Prim: PrimitiveType> IntoIterator for Errors<Prim> {
type Item = Error<Prim>;
type IntoIter = vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ErrorContext<Prim: PrimitiveType> {
None,
Lvalue(Type<Prim>),
FnDefinition {
args: Tuple<Prim>,
},
FnCall {
definition: Type<Prim>,
call_signature: Type<Prim>,
},
Assignment {
lhs: Type<Prim>,
rhs: Type<Prim>,
},
TypeCast {
source: Type<Prim>,
target: Type<Prim>,
},
UnaryOp(UnaryOpContext<Prim>),
BinaryOp(BinaryOpContext<Prim>),
TupleIndex {
ty: Type<Prim>,
},
ObjectFieldAccess {
ty: Type<Prim>,
},
}
impl<Prim: PrimitiveType> From<UnaryOpContext<Prim>> for ErrorContext<Prim> {
fn from(value: UnaryOpContext<Prim>) -> Self {
Self::UnaryOp(value)
}
}
impl<Prim: PrimitiveType> From<BinaryOpContext<Prim>> for ErrorContext<Prim> {
fn from(value: BinaryOpContext<Prim>) -> Self {
Self::BinaryOp(value)
}
}
impl<Prim: PrimitiveType> ErrorContext<Prim> {
fn map_types(&mut self, mapper: &mut impl VisitMut<Prim>) {
match self {
Self::None => { }
Self::Lvalue(lvalue) => mapper.visit_type_mut(lvalue),
Self::FnDefinition { args } => mapper.visit_tuple_mut(args),
Self::FnCall {
definition,
call_signature,
} => {
mapper.visit_type_mut(definition);
mapper.visit_type_mut(call_signature);
}
Self::Assignment { lhs, rhs } | Self::BinaryOp(BinaryOpContext { lhs, rhs, .. }) => {
mapper.visit_type_mut(lhs);
mapper.visit_type_mut(rhs);
}
Self::TypeCast { source, target } => {
mapper.visit_type_mut(source);
mapper.visit_type_mut(target);
}
Self::UnaryOp(UnaryOpContext { arg, .. }) => {
mapper.visit_type_mut(arg);
}
Self::TupleIndex { ty } | Self::ObjectFieldAccess { ty } => {
mapper.visit_type_mut(ty);
}
}
}
}