• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     /// 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 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result321     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