arithmetic_typing/error/
op_errors.rs

1//! `OpErrors` type.
2
3use std::ops;
4
5use crate::{
6    ast::TypeAst,
7    error::{Error, ErrorContext, ErrorKind, ErrorLocation},
8    PrimitiveType,
9};
10use arithmetic_parser::{grammars::Grammar, Destructure, Spanned, SpannedExpr, SpannedLvalue};
11
12/// Error container tied to a particular top-level operation that has a certain span
13/// and [context](ErrorContext).
14///
15/// Supplied as an argument to [`TypeArithmetic`] methods and [`Substitutions::unify()`].
16///
17/// [`TypeArithmetic`]: crate::arith::TypeArithmetic
18/// [`Substitutions::unify()`]: crate::arith::Substitutions::unify()
19#[derive(Debug)]
20pub struct OpErrors<'a, Prim: PrimitiveType> {
21    errors: Goat<'a, Vec<ErrorPrecursor<Prim>>>,
22    current_location: Vec<ErrorLocation>,
23}
24
25impl<Prim: PrimitiveType> OpErrors<'_, Prim> {
26    /// Adds a new `error` into this the error list.
27    pub fn push(&mut self, kind: ErrorKind<Prim>) {
28        self.errors.push(ErrorPrecursor {
29            kind,
30            location: self.current_location.clone(),
31        });
32    }
33
34    /// Invokes the provided closure and returns `false` if new errors were
35    /// added during the closure execution.
36    pub fn check(&mut self, check: impl FnOnce(OpErrors<'_, Prim>)) -> bool {
37        let error_count = self.errors.len();
38        check(self.by_ref());
39        self.errors.len() == error_count
40    }
41
42    /// Mutably borrows this container allowing to use it multiple times.
43    pub fn by_ref(&mut self) -> OpErrors<'_, Prim> {
44        OpErrors {
45            errors: Goat::Borrowed(&mut *self.errors),
46            current_location: self.current_location.clone(),
47        }
48    }
49
50    /// Narrows down the location of the error.
51    pub fn with_location(&mut self, location: impl Into<ErrorLocation>) -> OpErrors<'_, Prim> {
52        let mut current_location = self.current_location.clone();
53        current_location.push(location.into());
54        OpErrors {
55            errors: Goat::Borrowed(&mut *self.errors),
56            current_location,
57        }
58    }
59
60    pub(crate) fn push_location(&mut self, location: impl Into<ErrorLocation>) {
61        self.current_location.push(location.into());
62    }
63
64    pub(crate) fn pop_location(&mut self) {
65        self.current_location.pop().expect("Location is empty");
66    }
67
68    #[cfg(test)]
69    pub(crate) fn into_vec(self) -> Vec<ErrorKind<Prim>> {
70        let errors = match self.errors {
71            Goat::Owned(errors) => errors,
72            Goat::Borrowed(_) => panic!("Attempt to call `into_vec` for borrowed errors"),
73        };
74        errors.into_iter().map(|err| err.kind).collect()
75    }
76}
77
78impl<Prim: PrimitiveType> OpErrors<'static, Prim> {
79    pub(crate) fn new() -> Self {
80        Self {
81            errors: Goat::Owned(vec![]),
82            current_location: vec![],
83        }
84    }
85
86    pub(crate) fn contextualize<'a, T: Grammar<'a>>(
87        self,
88        span: &SpannedExpr<'a, T>,
89        context: impl Into<ErrorContext<Prim>>,
90    ) -> Vec<Error<'a, Prim>> {
91        let context = context.into();
92        self.do_contextualize(|item| item.into_expr_error(context.clone(), span))
93    }
94
95    fn do_contextualize<'a>(
96        self,
97        map_fn: impl Fn(ErrorPrecursor<Prim>) -> Error<'a, Prim>,
98    ) -> Vec<Error<'a, Prim>> {
99        let errors = match self.errors {
100            Goat::Owned(errors) => errors,
101            Goat::Borrowed(_) => unreachable!(),
102        };
103        errors.into_iter().map(map_fn).collect()
104    }
105
106    pub(crate) fn contextualize_assignment<'a>(
107        self,
108        span: &SpannedLvalue<'a, TypeAst<'a>>,
109        context: &ErrorContext<Prim>,
110    ) -> Vec<Error<'a, Prim>> {
111        if self.errors.is_empty() {
112            vec![]
113        } else {
114            self.do_contextualize(|item| item.into_assignment_error(context.clone(), span))
115        }
116    }
117
118    pub(crate) fn contextualize_destructure<'a>(
119        self,
120        span: &Spanned<'a, Destructure<'a, TypeAst<'a>>>,
121        create_context: impl FnOnce() -> ErrorContext<Prim>,
122    ) -> Vec<Error<'a, Prim>> {
123        if self.errors.is_empty() {
124            vec![]
125        } else {
126            let context = create_context();
127            self.do_contextualize(|item| item.into_destructure_error(context.clone(), span))
128        }
129    }
130}
131
132/// Analogue of `Cow` with a mutable ref.
133#[derive(Debug)]
134enum Goat<'a, T> {
135    Owned(T),
136    Borrowed(&'a mut T),
137}
138
139impl<T> ops::Deref for Goat<'_, T> {
140    type Target = T;
141
142    fn deref(&self) -> &Self::Target {
143        match self {
144            Self::Owned(value) => value,
145            Self::Borrowed(mut_ref) => mut_ref,
146        }
147    }
148}
149
150impl<T> ops::DerefMut for Goat<'_, T> {
151    fn deref_mut(&mut self) -> &mut Self::Target {
152        match self {
153            Self::Owned(value) => value,
154            Self::Borrowed(mut_ref) => *mut_ref,
155        }
156    }
157}
158
159#[derive(Debug)]
160struct ErrorPrecursor<Prim: PrimitiveType> {
161    kind: ErrorKind<Prim>,
162    location: Vec<ErrorLocation>,
163}
164
165impl<Prim: PrimitiveType> ErrorPrecursor<Prim> {
166    fn into_expr_error<'a, T: Grammar<'a>>(
167        self,
168        context: ErrorContext<Prim>,
169        root_expr: &SpannedExpr<'a, T>,
170    ) -> Error<'a, Prim> {
171        Error {
172            inner: ErrorLocation::walk_expr(&self.location, root_expr).copy_with_extra(self.kind),
173            root_span: root_expr.with_no_extra(),
174            context,
175            location: self.location,
176        }
177    }
178
179    fn into_assignment_error<'a>(
180        self,
181        context: ErrorContext<Prim>,
182        root_lvalue: &SpannedLvalue<'a, TypeAst<'a>>,
183    ) -> Error<'a, Prim> {
184        Error {
185            inner: ErrorLocation::walk_lvalue(&self.location, root_lvalue)
186                .copy_with_extra(self.kind),
187            root_span: root_lvalue.with_no_extra(),
188            context,
189            location: self.location,
190        }
191    }
192
193    fn into_destructure_error<'a>(
194        self,
195        context: ErrorContext<Prim>,
196        root_destructure: &Spanned<'a, Destructure<'a, TypeAst<'a>>>,
197    ) -> Error<'a, Prim> {
198        Error {
199            inner: ErrorLocation::walk_destructure(&self.location, root_destructure)
200                .copy_with_extra(self.kind),
201            root_span: root_destructure.with_no_extra(),
202            context,
203            location: self.location,
204        }
205    }
206}