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