Skip to main content

obeli_sk_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    /// Creates an "unexpected" syntax error (found X, with message).
46    #[inline]
47    pub(crate) fn unexpected<F, M, P>(found: F, message: M, pos: P) -> Self
48    where
49        F: fmt::Display,
50        M: Into<Box<str>>,
51        P: Into<Position>,
52    {
53        Self::Syntax(
54            format!("unexpected {found}, {}", message.into().as_ref()).into(),
55            pos.into(),
56        )
57    }
58}
59
60impl fmt::Display for Error {
61    #[inline]
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        match self {
64            Self::IO(e) => e.fmt(f),
65            Self::Syntax(e, pos) => write!(
66                f,
67                "{e} at line {}, col {}",
68                pos.line_number(),
69                pos.column_number()
70            ),
71        }
72    }
73}
74
75impl error::Error for Error {
76    #[inline]
77    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
78        match self {
79            Self::IO(err) => Some(err),
80            Self::Syntax(_, _) => None,
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use std::{error::Error as _, io};
89
90    #[test]
91    fn syntax() {
92        let err = Error::syntax("testing", Position::new(1, 1));
93        if let Error::Syntax(err, pos) = err {
94            assert_eq!(err.as_ref(), "testing");
95            assert_eq!(pos, Position::new(1, 1));
96        } else {
97            unreachable!()
98        }
99
100        let err = Error::syntax("testing", Position::new(1, 1));
101        assert_eq!(err.to_string(), "testing at line 1, col 1");
102        assert!(err.source().is_none());
103    }
104
105    #[test]
106    fn io() {
107        let custom_error = io::Error::other("I/O error");
108        let err = custom_error.into();
109        if let Error::IO(err) = err {
110            assert_eq!(err.to_string(), "I/O error");
111        } else {
112            unreachable!()
113        }
114
115        let custom_error = io::Error::other("I/O error");
116        let err: Error = custom_error.into();
117        assert_eq!(err.to_string(), "I/O error");
118        err.source().map_or_else(
119            || unreachable!(),
120            |io_err| {
121                assert_eq!(io_err.to_string(), "I/O error");
122            },
123        );
124    }
125}