]> git.lizzy.rs Git - rust.git/blob - src/librustc_lexer/src/lib.rs
Fix font color for help button in ayu and dark themes
[rust.git] / src / librustc_lexer / src / lib.rs
1 //! Low-level Rust lexer.
2 //!
3 //! The idea with `librustc_lexer` is to make a reusable library,
4 //! by separating out pure lexing and rustc-specific concerns, like spans,
5 //! error reporting an interning.  So, rustc_lexer operates directly on `&str`,
6 //! produces simple tokens which are a pair of type-tag and a bit of original text,
7 //! and does not report errors, instead storing them as flags on the token.
8 //!
9 //! Tokens produced by this lexer are not yet ready for parsing the Rust syntax.
10 //! For that see [`librustc_parse::lexer`], which converts this basic token stream
11 //! into wide tokens used by actual parser.
12 //!
13 //! The purpose of this crate is to convert raw sources into a labeled sequence
14 //! of well-known token types, so building an actual Rust token stream will
15 //! be easier.
16 //!
17 //! The main entity of this crate is the [`TokenKind`] enum which represents common
18 //! lexeme types.
19 //!
20 //! [`librustc_parse::lexer`]: ../rustc_parse/lexer/index.html
21 // We want to be able to build this crate with a stable compiler, so no
22 // `#![feature]` attributes should be added.
23
24 mod cursor;
25 pub mod unescape;
26
27 #[cfg(test)]
28 mod tests;
29
30 use self::LiteralKind::*;
31 use self::TokenKind::*;
32 use crate::cursor::{Cursor, EOF_CHAR};
33 use std::convert::TryFrom;
34
35 /// Parsed token.
36 /// It doesn't contain information about data that has been parsed,
37 /// only the type of the token and its size.
38 pub struct Token {
39     pub kind: TokenKind,
40     pub len: usize,
41 }
42
43 impl Token {
44     fn new(kind: TokenKind, len: usize) -> Token {
45         Token { kind, len }
46     }
47 }
48
49 /// Enum representing common lexeme types.
50 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
51 pub enum TokenKind {
52     // Multi-char tokens:
53     /// "// comment"
54     LineComment,
55     /// `/* block comment */`
56     ///
57     /// Block comments can be recursive, so the sequence like `/* /* */`
58     /// will not be considered terminated and will result in a parsing error.
59     BlockComment { terminated: bool },
60     /// Any whitespace characters sequence.
61     Whitespace,
62     /// "ident" or "continue"
63     /// At this step keywords are also considered identifiers.
64     Ident,
65     /// "r#ident"
66     RawIdent,
67     /// "12_u8", "1.0e-40", "b"123"". See `LiteralKind` for more details.
68     Literal { kind: LiteralKind, suffix_start: usize },
69     /// "'a"
70     Lifetime { starts_with_number: bool },
71
72     // One-char tokens:
73     /// ";"
74     Semi,
75     /// ","
76     Comma,
77     /// "."
78     Dot,
79     /// "("
80     OpenParen,
81     /// ")"
82     CloseParen,
83     /// "{"
84     OpenBrace,
85     /// "}"
86     CloseBrace,
87     /// "["
88     OpenBracket,
89     /// "]"
90     CloseBracket,
91     /// "@"
92     At,
93     /// "#"
94     Pound,
95     /// "~"
96     Tilde,
97     /// "?"
98     Question,
99     /// ":"
100     Colon,
101     /// "$"
102     Dollar,
103     /// "="
104     Eq,
105     /// "!"
106     Not,
107     /// "<"
108     Lt,
109     /// ">"
110     Gt,
111     /// "-"
112     Minus,
113     /// "&"
114     And,
115     /// "|"
116     Or,
117     /// "+"
118     Plus,
119     /// "*"
120     Star,
121     /// "/"
122     Slash,
123     /// "^"
124     Caret,
125     /// "%"
126     Percent,
127
128     /// Unknown token, not expected by the lexer, e.g. "№"
129     Unknown,
130 }
131
132 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
133 pub enum LiteralKind {
134     /// "12_u8", "0o100", "0b120i99"
135     Int { base: Base, empty_int: bool },
136     /// "12.34f32", "0b100.100"
137     Float { base: Base, empty_exponent: bool },
138     /// "'a'", "'\\'", "'''", "';"
139     Char { terminated: bool },
140     /// "b'a'", "b'\\'", "b'''", "b';"
141     Byte { terminated: bool },
142     /// ""abc"", ""abc"
143     Str { terminated: bool },
144     /// "b"abc"", "b"abc"
145     ByteStr { terminated: bool },
146     /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a"
147     RawStr { n_hashes: u16, err: Option<RawStrError> },
148     /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a"
149     RawByteStr { n_hashes: u16, err: Option<RawStrError> },
150 }
151
152 /// Error produced validating a raw string. Represents cases like:
153 /// - `r##~"abcde"##`: `InvalidStarter`
154 /// - `r###"abcde"##`: `NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)`
155 /// - Too many `#`s (>65535): `TooManyDelimiters`
156 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
157 pub enum RawStrError {
158     /// Non `#` characters exist between `r` and `"` eg. `r#~"..`
159     InvalidStarter { bad_char: char },
160     /// The string was never terminated. `possible_terminator_offset` is the number of characters after `r` or `br` where they
161     /// may have intended to terminate it.
162     NoTerminator { expected: usize, found: usize, possible_terminator_offset: Option<usize> },
163     /// More than 65535 `#`s exist.
164     TooManyDelimiters { found: usize },
165 }
166
167 /// Base of numeric literal encoding according to its prefix.
168 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
169 pub enum Base {
170     /// Literal starts with "0b".
171     Binary,
172     /// Literal starts with "0o".
173     Octal,
174     /// Literal starts with "0x".
175     Hexadecimal,
176     /// Literal doesn't contain a prefix.
177     Decimal,
178 }
179
180 /// `rustc` allows files to have a shebang, e.g. "#!/usr/bin/rustrun",
181 /// but shebang isn't a part of rust syntax.
182 pub fn strip_shebang(input: &str) -> Option<usize> {
183     // Shebang must start with `#!` literally, without any preceding whitespace.
184     // For simplicity we consider any line starting with `#!` a shebang,
185     // regardless of restrictions put on shebangs by specific platforms.
186     if let Some(input_tail) = input.strip_prefix("#!") {
187         // Ok, this is a shebang but if the next non-whitespace token is `[` or maybe
188         // a doc comment (due to `TokenKind::(Line,Block)Comment` ambiguity at lexer level),
189         // then it may be valid Rust code, so consider it Rust code.
190         let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok|
191             !matches!(tok, TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment { .. })
192         );
193         if next_non_whitespace_token != Some(TokenKind::OpenBracket) {
194             // No other choice than to consider this a shebang.
195             return Some(2 + input_tail.lines().next().unwrap_or_default().len());
196         }
197     }
198     None
199 }
200
201 /// Parses the first token from the provided input string.
202 pub fn first_token(input: &str) -> Token {
203     debug_assert!(!input.is_empty());
204     Cursor::new(input).advance_token()
205 }
206
207 /// Creates an iterator that produces tokens from the input string.
208 pub fn tokenize(mut input: &str) -> impl Iterator<Item = Token> + '_ {
209     std::iter::from_fn(move || {
210         if input.is_empty() {
211             return None;
212         }
213         let token = first_token(input);
214         input = &input[token.len..];
215         Some(token)
216     })
217 }
218
219 /// True if `c` is considered a whitespace according to Rust language definition.
220 /// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html)
221 /// for definitions of these classes.
222 pub fn is_whitespace(c: char) -> bool {
223     // This is Pattern_White_Space.
224     //
225     // Note that this set is stable (ie, it doesn't change with different
226     // Unicode versions), so it's ok to just hard-code the values.
227
228     match c {
229         // Usual ASCII suspects
230         | '\u{0009}' // \t
231         | '\u{000A}' // \n
232         | '\u{000B}' // vertical tab
233         | '\u{000C}' // form feed
234         | '\u{000D}' // \r
235         | '\u{0020}' // space
236
237         // NEXT LINE from latin1
238         | '\u{0085}'
239
240         // Bidi markers
241         | '\u{200E}' // LEFT-TO-RIGHT MARK
242         | '\u{200F}' // RIGHT-TO-LEFT MARK
243
244         // Dedicated whitespace characters from Unicode
245         | '\u{2028}' // LINE SEPARATOR
246         | '\u{2029}' // PARAGRAPH SEPARATOR
247         => true,
248         _ => false,
249     }
250 }
251
252 /// True if `c` is valid as a first character of an identifier.
253 /// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
254 /// a formal definition of valid identifier name.
255 pub fn is_id_start(c: char) -> bool {
256     // This is XID_Start OR '_' (which formally is not a XID_Start).
257     // We also add fast-path for ascii idents
258     ('a' <= c && c <= 'z')
259         || ('A' <= c && c <= 'Z')
260         || c == '_'
261         || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_start(c))
262 }
263
264 /// True if `c` is valid as a non-first character of an identifier.
265 /// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
266 /// a formal definition of valid identifier name.
267 pub fn is_id_continue(c: char) -> bool {
268     // This is exactly XID_Continue.
269     // We also add fast-path for ascii idents
270     ('a' <= c && c <= 'z')
271         || ('A' <= c && c <= 'Z')
272         || ('0' <= c && c <= '9')
273         || c == '_'
274         || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_continue(c))
275 }
276
277 /// The passed string is lexically an identifier.
278 pub fn is_ident(string: &str) -> bool {
279     let mut chars = string.chars();
280     if let Some(start) = chars.next() {
281         is_id_start(start) && chars.all(is_id_continue)
282     } else {
283         false
284     }
285 }
286
287 impl Cursor<'_> {
288     /// Parses a token from the input string.
289     fn advance_token(&mut self) -> Token {
290         let first_char = self.bump().unwrap();
291         let token_kind = match first_char {
292             // Slash, comment or block comment.
293             '/' => match self.first() {
294                 '/' => self.line_comment(),
295                 '*' => self.block_comment(),
296                 _ => Slash,
297             },
298
299             // Whitespace sequence.
300             c if is_whitespace(c) => self.whitespace(),
301
302             // Raw identifier, raw string literal or identifier.
303             'r' => match (self.first(), self.second()) {
304                 ('#', c1) if is_id_start(c1) => self.raw_ident(),
305                 ('#', _) | ('"', _) => {
306                     let (n_hashes, err) = self.raw_double_quoted_string(1);
307                     let suffix_start = self.len_consumed();
308                     if err.is_none() {
309                         self.eat_literal_suffix();
310                     }
311                     let kind = RawStr { n_hashes, err };
312                     Literal { kind, suffix_start }
313                 }
314                 _ => self.ident(),
315             },
316
317             // Byte literal, byte string literal, raw byte string literal or identifier.
318             'b' => match (self.first(), self.second()) {
319                 ('\'', _) => {
320                     self.bump();
321                     let terminated = self.single_quoted_string();
322                     let suffix_start = self.len_consumed();
323                     if terminated {
324                         self.eat_literal_suffix();
325                     }
326                     let kind = Byte { terminated };
327                     Literal { kind, suffix_start }
328                 }
329                 ('"', _) => {
330                     self.bump();
331                     let terminated = self.double_quoted_string();
332                     let suffix_start = self.len_consumed();
333                     if terminated {
334                         self.eat_literal_suffix();
335                     }
336                     let kind = ByteStr { terminated };
337                     Literal { kind, suffix_start }
338                 }
339                 ('r', '"') | ('r', '#') => {
340                     self.bump();
341                     let (n_hashes, err) = self.raw_double_quoted_string(2);
342                     let suffix_start = self.len_consumed();
343                     if err.is_none() {
344                         self.eat_literal_suffix();
345                     }
346                     let kind = RawByteStr { n_hashes, err };
347                     Literal { kind, suffix_start }
348                 }
349                 _ => self.ident(),
350             },
351
352             // Identifier (this should be checked after other variant that can
353             // start as identifier).
354             c if is_id_start(c) => self.ident(),
355
356             // Numeric literal.
357             c @ '0'..='9' => {
358                 let literal_kind = self.number(c);
359                 let suffix_start = self.len_consumed();
360                 self.eat_literal_suffix();
361                 TokenKind::Literal { kind: literal_kind, suffix_start }
362             }
363
364             // One-symbol tokens.
365             ';' => Semi,
366             ',' => Comma,
367             '.' => Dot,
368             '(' => OpenParen,
369             ')' => CloseParen,
370             '{' => OpenBrace,
371             '}' => CloseBrace,
372             '[' => OpenBracket,
373             ']' => CloseBracket,
374             '@' => At,
375             '#' => Pound,
376             '~' => Tilde,
377             '?' => Question,
378             ':' => Colon,
379             '$' => Dollar,
380             '=' => Eq,
381             '!' => Not,
382             '<' => Lt,
383             '>' => Gt,
384             '-' => Minus,
385             '&' => And,
386             '|' => Or,
387             '+' => Plus,
388             '*' => Star,
389             '^' => Caret,
390             '%' => Percent,
391
392             // Lifetime or character literal.
393             '\'' => self.lifetime_or_char(),
394
395             // String literal.
396             '"' => {
397                 let terminated = self.double_quoted_string();
398                 let suffix_start = self.len_consumed();
399                 if terminated {
400                     self.eat_literal_suffix();
401                 }
402                 let kind = Str { terminated };
403                 Literal { kind, suffix_start }
404             }
405             _ => Unknown,
406         };
407         Token::new(token_kind, self.len_consumed())
408     }
409
410     fn line_comment(&mut self) -> TokenKind {
411         debug_assert!(self.prev() == '/' && self.first() == '/');
412         self.bump();
413         self.eat_while(|c| c != '\n');
414         LineComment
415     }
416
417     fn block_comment(&mut self) -> TokenKind {
418         debug_assert!(self.prev() == '/' && self.first() == '*');
419         self.bump();
420         let mut depth = 1usize;
421         while let Some(c) = self.bump() {
422             match c {
423                 '/' if self.first() == '*' => {
424                     self.bump();
425                     depth += 1;
426                 }
427                 '*' if self.first() == '/' => {
428                     self.bump();
429                     depth -= 1;
430                     if depth == 0 {
431                         // This block comment is closed, so for a construction like "/* */ */"
432                         // there will be a successfully parsed block comment "/* */"
433                         // and " */" will be processed separately.
434                         break;
435                     }
436                 }
437                 _ => (),
438             }
439         }
440
441         BlockComment { terminated: depth == 0 }
442     }
443
444     fn whitespace(&mut self) -> TokenKind {
445         debug_assert!(is_whitespace(self.prev()));
446         self.eat_while(is_whitespace);
447         Whitespace
448     }
449
450     fn raw_ident(&mut self) -> TokenKind {
451         debug_assert!(self.prev() == 'r' && self.first() == '#' && is_id_start(self.second()));
452         // Eat "#" symbol.
453         self.bump();
454         // Eat the identifier part of RawIdent.
455         self.eat_identifier();
456         RawIdent
457     }
458
459     fn ident(&mut self) -> TokenKind {
460         debug_assert!(is_id_start(self.prev()));
461         // Start is already eaten, eat the rest of identifier.
462         self.eat_while(is_id_continue);
463         Ident
464     }
465
466     fn number(&mut self, first_digit: char) -> LiteralKind {
467         debug_assert!('0' <= self.prev() && self.prev() <= '9');
468         let mut base = Base::Decimal;
469         if first_digit == '0' {
470             // Attempt to parse encoding base.
471             let has_digits = match self.first() {
472                 'b' => {
473                     base = Base::Binary;
474                     self.bump();
475                     self.eat_decimal_digits()
476                 }
477                 'o' => {
478                     base = Base::Octal;
479                     self.bump();
480                     self.eat_decimal_digits()
481                 }
482                 'x' => {
483                     base = Base::Hexadecimal;
484                     self.bump();
485                     self.eat_hexadecimal_digits()
486                 }
487                 // Not a base prefix.
488                 '0'..='9' | '_' | '.' | 'e' | 'E' => {
489                     self.eat_decimal_digits();
490                     true
491                 }
492                 // Just a 0.
493                 _ => return Int { base, empty_int: false },
494             };
495             // Base prefix was provided, but there were no digits
496             // after it, e.g. "0x".
497             if !has_digits {
498                 return Int { base, empty_int: true };
499             }
500         } else {
501             // No base prefix, parse number in the usual way.
502             self.eat_decimal_digits();
503         };
504
505         match self.first() {
506             // Don't be greedy if this is actually an
507             // integer literal followed by field/method access or a range pattern
508             // (`0..2` and `12.foo()`)
509             '.' if self.second() != '.' && !is_id_start(self.second()) => {
510                 // might have stuff after the ., and if it does, it needs to start
511                 // with a number
512                 self.bump();
513                 let mut empty_exponent = false;
514                 if self.first().is_digit(10) {
515                     self.eat_decimal_digits();
516                     match self.first() {
517                         'e' | 'E' => {
518                             self.bump();
519                             empty_exponent = !self.eat_float_exponent();
520                         }
521                         _ => (),
522                     }
523                 }
524                 Float { base, empty_exponent }
525             }
526             'e' | 'E' => {
527                 self.bump();
528                 let empty_exponent = !self.eat_float_exponent();
529                 Float { base, empty_exponent }
530             }
531             _ => Int { base, empty_int: false },
532         }
533     }
534
535     fn lifetime_or_char(&mut self) -> TokenKind {
536         debug_assert!(self.prev() == '\'');
537
538         let can_be_a_lifetime = if self.second() == '\'' {
539             // It's surely not a lifetime.
540             false
541         } else {
542             // If the first symbol is valid for identifier, it can be a lifetime.
543             // Also check if it's a number for a better error reporting (so '0 will
544             // be reported as invalid lifetime and not as unterminated char literal).
545             is_id_start(self.first()) || self.first().is_digit(10)
546         };
547
548         if !can_be_a_lifetime {
549             let terminated = self.single_quoted_string();
550             let suffix_start = self.len_consumed();
551             if terminated {
552                 self.eat_literal_suffix();
553             }
554             let kind = Char { terminated };
555             return Literal { kind, suffix_start };
556         }
557
558         // Either a lifetime or a character literal with
559         // length greater than 1.
560
561         let starts_with_number = self.first().is_digit(10);
562
563         // Skip the literal contents.
564         // First symbol can be a number (which isn't a valid identifier start),
565         // so skip it without any checks.
566         self.bump();
567         self.eat_while(is_id_continue);
568
569         // Check if after skipping literal contents we've met a closing
570         // single quote (which means that user attempted to create a
571         // string with single quotes).
572         if self.first() == '\'' {
573             self.bump();
574             let kind = Char { terminated: true };
575             Literal { kind, suffix_start: self.len_consumed() }
576         } else {
577             Lifetime { starts_with_number }
578         }
579     }
580
581     fn single_quoted_string(&mut self) -> bool {
582         debug_assert!(self.prev() == '\'');
583         // Check if it's a one-symbol literal.
584         if self.second() == '\'' && self.first() != '\\' {
585             self.bump();
586             self.bump();
587             return true;
588         }
589
590         // Literal has more than one symbol.
591
592         // Parse until either quotes are terminated or error is detected.
593         loop {
594             match self.first() {
595                 // Quotes are terminated, finish parsing.
596                 '\'' => {
597                     self.bump();
598                     return true;
599                 }
600                 // Probably beginning of the comment, which we don't want to include
601                 // to the error report.
602                 '/' => break,
603                 // Newline without following '\'' means unclosed quote, stop parsing.
604                 '\n' if self.second() != '\'' => break,
605                 // End of file, stop parsing.
606                 EOF_CHAR if self.is_eof() => break,
607                 // Escaped slash is considered one character, so bump twice.
608                 '\\' => {
609                     self.bump();
610                     self.bump();
611                 }
612                 // Skip the character.
613                 _ => {
614                     self.bump();
615                 }
616             }
617         }
618         // String was not terminated.
619         false
620     }
621
622     /// Eats double-quoted string and returns true
623     /// if string is terminated.
624     fn double_quoted_string(&mut self) -> bool {
625         debug_assert!(self.prev() == '"');
626         while let Some(c) = self.bump() {
627             match c {
628                 '"' => {
629                     return true;
630                 }
631                 '\\' if self.first() == '\\' || self.first() == '"' => {
632                     // Bump again to skip escaped character.
633                     self.bump();
634                 }
635                 _ => (),
636             }
637         }
638         // End of file reached.
639         false
640     }
641
642     /// Eats the double-quoted string and returns `n_hashes` and an error if encountered.
643     fn raw_double_quoted_string(&mut self, prefix_len: usize) -> (u16, Option<RawStrError>) {
644         // Wrap the actual function to handle the error with too many hashes.
645         // This way, it eats the whole raw string.
646         let (n_hashes, err) = self.raw_string_unvalidated(prefix_len);
647         // Only up to 65535 `#`s are allowed in raw strings
648         match u16::try_from(n_hashes) {
649             Ok(num) => (num, err),
650             // We lie about the number of hashes here :P
651             Err(_) => (0, Some(RawStrError::TooManyDelimiters { found: n_hashes })),
652         }
653     }
654
655     fn raw_string_unvalidated(&mut self, prefix_len: usize) -> (usize, Option<RawStrError>) {
656         debug_assert!(self.prev() == 'r');
657         let start_pos = self.len_consumed();
658         let mut possible_terminator_offset = None;
659         let mut max_hashes = 0;
660
661         // Count opening '#' symbols.
662         let n_start_hashes = self.eat_while(|c| c == '#');
663
664         // Check that string is started.
665         match self.bump() {
666             Some('"') => (),
667             c => {
668                 let c = c.unwrap_or(EOF_CHAR);
669                 return (n_start_hashes, Some(RawStrError::InvalidStarter { bad_char: c }));
670             }
671         }
672
673         // Skip the string contents and on each '#' character met, check if this is
674         // a raw string termination.
675         loop {
676             self.eat_while(|c| c != '"');
677
678             if self.is_eof() {
679                 return (
680                     n_start_hashes,
681                     Some(RawStrError::NoTerminator {
682                         expected: n_start_hashes,
683                         found: max_hashes,
684                         possible_terminator_offset,
685                     }),
686                 );
687             }
688
689             // Eat closing double quote.
690             self.bump();
691
692             // Check that amount of closing '#' symbols
693             // is equal to the amount of opening ones.
694             // Note that this will not consume extra trailing `#` characters:
695             // `r###"abcde"####` is lexed as a `RawStr { n_hashes: 3 }`
696             // followed by a `#` token.
697             let mut hashes_left = n_start_hashes;
698             let is_closing_hash = |c| {
699                 if c == '#' && hashes_left != 0 {
700                     hashes_left -= 1;
701                     true
702                 } else {
703                     false
704                 }
705             };
706             let n_end_hashes = self.eat_while(is_closing_hash);
707
708             if n_end_hashes == n_start_hashes {
709                 return (n_start_hashes, None);
710             } else if n_end_hashes > max_hashes {
711                 // Keep track of possible terminators to give a hint about
712                 // where there might be a missing terminator
713                 possible_terminator_offset =
714                     Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len);
715                 max_hashes = n_end_hashes;
716             }
717         }
718     }
719
720     fn eat_decimal_digits(&mut self) -> bool {
721         let mut has_digits = false;
722         loop {
723             match self.first() {
724                 '_' => {
725                     self.bump();
726                 }
727                 '0'..='9' => {
728                     has_digits = true;
729                     self.bump();
730                 }
731                 _ => break,
732             }
733         }
734         has_digits
735     }
736
737     fn eat_hexadecimal_digits(&mut self) -> bool {
738         let mut has_digits = false;
739         loop {
740             match self.first() {
741                 '_' => {
742                     self.bump();
743                 }
744                 '0'..='9' | 'a'..='f' | 'A'..='F' => {
745                     has_digits = true;
746                     self.bump();
747                 }
748                 _ => break,
749             }
750         }
751         has_digits
752     }
753
754     /// Eats the float exponent. Returns true if at least one digit was met,
755     /// and returns false otherwise.
756     fn eat_float_exponent(&mut self) -> bool {
757         debug_assert!(self.prev() == 'e' || self.prev() == 'E');
758         if self.first() == '-' || self.first() == '+' {
759             self.bump();
760         }
761         self.eat_decimal_digits()
762     }
763
764     // Eats the suffix of the literal, e.g. "_u8".
765     fn eat_literal_suffix(&mut self) {
766         self.eat_identifier();
767     }
768
769     // Eats the identifier.
770     fn eat_identifier(&mut self) {
771         if !is_id_start(self.first()) {
772             return;
773         }
774         self.bump();
775
776         self.eat_while(is_id_continue);
777     }
778
779     /// Eats symbols while predicate returns true or until the end of file is reached.
780     /// Returns amount of eaten symbols.
781     fn eat_while<F>(&mut self, mut predicate: F) -> usize
782     where
783         F: FnMut(char) -> bool,
784     {
785         let mut eaten: usize = 0;
786         while predicate(self.first()) && !self.is_eof() {
787             eaten += 1;
788             self.bump();
789         }
790
791         eaten
792     }
793 }