1use std::{fmt, ops::Range};
2
3
4#[derive(Debug, Clone, Copy)]
7pub struct InvalidToken {
8 pub(crate) expected: TokenKind,
9 pub(crate) actual: TokenKind,
10 pub(crate) span: Span,
11}
12
13impl InvalidToken {
14 pub fn to_compile_error(&self) -> proc_macro::TokenStream {
18 use proc_macro::{Delimiter, Group, Ident, 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 #[cfg(feature = "proc-macro2")]
43 pub fn to_compile_error2(&self) -> proc_macro2::TokenStream {
44 use proc_macro2::{Delimiter, Group, Ident, 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
65impl std::error::Error for InvalidToken {}
66
67impl 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 TokenKind::CStringLit => r#"a C string literal (e.g. c"Ferris")"#,
83 }
84 }
85
86 write!(f, "expected {}, but found {}", kind_desc(self.expected), kind_desc(self.actual))
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub(crate) enum TokenKind {
92 Punct,
93 Ident,
94 Group,
95 Literal,
96 BoolLit,
97 ByteLit,
98 ByteStringLit,
99 CharLit,
100 FloatLit,
101 IntegerLit,
102 StringLit,
103 CStringLit,
104}
105
106#[derive(Debug, Clone, Copy)]
108pub(crate) enum Span {
109 One(proc_macro::Span),
110 #[cfg(feature = "proc-macro2")]
111 Two(proc_macro2::Span),
112}
113
114impl From<proc_macro::Span> for Span {
115 fn from(src: proc_macro::Span) -> Self {
116 Self::One(src)
117 }
118}
119
120#[cfg(feature = "proc-macro2")]
121impl From<proc_macro2::Span> for Span {
122 fn from(src: proc_macro2::Span) -> Self {
123 Self::Two(src)
124 }
125}
126
127#[derive(Debug, Clone)]
156pub struct ParseError {
157 pub(crate) span: Option<Range<usize>>,
158 pub(crate) kind: ParseErrorKind,
159}
160
161impl ParseError {
162 pub fn span(&self) -> Option<Range<usize>> {
166 self.span.clone()
167 }
168
169 pub(crate) fn offset_span(self, offset: usize) -> Self {
171 Self {
172 span: self.span.map(|span| span.start + offset..span.end + offset),
173 ..self
174 }
175 }
176}
177
178pub(crate) fn perr(span: impl SpanLike, kind: ParseErrorKind) -> ParseError {
182 ParseError {
183 span: span.into_span(),
184 kind,
185 }
186}
187
188pub(crate) trait SpanLike {
189 fn into_span(self) -> Option<Range<usize>>;
190}
191
192impl SpanLike for Option<Range<usize>> {
193 #[inline(always)]
194 fn into_span(self) -> Option<Range<usize>> {
195 self
196 }
197}
198impl SpanLike for Range<usize> {
199 #[inline(always)]
200 fn into_span(self) -> Option<Range<usize>> {
201 Some(self)
202 }
203}
204impl SpanLike for usize {
205 #[inline(always)]
206 fn into_span(self) -> Option<Range<usize>> {
207 Some(self..self + 1)
208 }
209}
210
211
212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214#[non_exhaustive]
215pub(crate) enum ParseErrorKind {
216 Empty,
218
219 UnexpectedChar,
221
222 InvalidLiteral,
224
225 DoesNotStartWithDigit,
227
228 InvalidDigit,
230
231 NoDigits,
233
234 NoExponentDigits,
236
237 UnknownEscape,
239
240 UnterminatedEscape,
243
244 InvalidXEscape,
246
247 NonAsciiXEscape,
249
250 UnicodeEscapeInByteLiteral,
252
253 InvalidStartOfUnicodeEscape,
255
256 UnicodeEscapeWithoutBrace,
258
259 NonHexDigitInUnicodeEscape,
262
263 TooManyDigitInUnicodeEscape,
265
266 InvalidUnicodeEscapeChar,
268
269 UnterminatedUnicodeEscape,
271
272 UnterminatedCharLiteral,
274
275 OverlongCharLiteral,
277
278 EmptyCharLiteral,
280
281 UnterminatedByteLiteral,
282 OverlongByteLiteral,
283 EmptyByteLiteral,
284 NonAsciiInByteLiteral,
285
286 UnescapedSingleQuote,
289
290 UnescapedSpecialWhitespace,
292
293 DoesNotStartWithQuote,
297
298 UnterminatedRawString,
300
301 UnterminatedString,
303
304 InvalidStringLiteralStart,
306
307 InvalidByteLiteralStart,
309
310 InvalidByteStringLiteralStart,
311
312 InvalidCStringLiteralStart,
314
315 DisallowedNulEscape,
317
318 NulByte,
320
321 CarriageReturn,
323
324 TooManyHashes,
326
327 InvalidSuffix,
329
330 UnexpectedIntegerLit,
333
334 IntegerSuffixStartingWithE,
337}
338
339impl std::error::Error for ParseError {}
340
341impl fmt::Display for ParseError {
342 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
343 use ParseErrorKind::*;
344
345 let description = match self.kind {
346 Empty => "input is empty",
347 UnexpectedChar => "unexpected character",
348 InvalidLiteral => "invalid literal",
349 DoesNotStartWithDigit => "number literal does not start with decimal digit",
350 InvalidDigit => "integer literal contains a digit invalid for its base",
351 NoDigits => "integer literal does not contain any digits",
352 NoExponentDigits => "exponent of floating point literal does not contain any digits",
353 UnknownEscape => "unknown escape",
354 UnterminatedEscape => "unterminated escape: input ended too soon",
355 InvalidXEscape => r"invalid `\x` escape: not followed by two hex digits",
356 NonAsciiXEscape => r"`\x` escape in char/string literal exceed ASCII range",
357 UnicodeEscapeInByteLiteral => r"`\u{...}` escape in byte (string) literal not allowed",
358 InvalidStartOfUnicodeEscape => r"invalid start of `\u{...}` escape",
359 UnicodeEscapeWithoutBrace => r"`Unicode \u{...}` escape without opening brace",
360 NonHexDigitInUnicodeEscape => r"non-hex digit found in `\u{...}` escape",
361 TooManyDigitInUnicodeEscape => r"more than six digits in `\u{...}` escape",
362 InvalidUnicodeEscapeChar => r"value specified in `\u{...}` escape is not a valid char",
363 UnterminatedUnicodeEscape => r"unterminated `\u{...}` escape",
364 UnterminatedCharLiteral => "character literal is not terminated",
365 OverlongCharLiteral => "character literal contains more than one character",
366 EmptyCharLiteral => "empty character literal",
367 UnterminatedByteLiteral => "byte literal is not terminated",
368 OverlongByteLiteral => "byte literal contains more than one byte",
369 EmptyByteLiteral => "empty byte literal",
370 NonAsciiInByteLiteral => "non ASCII character in byte (string) literal",
371 UnescapedSingleQuote => "character literal contains unescaped ' character",
372 UnescapedSpecialWhitespace => r"unescaped newline (\n), tab (\t) or cr (\r) character",
373 DoesNotStartWithQuote => "invalid start for char/byte/string literal",
374 UnterminatedRawString => "unterminated raw (byte) string literal",
375 UnterminatedString => "unterminated (byte) string literal",
376 InvalidStringLiteralStart => "invalid start for string literal",
377 InvalidByteLiteralStart => "invalid start for byte literal",
378 InvalidByteStringLiteralStart => "invalid start for byte string literal",
379 InvalidCStringLiteralStart => "invalid start for C string literal",
380 DisallowedNulEscape => r"`\0` escape not allowed inside C string literal",
381 NulByte => r"nul byte not allowed inside C string literal",
382 CarriageReturn => r"`\r` not allowed in string literals",
383 TooManyHashes => "raw string literal has too many # symbols (max 256)",
384 InvalidSuffix => "literal suffix is not a valid identifier",
385 UnexpectedIntegerLit => "expected float literal, but found integer",
386 IntegerSuffixStartingWithE => "integer literal suffix must not start with 'e' or 'E'",
387 };
388
389 description.fmt(f)?;
390 if let Some(span) = &self.span {
391 write!(f, " (at {}..{})", span.start, span.end)?;
392 }
393
394 Ok(())
395 }
396}