1 | use std::{fmt, ops::Range}; |
2 | |
3 | |
4 | /// An error signaling that a different kind of token was expected. Returned by |
5 | /// the various `TryFrom` impls. |
6 | #[derive (Debug, Clone, Copy)] |
7 | pub struct InvalidToken { |
8 | pub(crate) expected: TokenKind, |
9 | pub(crate) actual: TokenKind, |
10 | pub(crate) span: Span, |
11 | } |
12 | |
13 | impl InvalidToken { |
14 | /// Returns a token stream representing `compile_error!("msg");` where |
15 | /// `"msg"` is the output of `self.to_string()`. **Panics if called outside |
16 | /// of a proc-macro context!** |
17 | pub fn to_compile_error(&self) -> proc_macro::TokenStream { |
18 | use proc_macro::{Delimiter, Ident, Group, Punct, Spacing, TokenTree}; |
19 | |
20 | let span = match self.span { |
21 | Span::One(s) => s, |
22 | #[cfg (feature = "proc-macro2" )] |
23 | Span::Two(s) => s.unwrap(), |
24 | }; |
25 | let msg = self.to_string(); |
26 | let tokens = vec![ |
27 | TokenTree::from(Ident::new("compile_error" , span)), |
28 | TokenTree::from(Punct::new('!' , Spacing::Alone)), |
29 | TokenTree::from(Group::new( |
30 | Delimiter::Parenthesis, |
31 | TokenTree::from(proc_macro::Literal::string(&msg)).into(), |
32 | )), |
33 | ]; |
34 | |
35 | |
36 | tokens.into_iter().map(|mut t| { t.set_span(span); t }).collect() |
37 | } |
38 | |
39 | /// Like [`to_compile_error`][Self::to_compile_error], but returns a token |
40 | /// stream from `proc_macro2` and does not panic outside of a proc-macro |
41 | /// context. |
42 | #[cfg (feature = "proc-macro2" )] |
43 | pub fn to_compile_error2(&self) -> proc_macro2::TokenStream { |
44 | use proc_macro2::{Delimiter, Ident, Group, Punct, Spacing, TokenTree}; |
45 | |
46 | let span = match self.span { |
47 | Span::One(s) => proc_macro2::Span::from(s), |
48 | Span::Two(s) => s, |
49 | }; |
50 | let msg = self.to_string(); |
51 | let tokens = vec![ |
52 | TokenTree::from(Ident::new("compile_error" , span)), |
53 | TokenTree::from(Punct::new('!' , Spacing::Alone)), |
54 | TokenTree::from(Group::new( |
55 | Delimiter::Parenthesis, |
56 | TokenTree::from(proc_macro2::Literal::string(&msg)).into(), |
57 | )), |
58 | ]; |
59 | |
60 | |
61 | tokens.into_iter().map(|mut t| { t.set_span(span); t }).collect() |
62 | } |
63 | } |
64 | |
65 | impl std::error::Error for InvalidToken {} |
66 | |
67 | impl fmt::Display for InvalidToken { |
68 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
69 | fn kind_desc(kind: TokenKind) -> &'static str { |
70 | match kind { |
71 | TokenKind::Punct => "a punctuation character" , |
72 | TokenKind::Ident => "an identifier" , |
73 | TokenKind::Group => "a group" , |
74 | TokenKind::Literal => "a literal" , |
75 | TokenKind::BoolLit => "a bool literal (`true` or `false`)" , |
76 | TokenKind::ByteLit => "a byte literal (e.g. `b'r')" , |
77 | TokenKind::ByteStringLit => r#"a byte string literal (e.g. `b"fox"`)"# , |
78 | TokenKind::CharLit => "a character literal (e.g. `'P'`)" , |
79 | TokenKind::FloatLit => "a float literal (e.g. `3.14`)" , |
80 | TokenKind::IntegerLit => "an integer literal (e.g. `27`)" , |
81 | TokenKind::StringLit => r#"a string literal (e.g. "Ferris")"# , |
82 | } |
83 | } |
84 | |
85 | write!(f, "expected {}, but found {}" , kind_desc(self.expected), kind_desc(self.actual)) |
86 | } |
87 | } |
88 | |
89 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
90 | pub(crate) enum TokenKind { |
91 | Punct, |
92 | Ident, |
93 | Group, |
94 | Literal, |
95 | BoolLit, |
96 | ByteLit, |
97 | ByteStringLit, |
98 | CharLit, |
99 | FloatLit, |
100 | IntegerLit, |
101 | StringLit, |
102 | } |
103 | |
104 | /// Unfortunately, we have to deal with both cases. |
105 | #[derive (Debug, Clone, Copy)] |
106 | pub(crate) enum Span { |
107 | One(proc_macro::Span), |
108 | #[cfg (feature = "proc-macro2" )] |
109 | Two(proc_macro2::Span), |
110 | } |
111 | |
112 | impl From<proc_macro::Span> for Span { |
113 | fn from(src: proc_macro::Span) -> Self { |
114 | Self::One(src) |
115 | } |
116 | } |
117 | |
118 | #[cfg (feature = "proc-macro2" )] |
119 | impl From<proc_macro2::Span> for Span { |
120 | fn from(src: proc_macro2::Span) -> Self { |
121 | Self::Two(src) |
122 | } |
123 | } |
124 | |
125 | /// Errors during parsing. |
126 | /// |
127 | /// This type should be seen primarily for error reporting and not for catching |
128 | /// specific cases. The span and error kind are not guaranteed to be stable |
129 | /// over different versions of this library, meaning that a returned error can |
130 | /// change from one version to the next. There are simply too many fringe cases |
131 | /// that are not easy to classify as a specific error kind. It depends entirely |
132 | /// on the specific parser code how an invalid input is categorized. |
133 | /// |
134 | /// Consider these examples: |
135 | /// - `'\` can be seen as |
136 | /// - invalid escape in character literal, or |
137 | /// - unterminated character literal. |
138 | /// - `'''` can be seen as |
139 | /// - empty character literal, or |
140 | /// - unescaped quote character in character literal. |
141 | /// - `0b64` can be seen as |
142 | /// - binary integer literal with invalid digit 6, or |
143 | /// - binary integer literal with invalid digit 4, or |
144 | /// - decimal integer literal with invalid digit b, or |
145 | /// - decimal integer literal 0 with unknown type suffix `b64`. |
146 | /// |
147 | /// If you want to see more if these examples, feel free to check out the unit |
148 | /// tests of this library. |
149 | /// |
150 | /// While this library does its best to emit sensible and precise errors, and to |
151 | /// keep the returned errors as stable as possible, full stability cannot be |
152 | /// guaranteed. |
153 | #[derive (Debug, Clone)] |
154 | pub struct ParseError { |
155 | pub(crate) span: Option<Range<usize>>, |
156 | pub(crate) kind: ParseErrorKind, |
157 | } |
158 | |
159 | impl ParseError { |
160 | /// Returns a span of this error, if available. **Note**: the returned span |
161 | /// might change in future versions of this library. See [the documentation |
162 | /// of this type][ParseError] for more information. |
163 | pub fn span(&self) -> Option<Range<usize>> { |
164 | self.span.clone() |
165 | } |
166 | } |
167 | |
168 | /// This is a free standing function instead of an associated one to reduce |
169 | /// noise around parsing code. There are lots of places that create errors, we |
170 | /// I wanna keep them as short as possible. |
171 | pub(crate) fn perr(span: impl SpanLike, kind: ParseErrorKind) -> ParseError { |
172 | ParseError { |
173 | span: span.into_span(), |
174 | kind, |
175 | } |
176 | } |
177 | |
178 | pub(crate) trait SpanLike { |
179 | fn into_span(self) -> Option<Range<usize>>; |
180 | } |
181 | |
182 | impl SpanLike for Option<Range<usize>> { |
183 | #[inline (always)] |
184 | fn into_span(self) -> Option<Range<usize>> { |
185 | self |
186 | } |
187 | } |
188 | impl SpanLike for Range<usize> { |
189 | #[inline (always)] |
190 | fn into_span(self) -> Option<Range<usize>> { |
191 | Some(self) |
192 | } |
193 | } |
194 | impl SpanLike for usize { |
195 | #[inline (always)] |
196 | fn into_span(self) -> Option<Range<usize>> { |
197 | Some(self..self + 1) |
198 | } |
199 | } |
200 | |
201 | |
202 | /// Kinds of errors. |
203 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
204 | #[non_exhaustive ] |
205 | pub(crate) enum ParseErrorKind { |
206 | /// The input was an empty string |
207 | Empty, |
208 | |
209 | /// An unexpected char was encountered. |
210 | UnexpectedChar, |
211 | |
212 | /// Literal was not recognized. |
213 | InvalidLiteral, |
214 | |
215 | /// Input does not start with decimal digit when trying to parse an integer. |
216 | DoesNotStartWithDigit, |
217 | |
218 | /// A digit invalid for the specified integer base was found. |
219 | InvalidDigit, |
220 | |
221 | /// Integer literal does not contain any valid digits. |
222 | NoDigits, |
223 | |
224 | /// Exponent of a float literal does not contain any digits. |
225 | NoExponentDigits, |
226 | |
227 | /// An unknown escape code, e.g. `\b`. |
228 | UnknownEscape, |
229 | |
230 | /// A started escape sequence where the input ended before the escape was |
231 | /// finished. |
232 | UnterminatedEscape, |
233 | |
234 | /// An `\x` escape where the two digits are not valid hex digits. |
235 | InvalidXEscape, |
236 | |
237 | /// A string or character literal using the `\xNN` escape where `NN > 0x7F`. |
238 | NonAsciiXEscape, |
239 | |
240 | /// A `\u{...}` escape in a byte or byte string literal. |
241 | UnicodeEscapeInByteLiteral, |
242 | |
243 | /// A Unicode escape that does not start with a hex digit. |
244 | InvalidStartOfUnicodeEscape, |
245 | |
246 | /// A `\u{...}` escape that lacks the opening brace. |
247 | UnicodeEscapeWithoutBrace, |
248 | |
249 | /// In a `\u{...}` escape, a non-hex digit and non-underscore character was |
250 | /// found. |
251 | NonHexDigitInUnicodeEscape, |
252 | |
253 | /// More than 6 digits found in unicode escape. |
254 | TooManyDigitInUnicodeEscape, |
255 | |
256 | /// The value from a unicode escape does not represent a valid character. |
257 | InvalidUnicodeEscapeChar, |
258 | |
259 | /// A `\u{..` escape that is not terminated (lacks the closing brace). |
260 | UnterminatedUnicodeEscape, |
261 | |
262 | /// A character literal that's not terminated. |
263 | UnterminatedCharLiteral, |
264 | |
265 | /// A character literal that contains more than one character. |
266 | OverlongCharLiteral, |
267 | |
268 | /// An empty character literal, i.e. `''`. |
269 | EmptyCharLiteral, |
270 | |
271 | UnterminatedByteLiteral, |
272 | OverlongByteLiteral, |
273 | EmptyByteLiteral, |
274 | NonAsciiInByteLiteral, |
275 | |
276 | /// A `'` character was not escaped in a character or byte literal, or a `"` |
277 | /// character was not escaped in a string or byte string literal. |
278 | UnescapedSingleQuote, |
279 | |
280 | /// A \n, \t or \r raw character in a char or byte literal. |
281 | UnescapedSpecialWhitespace, |
282 | |
283 | /// When parsing a character, byte, string or byte string literal directly |
284 | /// and the input does not start with the corresponding quote character |
285 | /// (plus optional raw string prefix). |
286 | DoesNotStartWithQuote, |
287 | |
288 | /// Unterminated raw string literal. |
289 | UnterminatedRawString, |
290 | |
291 | /// String literal without a `"` at the end. |
292 | UnterminatedString, |
293 | |
294 | /// Invalid start for a string literal. |
295 | InvalidStringLiteralStart, |
296 | |
297 | /// Invalid start for a byte literal. |
298 | InvalidByteLiteralStart, |
299 | |
300 | InvalidByteStringLiteralStart, |
301 | |
302 | /// An literal `\r` character not followed by a `\n` character in a |
303 | /// (raw) string or byte string literal. |
304 | IsolatedCr, |
305 | |
306 | /// Literal suffix is not a valid identifier. |
307 | InvalidSuffix, |
308 | |
309 | /// Returned by `Float::parse` if an integer literal (no fractional nor |
310 | /// exponent part) is passed. |
311 | UnexpectedIntegerLit, |
312 | |
313 | /// Integer suffixes cannot start with `e` or `E` as this conflicts with the |
314 | /// grammar for float literals. |
315 | IntegerSuffixStartingWithE, |
316 | } |
317 | |
318 | impl std::error::Error for ParseError {} |
319 | |
320 | impl fmt::Display for ParseError { |
321 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
322 | use ParseErrorKind::*; |
323 | |
324 | let description = match self.kind { |
325 | Empty => "input is empty" , |
326 | UnexpectedChar => "unexpected character" , |
327 | InvalidLiteral => "invalid literal" , |
328 | DoesNotStartWithDigit => "number literal does not start with decimal digit" , |
329 | InvalidDigit => "integer literal contains a digit invalid for its base" , |
330 | NoDigits => "integer literal does not contain any digits" , |
331 | NoExponentDigits => "exponent of floating point literal does not contain any digits" , |
332 | UnknownEscape => "unknown escape" , |
333 | UnterminatedEscape => "unterminated escape: input ended too soon" , |
334 | InvalidXEscape => r"invalid `\x` escape: not followed by two hex digits" , |
335 | NonAsciiXEscape => r"`\x` escape in char/string literal exceed ASCII range" , |
336 | UnicodeEscapeInByteLiteral => r"`\u{...}` escape in byte (string) literal not allowed" , |
337 | InvalidStartOfUnicodeEscape => r"invalid start of `\u{...}` escape" , |
338 | UnicodeEscapeWithoutBrace => r"`Unicode \u{...}` escape without opening brace" , |
339 | NonHexDigitInUnicodeEscape => r"non-hex digit found in `\u{...}` escape" , |
340 | TooManyDigitInUnicodeEscape => r"more than six digits in `\u{...}` escape" , |
341 | InvalidUnicodeEscapeChar => r"value specified in `\u{...}` escape is not a valid char" , |
342 | UnterminatedUnicodeEscape => r"unterminated `\u{...}` escape" , |
343 | UnterminatedCharLiteral => "character literal is not terminated" , |
344 | OverlongCharLiteral => "character literal contains more than one character" , |
345 | EmptyCharLiteral => "empty character literal" , |
346 | UnterminatedByteLiteral => "byte literal is not terminated" , |
347 | OverlongByteLiteral => "byte literal contains more than one byte" , |
348 | EmptyByteLiteral => "empty byte literal" , |
349 | NonAsciiInByteLiteral => "non ASCII character in byte (string) literal" , |
350 | UnescapedSingleQuote => "character literal contains unescaped ' character" , |
351 | UnescapedSpecialWhitespace => r"unescaped newline (\n), tab (\t) or cr (\r) character" , |
352 | DoesNotStartWithQuote => "invalid start for char/byte/string literal" , |
353 | UnterminatedRawString => "unterminated raw (byte) string literal" , |
354 | UnterminatedString => "unterminated (byte) string literal" , |
355 | InvalidStringLiteralStart => "invalid start for string literal" , |
356 | InvalidByteLiteralStart => "invalid start for byte literal" , |
357 | InvalidByteStringLiteralStart => "invalid start for byte string literal" , |
358 | IsolatedCr => r"`\r` not immediately followed by `\n` in string" , |
359 | InvalidSuffix => "literal suffix is not a valid identifier" , |
360 | UnexpectedIntegerLit => "expected float literal, but found integer" , |
361 | IntegerSuffixStartingWithE => "integer literal suffix must not start with 'e' or 'E'" , |
362 | }; |
363 | |
364 | description.fmt(f)?; |
365 | if let Some(span) = &self.span { |
366 | write!(f, " (at {}.. {})" , span.start, span.end)?; |
367 | } |
368 | |
369 | Ok(()) |
370 | } |
371 | } |
372 | |