Skip to main content

cynic_parser/
errors.rs

1#[cfg(feature = "report")]
2mod report;
3
4use std::fmt;
5
6#[cfg(feature = "report")]
7pub use report::Report;
8
9use crate::{
10    Span, lexer,
11    parser::AdditionalErrors,
12    type_system::{DirectiveLocation, MalformedDirectiveLocation},
13};
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub enum Error {
17    /// Generated by the parser when it encounters a token (or EOF) it did not
18    /// expect.
19    InvalidToken { location: usize },
20
21    /// Generated by the parser when it encounters an EOF it did not expect.
22    UnrecognizedEof {
23        /// The end of the final token
24        location: usize,
25
26        /// The set of expected tokens: these names are taken from the
27        /// grammar and hence may not necessarily be suitable for
28        /// presenting to the user.
29        expected: Vec<String>,
30    },
31
32    /// Generated by the parser when it encounters a token it did not expect.
33    UnrecognizedToken {
34        /// The unexpected token of type `T` with a span given by the two `L` values.
35        token: (usize, String, usize),
36
37        /// The set of expected tokens: these names are taken from the
38        /// grammar and hence may not necessarily be suitable for
39        /// presenting to the user.
40        expected: Vec<String>,
41    },
42
43    /// Generated by the parser when it encounters additional, unexpected tokens.
44    ExtraToken { token: (usize, String, usize) },
45
46    /// Lexing errors
47    Lexical(lexer::LexicalError),
48
49    /// Malformed string literal
50    MalformedStringLiteral(crate::common::MalformedStringError),
51
52    /// Malformed directive location
53    MalformedDirectiveLocation(usize, String, usize),
54
55    /// Variable found in const position
56    VariableInConstPosition(usize, String, usize),
57
58    /// The GraphQl document was empty
59    EmptyTypeSystemDocument,
60
61    /// The GraphQl document was empty
62    EmptyExecutableDocument,
63
64    /// The schema coordinate was empty
65    EmptySchemaCoordinate,
66}
67
68impl Error {
69    pub fn span(&self) -> Option<Span> {
70        match self {
71            Error::InvalidToken { location } => Span::new(*location, *location).into(),
72            Error::UnrecognizedEof { location, .. } => Span::new(*location, *location).into(),
73            Error::UnrecognizedToken {
74                token: (start, _, end),
75                ..
76            } => Span::new(*start, *end).into(),
77            Error::ExtraToken {
78                token: (start, _, end),
79                ..
80            } => Span::new(*start, *end).into(),
81            Error::Lexical(error) => error.span().into(),
82            Error::MalformedStringLiteral(error) => error.span().into(),
83            Error::MalformedDirectiveLocation(lhs, _, rhs) => Span::new(*lhs, *rhs).into(),
84            Error::VariableInConstPosition(lhs, _, rhs) => Span::new(*lhs, *rhs).into(),
85            Error::EmptyExecutableDocument
86            | Error::EmptyTypeSystemDocument
87            | Error::EmptySchemaCoordinate => None,
88        }
89    }
90}
91
92impl std::error::Error for Error {
93    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
94        match self {
95            Error::InvalidToken { .. }
96            | Error::UnrecognizedEof { .. }
97            | Error::UnrecognizedToken { .. }
98            | Error::ExtraToken { .. }
99            | Error::MalformedStringLiteral(..)
100            | Error::MalformedDirectiveLocation(..)
101            | Error::VariableInConstPosition(..)
102            | Error::EmptyTypeSystemDocument
103            | Error::EmptyExecutableDocument => None,
104            Error::Lexical(error) => Some(error),
105            Error::EmptySchemaCoordinate => todo!(),
106        }
107    }
108}
109
110impl fmt::Display for Error {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        match self {
113            Error::InvalidToken { location: _ } => {
114                write!(f, "invalid token")
115            }
116            Error::UnrecognizedEof {
117                location: _,
118                expected,
119            } => {
120                write!(f, "unexpected end of file (expected one of ")?;
121                for (i, item) in expected.iter().enumerate() {
122                    if i != 1 {
123                        write!(f, ", ")?;
124                    }
125                    write!(f, "{item}")?;
126                }
127                write!(f, ")")
128            }
129            Error::UnrecognizedToken {
130                token: (_, token, _),
131                expected,
132            } => {
133                write!(f, "unexpected {token} token (expected one of ")?;
134                for (i, item) in expected.iter().enumerate() {
135                    if i != 1 {
136                        write!(f, ", ")?;
137                    }
138                    write!(f, "{item}")?;
139                }
140                write!(f, ")")
141            }
142            Error::ExtraToken {
143                token: (_, token, _),
144            } => {
145                write!(f, "found a {token} after the expected end of the document")
146            }
147            Error::Lexical(error) => {
148                write!(f, "{error}")
149            }
150            Error::MalformedStringLiteral(error) => {
151                write!(f, "malformed string literal: {error}")
152            }
153            Error::MalformedDirectiveLocation(_, location, _) => {
154                write!(
155                    f,
156                    "unknown directive location: {location}. expected one of "
157                )?;
158
159                for (i, location) in DirectiveLocation::all_locations().iter().enumerate() {
160                    if i != 0 {
161                        write!(f, ", ")?;
162                    }
163                    write!(f, "{location}")?;
164                }
165                Ok(())
166            }
167            Error::VariableInConstPosition(_, name, _) => {
168                write!(
169                    f,
170                    "the variable ${name} was found in a position that does not allow variables"
171                )?;
172                Ok(())
173            }
174            Error::EmptyExecutableDocument => {
175                write!(
176                    f,
177                    "the graphql document was empty, please provide an operation"
178                )
179            }
180            Error::EmptyTypeSystemDocument => {
181                write!(
182                    f,
183                    "the graphql document was empty, please provide at least one definition"
184                )
185            }
186            Error::EmptySchemaCoordinate => {
187                write!(f, "the schema coordinate was empty")
188            }
189        }
190    }
191}
192
193impl From<lalrpop_util::ParseError<usize, lexer::Token<'_>, AdditionalErrors>> for Error {
194    fn from(value: lalrpop_util::ParseError<usize, lexer::Token<'_>, AdditionalErrors>) -> Self {
195        use lalrpop_util::ParseError;
196        match value {
197            ParseError::InvalidToken { location } => Error::InvalidToken { location },
198            ParseError::UnrecognizedEof { location, expected } => {
199                Error::UnrecognizedEof { location, expected }
200            }
201            ParseError::UnrecognizedToken {
202                token: (lspan, token, rspan),
203                expected,
204            } => Error::UnrecognizedToken {
205                token: (lspan, token.to_string(), rspan),
206                expected,
207            },
208            ParseError::ExtraToken {
209                token: (lspan, token, rspan),
210            } => Error::ExtraToken {
211                token: (lspan, token.to_string(), rspan),
212            },
213            ParseError::User {
214                error: AdditionalErrors::Lexical(error),
215            } => Error::Lexical(error),
216            ParseError::User {
217                error: AdditionalErrors::MalformedString(error),
218            } => Error::MalformedStringLiteral(error),
219            ParseError::User {
220                error: AdditionalErrors::MalformedDirectiveLocation(lhs, location, rhs),
221            } => Error::MalformedDirectiveLocation(lhs, location, rhs),
222            ParseError::User {
223                error: AdditionalErrors::VariableInConstPosition(lhs, name, rhs),
224            } => Error::MalformedDirectiveLocation(lhs, name, rhs),
225        }
226    }
227}
228
229impl MalformedDirectiveLocation {
230    pub(crate) fn into_lalrpop_error<'a>(
231        self,
232        (lhs, rhs): (usize, usize),
233    ) -> lalrpop_util::ParseError<usize, lexer::Token<'a>, AdditionalErrors> {
234        lalrpop_util::ParseError::User {
235            error: AdditionalErrors::MalformedDirectiveLocation(lhs, self.0, rhs),
236        }
237    }
238}