arithmetic_eval/
error.rs

1//! Evaluation errors.
2
3use core::fmt;
4
5pub use arithmetic_parser::UnsupportedType;
6use arithmetic_parser::{BinaryOp, LocatedSpan, Location, LvalueLen, Op, UnaryOp};
7
8use crate::{
9    alloc::{format, vec, Arc, Box, HashSet, String, ToOwned, ToString, Vec},
10    exec::ModuleId,
11    fns::FromValueError,
12    Value,
13};
14
15/// Arithmetic errors raised by [`Arithmetic`] operations on primitive values.
16///
17/// [`Arithmetic`]: crate::arith::Arithmetic
18#[derive(Debug)]
19#[non_exhaustive]
20pub enum ArithmeticError {
21    /// Integer overflow or underflow.
22    IntegerOverflow,
23    /// Division by zero.
24    DivisionByZero,
25    /// Exponent of [`Arithmetic::pow()`] cannot be converted to `usize`, for example because
26    /// it is too large or negative.
27    ///
28    /// [`Arithmetic::pow()`]: crate::arith::Arithmetic::pow()
29    InvalidExponent,
30    /// Integer used as a denominator in [`Arithmetic::div()`] has no multiplicative inverse.
31    ///
32    /// [`Arithmetic::div()`]: crate::arith::Arithmetic::div()
33    NoInverse,
34    /// Invalid operation with a custom error message.
35    ///
36    /// This error may be used by [`Arithmetic`](crate::arith::Arithmetic) implementations
37    /// as a catch-all fallback.
38    InvalidOp(anyhow::Error),
39}
40
41impl ArithmeticError {
42    /// Creates a new invalid operation error with the specified `message`.
43    pub fn invalid_op(message: impl Into<String>) -> Self {
44        Self::InvalidOp(anyhow::Error::msg(message.into()))
45    }
46}
47
48impl fmt::Display for ArithmeticError {
49    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::IntegerOverflow => formatter.write_str("integer overflow or underflow"),
52            Self::DivisionByZero => formatter.write_str("integer division by zero"),
53            Self::InvalidExponent => formatter.write_str("exponent is too large or negative"),
54            Self::NoInverse => formatter.write_str("integer has no multiplicative inverse"),
55            Self::InvalidOp(err) => write!(formatter, "invalid operation: {err}"),
56        }
57    }
58}
59
60#[cfg(feature = "std")]
61impl std::error::Error for ArithmeticError {
62    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
63        match self {
64            Self::InvalidOp(e) => Some(e.as_ref()),
65            _ => None,
66        }
67    }
68}
69
70/// Context for [`ErrorKind::TupleLenMismatch`].
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum TupleLenMismatchContext {
73    /// An error has occurred when evaluating a binary operation.
74    BinaryOp(BinaryOp),
75    /// An error has occurred during assignment.
76    Assignment,
77}
78
79impl fmt::Display for TupleLenMismatchContext {
80    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            Self::BinaryOp(op) => write!(formatter, "{op}"),
83            Self::Assignment => formatter.write_str("assignment"),
84        }
85    }
86}
87
88/// Context for [`ErrorKind::RepeatedAssignment`].
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum RepeatedAssignmentContext {
91    /// A duplicated variable is in function args.
92    FnArgs,
93    /// A duplicated variable is in an lvalue of an assignment.
94    Assignment,
95}
96
97impl fmt::Display for RepeatedAssignmentContext {
98    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
99        formatter.write_str(match self {
100            Self::FnArgs => "function args",
101            Self::Assignment => "assignment",
102        })
103    }
104}
105
106/// Kinds of errors that can occur when compiling or interpreting expressions and statements.
107#[derive(Debug)]
108#[non_exhaustive]
109pub enum ErrorKind {
110    /// Mismatch between length of tuples in a binary operation or assignment.
111    TupleLenMismatch {
112        /// Length of a tuple on the left-hand side.
113        lhs: LvalueLen,
114        /// Length of a tuple on the right-hand side.
115        rhs: usize,
116        /// Context in which the error has occurred.
117        context: TupleLenMismatchContext,
118    },
119
120    /// Field set differs between LHS and RHS, which are both objects.
121    FieldsMismatch {
122        /// Fields in LHS.
123        lhs_fields: HashSet<String>,
124        /// Fields in RHS.
125        rhs_fields: HashSet<String>,
126        /// Operation being performed.
127        op: BinaryOp,
128    },
129
130    /// Mismatch between the number of arguments in the function definition and its call.
131    ArgsLenMismatch {
132        /// Number of args at the function definition.
133        def: LvalueLen,
134        /// Number of args at the function call.
135        call: usize,
136    },
137
138    /// Cannot destructure a non-tuple variable.
139    CannotDestructure,
140
141    /// Repeated assignment to the same variable in function args or tuple destructuring.
142    RepeatedAssignment {
143        /// Context in which the error has occurred.
144        context: RepeatedAssignmentContext,
145    },
146
147    /// Repeated field in object initialization (e.g., `#{ x: 1, x: 2 }`) or destructure
148    /// (e.g., `{ x, x }`).
149    RepeatedField,
150
151    /// Variable with the enclosed name is not defined.
152    Undefined(String),
153    /// Variable is not initialized.
154    Uninitialized(String),
155
156    /// Field name is invalid.
157    InvalidFieldName(String),
158
159    /// Value is not callable (i.e., it is not a function).
160    CannotCall,
161    /// Value cannot be indexed (i.e., it is not a tuple).
162    CannotIndex,
163    /// A field cannot be accessed for the value (i.e., it is not an object).
164    CannotAccessFields,
165
166    /// Index is out of bounds for the indexed tuple.
167    IndexOutOfBounds {
168        /// Index.
169        index: usize,
170        /// Actual tuple length.
171        len: usize,
172    },
173    /// Object does not have a required field.
174    NoField {
175        /// Missing field.
176        field: String,
177        /// Available fields in the object in no particular order.
178        available_fields: Vec<String>,
179    },
180
181    /// Generic error during execution of a native function.
182    NativeCall(String),
183
184    /// Error while converting arguments for [`FnWrapper`](crate::fns::FnWrapper).
185    Wrapper(FromValueError),
186
187    /// Unexpected operand type for the specified operation.
188    UnexpectedOperand {
189        /// Operation which failed.
190        op: Op,
191    },
192
193    /// Value cannot be compared to other values. Only primitive values can be compared; other value types
194    /// cannot.
195    CannotCompare,
196
197    /// Construct not supported by the interpreter.
198    Unsupported(UnsupportedType),
199
200    /// [`Arithmetic`](crate::arith::Arithmetic) error, such as division by zero.
201    Arithmetic(ArithmeticError),
202}
203
204impl fmt::Display for ErrorKind {
205    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
206        match self {
207            Self::TupleLenMismatch { context, lhs, rhs } => {
208                write!(
209                    formatter,
210                    "Mismatch between length of tuples in {context}: LHS has {lhs} element(s), whereas RHS has {rhs}"
211                )
212            }
213            Self::FieldsMismatch {
214                op,
215                lhs_fields,
216                rhs_fields,
217            } => {
218                write!(
219                    formatter,
220                    "Cannot perform {op} on objects: LHS has fields {lhs_fields:?}, whereas RHS has fields {rhs_fields:?}"
221                )
222            }
223            Self::ArgsLenMismatch { def, call } => {
224                write!(
225                    formatter,
226                    "Mismatch between the number of arguments in the function definition and its call: \
227                     definition requires {def} arg(s), call has {call}"
228                )
229            }
230            Self::CannotDestructure => {
231                formatter.write_str("Cannot destructure a non-tuple variable")
232            }
233            Self::RepeatedAssignment { context } => write!(
234                formatter,
235                "Repeated assignment to the same variable in {context}"
236            ),
237            Self::RepeatedField => formatter.write_str("Repeated object field"),
238            Self::Undefined(var) => write!(formatter, "Variable `{var}` is not defined"),
239            Self::Uninitialized(var) => write!(formatter, "Variable `{var}` is not initialized"),
240            Self::InvalidFieldName(name) => write!(formatter, "`{name}` is not a valid field name"),
241            Self::CannotCall => formatter.write_str("Value is not callable"),
242            Self::CannotIndex => formatter.write_str("Value cannot be indexed"),
243            Self::CannotAccessFields => {
244                formatter.write_str("Fields cannot be accessed for the object")
245            }
246            Self::IndexOutOfBounds { index, len } => write!(
247                formatter,
248                "Attempting to get element {index} from tuple with length {len}"
249            ),
250            Self::NoField { field, .. } => write!(formatter, "Object does not have field {field}"),
251            Self::NativeCall(err) => write!(formatter, "Failed executing native function: {err}"),
252            Self::Wrapper(err) => write!(
253                formatter,
254                "Failed converting arguments for native function wrapper: {err}"
255            ),
256            Self::UnexpectedOperand { op } => write!(formatter, "Unexpected operand type for {op}"),
257            Self::CannotCompare => formatter.write_str("Value cannot be compared to other values"),
258            Self::Unsupported(ty) => write!(formatter, "Unsupported {ty}"),
259            Self::Arithmetic(err) => write!(formatter, "Arithmetic error: {err}"),
260        }
261    }
262}
263
264impl ErrorKind {
265    /// Creates a native error.
266    pub fn native(message: impl Into<String>) -> Self {
267        Self::NativeCall(message.into())
268    }
269
270    /// Creates an error for an lvalue type not supported by the interpreter.
271    pub fn unsupported<T: Into<UnsupportedType>>(ty: T) -> Self {
272        Self::Unsupported(ty.into())
273    }
274
275    /// Returned shortened error cause.
276    pub fn to_short_string(&self) -> String {
277        match self {
278            Self::TupleLenMismatch { context, .. } => {
279                format!("Mismatch between length of tuples in {context}")
280            }
281            Self::FieldsMismatch { op, .. } => {
282                format!("Mismatch between object shapes during {op}")
283            }
284            Self::ArgsLenMismatch { .. } => {
285                "Mismatch between the number of arguments in the function definition and its call"
286                    .to_owned()
287            }
288            Self::CannotDestructure => "Cannot destructure a non-tuple variable".to_owned(),
289            Self::RepeatedAssignment { context } => {
290                format!("Repeated assignment to the same variable in {context}")
291            }
292            Self::RepeatedField => "Repeated object field".to_owned(),
293            Self::Undefined(name) => format!("Variable `{name}` is not defined"),
294            Self::Uninitialized(name) => format!("Variable `{name}` is not initialized"),
295            Self::InvalidFieldName(name) => format!("`{name}` is not a valid field name"),
296            Self::CannotCall => "Value is not callable".to_owned(),
297            Self::CannotIndex => "Value cannot be indexed".to_owned(),
298            Self::CannotAccessFields => "Value has no fields".to_owned(),
299            Self::IndexOutOfBounds { len, .. } => {
300                format!("Index out of bounds for tuple with length {len}")
301            }
302            Self::NoField { field, .. } => format!("Object does not have field {field}"),
303            Self::NativeCall(message) => message.clone(),
304            Self::Wrapper(err) => err.to_string(),
305            Self::UnexpectedOperand { op } => format!("Unexpected operand type for {op}"),
306            Self::CannotCompare => "Value is not comparable".to_owned(),
307            Self::Unsupported(_) => "Grammar construct not supported".to_owned(),
308            Self::Arithmetic(_) => "Arithmetic error".to_owned(),
309        }
310    }
311
312    /// Returns a short description of the spanned information.
313    pub fn main_span_info(&self) -> String {
314        match self {
315            Self::TupleLenMismatch { context, lhs, .. } => {
316                format!("LHS of {context} with {lhs} element(s)")
317            }
318            Self::FieldsMismatch { lhs_fields, .. } => {
319                format!("LHS with fields {lhs_fields:?}")
320            }
321            Self::ArgsLenMismatch { call, .. } => format!("Called with {call} arg(s) here"),
322            Self::CannotDestructure => "Failed destructuring".to_owned(),
323            Self::RepeatedAssignment { .. } => "Re-assigned variable".to_owned(),
324            Self::RepeatedField => "Repeated object field".to_owned(),
325            Self::Undefined(_) => "Undefined variable occurrence".to_owned(),
326            Self::Uninitialized(_) => "Uninitialized value".to_owned(),
327            Self::InvalidFieldName(_) => "Invalid field".to_owned(),
328            Self::CannotIndex | Self::IndexOutOfBounds { .. } => "Indexing operation".to_owned(),
329            Self::CannotAccessFields | Self::NoField { .. } => "Field access".to_owned(),
330            Self::CannotCall | Self::NativeCall(_) | Self::Wrapper(_) => "Failed call".to_owned(),
331            Self::UnexpectedOperand { .. } => "Operand of wrong type".to_owned(),
332            Self::CannotCompare => "Cannot be compared".to_owned(),
333            Self::Unsupported(ty) => format!("Unsupported {ty}"),
334            Self::Arithmetic(e) => e.to_string(),
335        }
336    }
337
338    /// Returns information helping fix the error.
339    pub fn help(&self) -> Option<String> {
340        Some(match self {
341            Self::TupleLenMismatch { context, .. } => format!(
342                "If both args of {context} are tuples, the number of elements in them must agree"
343            ),
344            Self::FieldsMismatch { op, .. } => {
345                format!("If both args of {op} are objects, their field names must be the same")
346            }
347            Self::CannotDestructure => "Only tuples can be destructured".to_owned(),
348            Self::RepeatedAssignment { context } => {
349                format!("In {context}, all assigned variables must have different names")
350            }
351            Self::RepeatedField => "Field names in objects must be unique".to_owned(),
352            Self::InvalidFieldName(_) => "Field names must be `usize`s or identifiers".to_owned(),
353            Self::CannotCall => "Only functions are callable, i.e., can be used as `fn_name` \
354                in `fn_name(...)` expressions"
355                .to_owned(),
356            Self::CannotIndex => "Only tuples can be indexed".to_owned(),
357            Self::CannotAccessFields => "Only objects have fields".to_owned(),
358
359            Self::UnexpectedOperand { op: Op::Binary(op) } if op.is_arithmetic() => {
360                "Operands of binary arithmetic ops must be primitive values or tuples / objects \
361                 consisting of primitive values"
362                    .to_owned()
363            }
364            Self::UnexpectedOperand { op: Op::Binary(op) } if op.is_comparison() => {
365                "Operands of comparison ops must be primitive values".to_owned()
366            }
367            Self::UnexpectedOperand {
368                op: Op::Binary(BinaryOp::And | BinaryOp::Or),
369            } => "Operands of binary boolean ops must be boolean".to_owned(),
370            Self::UnexpectedOperand {
371                op: Op::Unary(UnaryOp::Neg),
372            } => "Operand of negation must be primitive values or tuples / objects \
373                  consisting of primitive values"
374                .to_owned(),
375            Self::UnexpectedOperand {
376                op: Op::Unary(UnaryOp::Not),
377            } => "Operand of boolean negation must be boolean".to_owned(),
378
379            Self::CannotCompare => {
380                "Only primitive values can be compared; complex values cannot".to_owned()
381            }
382
383            _ => return None,
384        })
385    }
386}
387
388#[cfg(feature = "std")]
389impl std::error::Error for ErrorKind {
390    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
391        match self {
392            Self::Wrapper(error) => Some(error),
393            Self::Arithmetic(error) => Some(error),
394            _ => None,
395        }
396    }
397}
398
399/// Auxiliary information about an evaluation error.
400#[derive(Debug, Clone, PartialEq, Eq)]
401#[non_exhaustive]
402pub enum AuxErrorInfo {
403    /// Function arguments declaration for [`ErrorKind::ArgsLenMismatch`].
404    FnArgs,
405
406    /// Previous variable assignment for [`ErrorKind::RepeatedAssignment`].
407    PrevAssignment,
408
409    /// Rvalue containing an invalid assignment for [`ErrorKind::CannotDestructure`]
410    /// or [`ErrorKind::TupleLenMismatch`].
411    Rvalue,
412
413    /// RHS of a binary operation on differently sized tuples.
414    UnbalancedRhsTuple(usize),
415    /// RHS of a binary operation on differently shaped objects.
416    UnbalancedRhsObject(HashSet<String>),
417
418    /// Invalid argument.
419    InvalidArg,
420
421    /// String representation of an argument value (e.g., for a failed equality assertion).
422    ArgValue(String),
423}
424
425impl AuxErrorInfo {
426    pub(crate) fn arg_value<T: fmt::Display>(value: &Value<T>) -> Self {
427        Self::ArgValue(value.to_string())
428    }
429}
430
431impl fmt::Display for AuxErrorInfo {
432    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
433        match self {
434            Self::FnArgs => formatter.write_str("Function arguments declared here"),
435            Self::PrevAssignment => formatter.write_str("Previous declaration"),
436            Self::Rvalue => formatter.write_str("RHS containing the invalid assignment"),
437            Self::UnbalancedRhsTuple(size) => {
438                write!(formatter, "RHS with the {size}-element tuple")
439            }
440            Self::UnbalancedRhsObject(fields) => {
441                write!(formatter, "RHS object with fields {fields:?}")
442            }
443            Self::InvalidArg => formatter.write_str("Invalid argument"),
444            Self::ArgValue(val) => write!(formatter, "Has value: {val}"),
445        }
446    }
447}
448
449/// Evaluation error together with one or more relevant code spans.
450#[derive(Debug)]
451pub struct Error {
452    kind: Box<ErrorKind>,
453    main_location: LocationInModule,
454    aux_locations: Vec<LocationInModule<AuxErrorInfo>>,
455}
456
457impl Error {
458    pub(crate) fn new<Span, T>(
459        module_id: Arc<dyn ModuleId>,
460        main_span: &LocatedSpan<Span, T>,
461        kind: ErrorKind,
462    ) -> Self
463    where
464        Span: Copy,
465        Location: From<LocatedSpan<Span>>,
466    {
467        Self {
468            kind: Box::new(kind),
469            main_location: LocationInModule::new(module_id, main_span.with_no_extra().into()),
470            aux_locations: vec![],
471        }
472    }
473
474    pub(crate) fn from_parts(main_span: LocationInModule, kind: ErrorKind) -> Self {
475        Self {
476            kind: Box::new(kind),
477            main_location: main_span,
478            aux_locations: vec![],
479        }
480    }
481
482    /// Adds an auxiliary span to this error. The `span` must be in the same module
483    /// as the main span.
484    #[must_use]
485    pub fn with_location<T>(mut self, location: &Location<T>, info: AuxErrorInfo) -> Self {
486        self.aux_locations.push(LocationInModule {
487            module_id: self.main_location.module_id.clone(),
488            location: location.copy_with_extra(info),
489        });
490        self
491    }
492
493    /// Returns the source of the error.
494    pub fn kind(&self) -> &ErrorKind {
495        &self.kind
496    }
497
498    /// Returns the main span for this error.
499    pub fn location(&self) -> &LocationInModule {
500        &self.main_location
501    }
502
503    /// Returns auxiliary spans for this error.
504    pub fn aux_spans(&self) -> &[LocationInModule<AuxErrorInfo>] {
505        &self.aux_locations
506    }
507}
508
509impl fmt::Display for Error {
510    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
511        self.main_location.fmt_location(formatter)?;
512        write!(formatter, ": {}", self.kind)
513    }
514}
515
516#[cfg(feature = "std")]
517impl std::error::Error for Error {
518    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
519        Some(&self.kind)
520    }
521}
522
523/// Result of an expression evaluation.
524pub type EvalResult<T> = Result<Value<T>, Error>;
525
526/// Code fragment together with information about the module containing the fragment.
527#[derive(Debug, Clone)]
528pub struct LocationInModule<T = ()> {
529    module_id: Arc<dyn ModuleId>,
530    location: Location<T>,
531}
532
533impl LocationInModule {
534    pub(crate) fn new(module_id: Arc<dyn ModuleId>, location: Location) -> Self {
535        Self {
536            module_id,
537            location,
538        }
539    }
540}
541
542impl<T> LocationInModule<T> {
543    /// Returns the ID of the module containing this fragment.
544    pub fn module_id(&self) -> &dyn ModuleId {
545        self.module_id.as_ref()
546    }
547
548    /// Returns the code fragment within the module. The fragment may be stripped
549    /// (i.e., contain only location info, not the code string itself).
550    pub fn in_module(&self) -> &Location<T> {
551        &self.location
552    }
553
554    pub(crate) fn fmt_location(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
555        write!(
556            formatter,
557            "{}:{}:{}",
558            self.module_id,
559            self.location.location_line(),
560            self.location.get_column()
561        )
562    }
563}
564
565/// Element of a backtrace, i.e., a function / method call.
566#[derive(Debug, Clone)]
567#[non_exhaustive]
568pub struct BacktraceElement {
569    /// Function name.
570    pub fn_name: String,
571    /// Code span of the function definition. `None` for native functions.
572    pub def_location: Option<LocationInModule>,
573    /// Code span of the function call.
574    pub call_location: LocationInModule,
575}
576
577/// Error backtrace.
578#[derive(Debug, Default)]
579pub(crate) struct Backtrace {
580    calls: Vec<BacktraceElement>,
581}
582
583impl Backtrace {
584    /// Appends a function call into the backtrace.
585    pub fn push_call(
586        &mut self,
587        fn_name: &str,
588        def_location: Option<LocationInModule>,
589        call_location: LocationInModule,
590    ) {
591        self.calls.push(BacktraceElement {
592            fn_name: fn_name.to_owned(),
593            def_location,
594            call_location,
595        });
596    }
597
598    /// Pops a function call.
599    pub fn pop_call(&mut self) {
600        self.calls.pop();
601    }
602}
603
604/// Error with the associated backtrace.
605#[derive(Debug)]
606pub struct ErrorWithBacktrace {
607    inner: Error,
608    backtrace: Backtrace,
609}
610
611impl ErrorWithBacktrace {
612    pub(crate) fn new(inner: Error, backtrace: Backtrace) -> Self {
613        Self { inner, backtrace }
614    }
615
616    /// Returns the source of the error.
617    pub fn source(&self) -> &Error {
618        &self.inner
619    }
620
621    /// Iterates over the error backtrace, starting from the most recent call.
622    pub fn backtrace(&self) -> impl Iterator<Item = &BacktraceElement> + '_ {
623        self.backtrace.calls.iter().rev()
624    }
625}
626
627impl fmt::Display for ErrorWithBacktrace {
628    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
629        fmt::Display::fmt(&self.inner, formatter)?;
630
631        if formatter.alternate() && !self.backtrace.calls.is_empty() {
632            writeln!(formatter, "\nBacktrace (most recent call last):")?;
633            for (index, call) in self.backtrace.calls.iter().enumerate() {
634                write!(formatter, "{:>4}: {} ", index + 1, call.fn_name)?;
635
636                if let Some(ref def_span) = call.def_location {
637                    write!(formatter, "(module `{}`)", def_span.module_id)?;
638                } else {
639                    formatter.write_str("(native)")?;
640                }
641
642                write!(formatter, " called at ")?;
643                call.call_location.fmt_location(formatter)?;
644                writeln!(formatter)?;
645            }
646        }
647        Ok(())
648    }
649}
650
651#[cfg(feature = "std")]
652impl std::error::Error for ErrorWithBacktrace {
653    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
654        std::error::Error::source(&self.inner)
655    }
656}
657
658#[cfg(test)]
659mod tests {
660    use super::*;
661    use crate::alloc::ToString;
662
663    #[test]
664    fn display_for_eval_error() {
665        let err = ErrorKind::Undefined("test".to_owned());
666        assert_eq!(err.to_string(), "Variable `test` is not defined");
667
668        let err = ErrorKind::ArgsLenMismatch {
669            def: LvalueLen::AtLeast(2),
670            call: 1,
671        };
672        assert!(err
673            .to_string()
674            .ends_with("definition requires at least 2 arg(s), call has 1"));
675    }
676
677    #[test]
678    fn display_for_spanned_eval_error() {
679        let input = "(_, test) = (1, 2);";
680        let main_span = Location::from_str(input, 4..8);
681        let err = Error::new(
682            Arc::new("test_module"),
683            &main_span,
684            ErrorKind::Undefined("test".to_owned()),
685        );
686        let err_string = err.to_string();
687        assert_eq!(
688            err_string,
689            "test_module:1:5: Variable `test` is not defined"
690        );
691    }
692
693    #[test]
694    fn display_for_error_with_backtrace() {
695        let input = "(_, test) = (1, 2);";
696        let main_span = Location::from_str(input, 4..8);
697        let err = Error::new(
698            Arc::new("test"),
699            &main_span,
700            ErrorKind::Undefined("test".to_owned()),
701        );
702
703        let mut err = ErrorWithBacktrace::new(err, Backtrace::default());
704        let call_span = LocationInModule::new(Arc::new("test"), Location::from_str(input, ..));
705        err.backtrace.push_call("test_fn", None, call_span);
706
707        let err_string = err.to_string();
708        assert_eq!(err_string, "test:1:5: Variable `test` is not defined");
709
710        let expanded_err_string = format!("{err:#}");
711        assert!(expanded_err_string.starts_with("test:1:5: Variable `test` is not defined"));
712        assert!(expanded_err_string.contains("\nBacktrace"));
713        assert!(expanded_err_string.contains("\n   1: test_fn (native) called at test:1:1"));
714    }
715}