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!**
to_compile_error(&self) -> proc_macro::TokenStream17 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")]
to_compile_error2(&self) -> proc_macro2::TokenStream43 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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result68 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 {
from(src: proc_macro::Span) -> Self113 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 {
from(src: proc_macro2::Span) -> Self120 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.
span(&self) -> Option<Range<usize>>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.
perr(span: impl SpanLike, kind: ParseErrorKind) -> ParseError171 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 {
into_span(self) -> Option<Range<usize>>179 fn into_span(self) -> Option<Range<usize>>;
180 }
181
182 impl SpanLike for Option<Range<usize>> {
183 #[inline(always)]
into_span(self) -> Option<Range<usize>>184 fn into_span(self) -> Option<Range<usize>> {
185 self
186 }
187 }
188 impl SpanLike for Range<usize> {
189 #[inline(always)]
into_span(self) -> Option<Range<usize>>190 fn into_span(self) -> Option<Range<usize>> {
191 Some(self)
192 }
193 }
194 impl SpanLike for usize {
195 #[inline(always)]
into_span(self) -> Option<Range<usize>>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 /// Found a integer type suffix that is invalid.
225 InvalidIntegerTypeSuffix,
226
227 /// Found a float type suffix that is invalid. Only `f32` and `f64` are
228 /// valid.
229 InvalidFloatTypeSuffix,
230
231 /// Exponent of a float literal does not contain any digits.
232 NoExponentDigits,
233
234 /// An unknown escape code, e.g. `\b`.
235 UnknownEscape,
236
237 /// A started escape sequence where the input ended before the escape was
238 /// finished.
239 UnterminatedEscape,
240
241 /// An `\x` escape where the two digits are not valid hex digits.
242 InvalidXEscape,
243
244 /// A string or character literal using the `\xNN` escape where `NN > 0x7F`.
245 NonAsciiXEscape,
246
247 /// A `\u{...}` escape in a byte or byte string literal.
248 UnicodeEscapeInByteLiteral,
249
250 /// A Unicode escape that does not start with a hex digit.
251 InvalidStartOfUnicodeEscape,
252
253 /// A `\u{...}` escape that lacks the opening brace.
254 UnicodeEscapeWithoutBrace,
255
256 /// In a `\u{...}` escape, a non-hex digit and non-underscore character was
257 /// found.
258 NonHexDigitInUnicodeEscape,
259
260 /// More than 6 digits found in unicode escape.
261 TooManyDigitInUnicodeEscape,
262
263 /// The value from a unicode escape does not represent a valid character.
264 InvalidUnicodeEscapeChar,
265
266 /// A `\u{..` escape that is not terminated (lacks the closing brace).
267 UnterminatedUnicodeEscape,
268
269 /// A character literal that's not terminated.
270 UnterminatedCharLiteral,
271
272 /// A character literal that contains more than one character.
273 OverlongCharLiteral,
274
275 /// An empty character literal, i.e. `''`.
276 EmptyCharLiteral,
277
278 UnterminatedByteLiteral,
279 OverlongByteLiteral,
280 EmptyByteLiteral,
281 NonAsciiInByteLiteral,
282
283 /// A `'` character was not escaped in a character or byte literal, or a `"`
284 /// character was not escaped in a string or byte string literal.
285 UnescapedSingleQuote,
286
287 /// A \n, \t or \r raw character in a char or byte literal.
288 UnescapedSpecialWhitespace,
289
290 /// When parsing a character, byte, string or byte string literal directly
291 /// and the input does not start with the corresponding quote character
292 /// (plus optional raw string prefix).
293 DoesNotStartWithQuote,
294
295 /// Unterminated raw string literal.
296 UnterminatedRawString,
297
298 /// String literal without a `"` at the end.
299 UnterminatedString,
300
301 /// Invalid start for a string literal.
302 InvalidStringLiteralStart,
303
304 /// Invalid start for a byte literal.
305 InvalidByteLiteralStart,
306
307 InvalidByteStringLiteralStart,
308
309 /// An literal `\r` character not followed by a `\n` character in a
310 /// (raw) string or byte string literal.
311 IsolatedCr,
312 }
313
314 impl std::error::Error for ParseError {}
315
316 impl fmt::Display for ParseError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result317 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
318 use ParseErrorKind::*;
319
320 let description = match self.kind {
321 Empty => "input is empty",
322 UnexpectedChar => "unexpected character",
323 InvalidLiteral => "invalid literal",
324 DoesNotStartWithDigit => "number literal does not start with decimal digit",
325 InvalidDigit => "integer literal contains a digit invalid for its base",
326 NoDigits => "integer literal does not contain any digits",
327 InvalidIntegerTypeSuffix => "invalid integer type suffix",
328 InvalidFloatTypeSuffix => "invalid floating point type suffix",
329 NoExponentDigits => "exponent of floating point literal does not contain any digits",
330 UnknownEscape => "unknown escape",
331 UnterminatedEscape => "unterminated escape: input ended too soon",
332 InvalidXEscape => r"invalid `\x` escape: not followed by two hex digits",
333 NonAsciiXEscape => r"`\x` escape in char/string literal exceed ASCII range",
334 UnicodeEscapeInByteLiteral => r"`\u{...}` escape in byte (string) literal not allowed",
335 InvalidStartOfUnicodeEscape => r"invalid start of `\u{...}` escape",
336 UnicodeEscapeWithoutBrace => r"`Unicode \u{...}` escape without opening brace",
337 NonHexDigitInUnicodeEscape => r"non-hex digit found in `\u{...}` escape",
338 TooManyDigitInUnicodeEscape => r"more than six digits in `\u{...}` escape",
339 InvalidUnicodeEscapeChar => r"value specified in `\u{...}` escape is not a valid char",
340 UnterminatedUnicodeEscape => r"unterminated `\u{...}` escape",
341 UnterminatedCharLiteral => "character literal is not terminated",
342 OverlongCharLiteral => "character literal contains more than one character",
343 EmptyCharLiteral => "empty character literal",
344 UnterminatedByteLiteral => "byte literal is not terminated",
345 OverlongByteLiteral => "byte literal contains more than one byte",
346 EmptyByteLiteral => "empty byte literal",
347 NonAsciiInByteLiteral => "non ASCII character in byte (string) literal",
348 UnescapedSingleQuote => "character literal contains unescaped ' character",
349 UnescapedSpecialWhitespace => r"unescaped newline (\n), tab (\t) or cr (\r) character",
350 DoesNotStartWithQuote => "invalid start for char/byte/string literal",
351 UnterminatedRawString => "unterminated raw (byte) string literal",
352 UnterminatedString => "unterminated (byte) string literal",
353 InvalidStringLiteralStart => "invalid start for string literal",
354 InvalidByteLiteralStart => "invalid start for byte literal",
355 InvalidByteStringLiteralStart => "invalid start for byte string literal",
356 IsolatedCr => r"`\r` not immediately followed by `\n` in string",
357 };
358
359 description.fmt(f)?;
360 if let Some(span) = &self.span {
361 write!(f, " (at {}..{})", span.start, span.end)?;
362 }
363
364 Ok(())
365 }
366 }
367