Skip to main content

boa_parser/lexer/
error.rs

1//! This module contains the errors used by the lexer.
2//!
3//! More information:
4//!  - [ECMAScript reference][spec]
5//!
6//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard
7
8use boa_ast::Position;
9use std::{error, fmt, io};
10
11/// An error that occurred during the lexing.
12#[derive(Debug)]
13pub enum Error {
14    /// An IO error is raised to indicate an issue when the lexer is reading data that isn't
15    /// related to the sourcecode itself.
16    IO(io::Error),
17
18    /// Indicates a parsing error due to the presence, or lack of, one or more characters.
19    ///
20    /// More information:
21    /// - [ECMAScript reference][spec]
22    ///
23    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror
24    Syntax(Box<str>, Position),
25}
26
27impl From<io::Error> for Error {
28    #[inline]
29    fn from(err: io::Error) -> Self {
30        Self::IO(err)
31    }
32}
33
34impl Error {
35    /// Creates a new syntax error.
36    #[inline]
37    pub(crate) fn syntax<M, P>(err: M, pos: P) -> Self
38    where
39        M: Into<Box<str>>,
40        P: Into<Position>,
41    {
42        Self::Syntax(err.into(), pos.into())
43    }
44}
45
46impl fmt::Display for Error {
47    #[inline]
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        match self {
50            Self::IO(e) => e.fmt(f),
51            Self::Syntax(e, pos) => write!(
52                f,
53                "{e} at line {}, col {}",
54                pos.line_number(),
55                pos.column_number()
56            ),
57        }
58    }
59}
60
61impl error::Error for Error {
62    #[inline]
63    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
64        match self {
65            Self::IO(err) => Some(err),
66            Self::Syntax(_, _) => None,
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use std::{error::Error as _, io};
75
76    #[test]
77    fn syntax() {
78        let err = Error::syntax("testing", Position::new(1, 1));
79        if let Error::Syntax(err, pos) = err {
80            assert_eq!(err.as_ref(), "testing");
81            assert_eq!(pos, Position::new(1, 1));
82        } else {
83            unreachable!()
84        }
85
86        let err = Error::syntax("testing", Position::new(1, 1));
87        assert_eq!(err.to_string(), "testing at line 1, col 1");
88        assert!(err.source().is_none());
89    }
90
91    #[test]
92    fn io() {
93        let custom_error = io::Error::other("I/O error");
94        let err = custom_error.into();
95        if let Error::IO(err) = err {
96            assert_eq!(err.to_string(), "I/O error");
97        } else {
98            unreachable!()
99        }
100
101        let custom_error = io::Error::other("I/O error");
102        let err: Error = custom_error.into();
103        assert_eq!(err.to_string(), "I/O error");
104        err.source().map_or_else(
105            || unreachable!(),
106            |io_err| {
107                assert_eq!(io_err.to_string(), "I/O error");
108            },
109        );
110    }
111}