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