Skip to main content

boa_parser/error/
mod.rs

1//! Error and result implementation for the parser.
2
3#[cfg(test)]
4mod tests;
5
6use crate::lexer::Error as LexError;
7use boa_ast::{Position, Span};
8use std::fmt;
9
10/// Result of a parsing operation.
11pub type ParseResult<T> = Result<T, Error>;
12
13/// Adds context to a parser error.
14pub(crate) trait ErrorContext {
15    /// Sets the context of the error, if possible.
16    fn set_context(self, context: &'static str) -> Self;
17
18    /// Gets the context of the error, if any.
19    #[allow(dead_code)]
20    fn context(&self) -> Option<&'static str>;
21}
22
23impl<T> ErrorContext for ParseResult<T> {
24    fn set_context(self, context: &'static str) -> Self {
25        self.map_err(|e| e.set_context(context))
26    }
27
28    fn context(&self) -> Option<&'static str> {
29        self.as_ref().err().and_then(Error::context)
30    }
31}
32
33impl From<LexError> for Error {
34    #[inline]
35    fn from(e: LexError) -> Self {
36        Self::lex(e)
37    }
38}
39
40/// An enum which represents errors encountered during parsing an expression
41#[derive(Debug)]
42pub enum Error {
43    /// When it expected a certain kind of token, but got another as part of something
44    Expected {
45        /// The token(s) that were expected.
46        expected: Box<[String]>,
47
48        /// The token that was not expected.
49        found: Box<str>,
50
51        /// The parsing context in which the error occurred.
52        context: &'static str,
53
54        /// Position of the source code where the error occurred.
55        span: Span,
56    },
57
58    /// When a token is unexpected
59    Unexpected {
60        /// The error message.
61        message: Box<str>,
62
63        /// The token that was not expected.
64        found: Box<str>,
65
66        /// Position of the source code where the error occurred.
67        span: Span,
68    },
69
70    /// When there is an abrupt end to the parsing
71    AbruptEnd,
72
73    /// A lexing error.
74    Lex {
75        /// The error that occurred during lexing.
76        err: LexError,
77    },
78
79    /// Catch all General Error
80    General {
81        /// The error message.
82        message: Box<str>,
83
84        /// Position of the source code where the error occurred.
85        position: Position,
86    },
87}
88
89impl Error {
90    /// Changes the context of the error, if any.
91    fn set_context(self, new_context: &'static str) -> Self {
92        match self {
93            Self::Expected {
94                expected,
95                found,
96                span,
97                ..
98            } => Self::expected(expected, found, span, new_context),
99            e => e,
100        }
101    }
102
103    /// Gets the context of the error, if any.
104    #[allow(unused)] // TODO: context method is unsed, candidate for removal?
105    const fn context(&self) -> Option<&'static str> {
106        if let Self::Expected { context, .. } = self {
107            Some(context)
108        } else {
109            None
110        }
111    }
112
113    /// Creates an `Expected` parsing error.
114    pub(crate) fn expected<E, F>(expected: E, found: F, span: Span, context: &'static str) -> Self
115    where
116        E: Into<Box<[String]>>,
117        F: Into<Box<str>>,
118    {
119        let expected = expected.into();
120        debug_assert_ne!(expected.len(), 0);
121
122        Self::Expected {
123            expected,
124            found: found.into(),
125            span,
126            context,
127        }
128    }
129
130    /// Creates an `Unexpected` parsing error.
131    pub(crate) fn unexpected<F, C>(found: F, span: Span, message: C) -> Self
132    where
133        F: Into<Box<str>>,
134        C: Into<Box<str>>,
135    {
136        Self::Unexpected {
137            found: found.into(),
138            span,
139            message: message.into(),
140        }
141    }
142
143    /// Creates a "general" parsing error.
144    pub(crate) fn general<S, P>(message: S, position: P) -> Self
145    where
146        S: Into<Box<str>>,
147        P: Into<Position>,
148    {
149        Self::General {
150            message: message.into(),
151            position: position.into(),
152        }
153    }
154
155    /// Creates a "general" parsing error with the specific error message for a misplaced function declaration.
156    pub(crate) fn misplaced_function_declaration(position: Position, strict: bool) -> Self {
157        Self::General {
158            message: format!(
159                "{}functions can only be declared at the top level or inside a block.",
160                if strict { "in strict mode code, " } else { "" }
161            )
162            .into(),
163            position,
164        }
165    }
166
167    /// Creates a "general" parsing error with the specific error message for a wrong function declaration with label.
168    pub(crate) fn wrong_labelled_function_declaration(position: Position) -> Self {
169        Self::General {
170            message: "labelled functions can only be declared at the top level or inside a block"
171                .into(),
172            position,
173        }
174    }
175
176    /// Creates a parsing error from a lexing error.
177    pub(crate) const fn lex(e: LexError) -> Self {
178        Self::Lex { err: e }
179    }
180}
181
182impl fmt::Display for Error {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        match self {
185            Self::Expected {
186                expected,
187                found,
188                span,
189                context,
190            } => {
191                write!(f, "expected ")?;
192                match &**expected {
193                    [single] => write!(f, "token '{single}'")?,
194                    expected => {
195                        write!(f, "one of ")?;
196                        for (i, token) in expected.iter().enumerate() {
197                            let prefix = if i == 0 {
198                                ""
199                            } else if i == expected.len() - 1 {
200                                " or "
201                            } else {
202                                ", "
203                            };
204                            write!(f, "{prefix}'{token}'")?;
205                        }
206                    }
207                }
208                write!(
209                    f,
210                    ", got '{found}' in {context} at line {}, col {}",
211                    span.start().line_number(),
212                    span.start().column_number()
213                )
214            }
215            Self::Unexpected {
216                found,
217                span,
218                message,
219            } => write!(
220                f,
221                "unexpected token '{found}', {message} at line {}, col {}",
222                span.start().line_number(),
223                span.start().column_number()
224            ),
225            Self::AbruptEnd => f.write_str("abrupt end"),
226            Self::General { message, position } => write!(
227                f,
228                "{message} at line {}, col {}",
229                position.line_number(),
230                position.column_number()
231            ),
232            Self::Lex { err } => err.fmt(f),
233        }
234    }
235}
236
237impl std::error::Error for Error {}