Skip to main content

rustc_lexer/
lib.rs

1//! Low-level Rust lexer.
2//!
3//! The idea with `rustc_lexer` is to make a reusable library,
4//! by separating out pure lexing and rustc-specific concerns, like spans,
5//! error reporting, and 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 [`rustc_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//! [`rustc_parse::lexer`]: ../rustc_parse/lexer/index.html
21
22// tidy-alphabetical-start
23// We want to be able to build this crate with a stable compiler,
24// so no `#![feature]` attributes should be added.
25#![deny(unstable_features)]
26// tidy-alphabetical-end
27
28#[cfg(test)]
29mod tests;
30
31use std::str::Chars;
32
33use LiteralKind::*;
34use TokenKind::*;
35pub use unicode_ident::UNICODE_VERSION;
36use unicode_properties::UnicodeEmoji;
37
38// Make sure that the Unicode version of the dependencies is the same.
39const _: () = {
40    let properties = unicode_properties::UNICODE_VERSION;
41    let ident = unicode_ident::UNICODE_VERSION;
42
43    if properties.0 != ident.0 as u64
44        || properties.1 != ident.1 as u64
45        || properties.2 != ident.2 as u64
46    {
47        {
    ::core::panicking::panic_fmt(format_args!("unicode-properties and unicode-ident must use the same Unicode version, `unicode_properties::UNICODE_VERSION` and `unicode_ident::UNICODE_VERSION` are different."));
};panic!(
48            "unicode-properties and unicode-ident must use the same Unicode version, \
49            `unicode_properties::UNICODE_VERSION` and `unicode_ident::UNICODE_VERSION` are \
50            different."
51        );
52    }
53};
54
55/// Parsed token.
56/// It doesn't contain information about data that has been parsed,
57/// only the type of the token and its size.
58#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Token {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "Token", "kind",
            &self.kind, "len", &&self.len)
    }
}Debug)]
59pub struct Token {
60    pub kind: TokenKind,
61    pub len: u32,
62}
63
64impl Token {
65    fn new(kind: TokenKind, len: u32) -> Token {
66        Token { kind, len }
67    }
68}
69
70/// Enum representing common lexeme types.
71#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for TokenKind { }
#[automatically_derived]
impl ::core::clone::Clone for TokenKind {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Option<DocStyle>>;
        let _: ::core::clone::AssertParamIsClone<Option<DocStyle>>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        let _: ::core::clone::AssertParamIsClone<LiteralKind>;
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for TokenKind { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for TokenKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::LineComment { doc_style: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "LineComment", "doc_style", &__self_0),
            Self::BlockComment { doc_style: __self_0, terminated: __self_1 }
                =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "BlockComment", "doc_style", __self_0, "terminated",
                    &__self_1),
            Self::Whitespace =>
                ::core::fmt::Formatter::write_str(f, "Whitespace"),
            Self::Frontmatter {
                has_invalid_preceding_whitespace: __self_0,
                invalid_infostring: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "Frontmatter", "has_invalid_preceding_whitespace", __self_0,
                    "invalid_infostring", &__self_1),
            Self::Ident => ::core::fmt::Formatter::write_str(f, "Ident"),
            Self::InvalidIdent =>
                ::core::fmt::Formatter::write_str(f, "InvalidIdent"),
            Self::RawIdent =>
                ::core::fmt::Formatter::write_str(f, "RawIdent"),
            Self::ForcedKeywordIdent =>
                ::core::fmt::Formatter::write_str(f, "ForcedKeywordIdent"),
            Self::UnknownPrefix =>
                ::core::fmt::Formatter::write_str(f, "UnknownPrefix"),
            Self::UnknownPrefixLifetime =>
                ::core::fmt::Formatter::write_str(f, "UnknownPrefixLifetime"),
            Self::RawLifetime =>
                ::core::fmt::Formatter::write_str(f, "RawLifetime"),
            Self::GuardedStrPrefix =>
                ::core::fmt::Formatter::write_str(f, "GuardedStrPrefix"),
            Self::Literal { kind: __self_0, suffix_start: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "Literal", "kind", __self_0, "suffix_start", &__self_1),
            Self::Lifetime { starts_with_number: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "Lifetime", "starts_with_number", &__self_0),
            Self::Semi => ::core::fmt::Formatter::write_str(f, "Semi"),
            Self::Comma => ::core::fmt::Formatter::write_str(f, "Comma"),
            Self::Dot => ::core::fmt::Formatter::write_str(f, "Dot"),
            Self::OpenParen =>
                ::core::fmt::Formatter::write_str(f, "OpenParen"),
            Self::CloseParen =>
                ::core::fmt::Formatter::write_str(f, "CloseParen"),
            Self::OpenBrace =>
                ::core::fmt::Formatter::write_str(f, "OpenBrace"),
            Self::CloseBrace =>
                ::core::fmt::Formatter::write_str(f, "CloseBrace"),
            Self::OpenBracket =>
                ::core::fmt::Formatter::write_str(f, "OpenBracket"),
            Self::CloseBracket =>
                ::core::fmt::Formatter::write_str(f, "CloseBracket"),
            Self::At => ::core::fmt::Formatter::write_str(f, "At"),
            Self::Pound => ::core::fmt::Formatter::write_str(f, "Pound"),
            Self::Tilde => ::core::fmt::Formatter::write_str(f, "Tilde"),
            Self::Question =>
                ::core::fmt::Formatter::write_str(f, "Question"),
            Self::Colon => ::core::fmt::Formatter::write_str(f, "Colon"),
            Self::Dollar => ::core::fmt::Formatter::write_str(f, "Dollar"),
            Self::Eq => ::core::fmt::Formatter::write_str(f, "Eq"),
            Self::Bang => ::core::fmt::Formatter::write_str(f, "Bang"),
            Self::Lt => ::core::fmt::Formatter::write_str(f, "Lt"),
            Self::Gt => ::core::fmt::Formatter::write_str(f, "Gt"),
            Self::Minus => ::core::fmt::Formatter::write_str(f, "Minus"),
            Self::And => ::core::fmt::Formatter::write_str(f, "And"),
            Self::Or => ::core::fmt::Formatter::write_str(f, "Or"),
            Self::Plus => ::core::fmt::Formatter::write_str(f, "Plus"),
            Self::Star => ::core::fmt::Formatter::write_str(f, "Star"),
            Self::Slash => ::core::fmt::Formatter::write_str(f, "Slash"),
            Self::Caret => ::core::fmt::Formatter::write_str(f, "Caret"),
            Self::Percent => ::core::fmt::Formatter::write_str(f, "Percent"),
            Self::Unknown => ::core::fmt::Formatter::write_str(f, "Unknown"),
            Self::Eof => ::core::fmt::Formatter::write_str(f, "Eof"),
        }
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for TokenKind { }
#[automatically_derived]
impl ::core::cmp::PartialEq for TokenKind {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::LineComment { doc_style: __self_0 },
                    Self::LineComment { doc_style: __arg1_0 }) =>
                    __self_0 == __arg1_0,
                (Self::BlockComment {
                    doc_style: __self_0, terminated: __self_1 },
                    Self::BlockComment {
                    doc_style: __arg1_0, terminated: __arg1_1 }) =>
                    __self_1 == __arg1_1 && __self_0 == __arg1_0,
                (Self::Frontmatter {
                    has_invalid_preceding_whitespace: __self_0,
                    invalid_infostring: __self_1 }, Self::Frontmatter {
                    has_invalid_preceding_whitespace: __arg1_0,
                    invalid_infostring: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (Self::Literal { kind: __self_0, suffix_start: __self_1 },
                    Self::Literal { kind: __arg1_0, suffix_start: __arg1_1 }) =>
                    __self_1 == __arg1_1 && __self_0 == __arg1_0,
                (Self::Lifetime { starts_with_number: __self_0 },
                    Self::Lifetime { starts_with_number: __arg1_0 }) =>
                    __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for TokenKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Option<DocStyle>>;
        let _: ::core::cmp::AssertParamIsEq<Option<DocStyle>>;
        let _: ::core::cmp::AssertParamIsEq<bool>;
        let _: ::core::cmp::AssertParamIsEq<LiteralKind>;
        let _: ::core::cmp::AssertParamIsEq<u32>;
    }
}Eq)]
72pub enum TokenKind {
73    /// A line comment, e.g. `// comment`.
74    LineComment {
75        doc_style: Option<DocStyle>,
76    },
77
78    /// A block comment, e.g. `/* block comment */`.
79    ///
80    /// Block comments can be recursive, so a sequence like `/* /* */`
81    /// will not be considered terminated and will result in a parsing error.
82    BlockComment {
83        doc_style: Option<DocStyle>,
84        terminated: bool,
85    },
86
87    /// Any whitespace character sequence.
88    Whitespace,
89
90    Frontmatter {
91        has_invalid_preceding_whitespace: bool,
92        invalid_infostring: bool,
93    },
94
95    /// An identifier or keyword, e.g. `ident` or `continue`.
96    Ident,
97
98    /// An identifier that is invalid because it contains emoji.
99    InvalidIdent,
100
101    /// A raw identifier, e.g. "r#ident".
102    RawIdent,
103
104    /// A forced keyword like `k#fn`.
105    ForcedKeywordIdent,
106
107    /// An unknown literal prefix, like `foo#`, `foo'`, `foo"`. Excludes
108    /// literal prefixes that contain emoji, which are considered "invalid".
109    ///
110    /// Note that only the
111    /// prefix (`foo`) is included in the token, not the separator (which is
112    /// lexed as its own distinct token). In Rust 2021 and later, reserved
113    /// prefixes are reported as errors; in earlier editions, they result in a
114    /// (allowed by default) lint, and are treated as regular identifier
115    /// tokens.
116    UnknownPrefix,
117
118    /// An unknown prefix in a lifetime, like `'foo#`.
119    ///
120    /// Like `UnknownPrefix`, only the `'` and prefix are included in the token
121    /// and not the separator.
122    UnknownPrefixLifetime,
123
124    /// A raw lifetime, e.g. `'r#foo`. In edition < 2021 it will be split into
125    /// several tokens: `'r` and `#` and `foo`.
126    RawLifetime,
127
128    /// Guarded string literal prefix: `#"` or `##`.
129    ///
130    /// Used for reserving "guarded strings" (RFC 3598) in edition 2024.
131    /// Split into the component tokens on older editions.
132    GuardedStrPrefix,
133
134    /// Literals, e.g. `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid
135    /// suffix, but may be present here on string and float literals. Users of
136    /// this type will need to check for and reject that case.
137    ///
138    /// See [LiteralKind] for more details.
139    Literal {
140        kind: LiteralKind,
141        suffix_start: u32,
142    },
143
144    /// A lifetime, e.g. `'a`.
145    Lifetime {
146        starts_with_number: bool,
147    },
148
149    /// `;`
150    Semi,
151    /// `,`
152    Comma,
153    /// `.`
154    Dot,
155    /// `(`
156    OpenParen,
157    /// `)`
158    CloseParen,
159    /// `{`
160    OpenBrace,
161    /// `}`
162    CloseBrace,
163    /// `[`
164    OpenBracket,
165    /// `]`
166    CloseBracket,
167    /// `@`
168    At,
169    /// `#`
170    Pound,
171    /// `~`
172    Tilde,
173    /// `?`
174    Question,
175    /// `:`
176    Colon,
177    /// `$`
178    Dollar,
179    /// `=`
180    Eq,
181    /// `!`
182    Bang,
183    /// `<`
184    Lt,
185    /// `>`
186    Gt,
187    /// `-`
188    Minus,
189    /// `&`
190    And,
191    /// `|`
192    Or,
193    /// `+`
194    Plus,
195    /// `*`
196    Star,
197    /// `/`
198    Slash,
199    /// `^`
200    Caret,
201    /// `%`
202    Percent,
203
204    /// Unknown token, not expected by the lexer, e.g. "№"
205    Unknown,
206
207    /// End of input.
208    Eof,
209}
210
211#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for DocStyle { }
#[automatically_derived]
impl ::core::clone::Clone for DocStyle {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for DocStyle { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for DocStyle {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                DocStyle::Outer => "Outer",
                DocStyle::Inner => "Inner",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for DocStyle { }
#[automatically_derived]
impl ::core::cmp::PartialEq for DocStyle {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DocStyle { }Eq)]
212pub enum DocStyle {
213    Outer,
214    Inner,
215}
216
217/// Enum representing the literal types supported by the lexer.
218///
219/// Note that the suffix is *not* considered when deciding the `LiteralKind` in
220/// this type. This means that float literals like `1f32` are classified by this
221/// type as `Int`. (Compare against `rustc_ast::token::LitKind` and
222/// `rustc_ast::ast::LitKind`).
223#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for LiteralKind { }
#[automatically_derived]
impl ::core::clone::Clone for LiteralKind {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Base>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        let _: ::core::clone::AssertParamIsClone<Option<u8>>;
        let _: ::core::clone::AssertParamIsClone<Option<u8>>;
        let _: ::core::clone::AssertParamIsClone<Option<u8>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LiteralKind { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for LiteralKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Int { base: __self_0, empty_int: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f, "Int",
                    "base", __self_0, "empty_int", &__self_1),
            Self::Float { base: __self_0, empty_exponent: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f, "Float",
                    "base", __self_0, "empty_exponent", &__self_1),
            Self::Char { terminated: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f, "Char",
                    "terminated", &__self_0),
            Self::Byte { terminated: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f, "Byte",
                    "terminated", &__self_0),
            Self::Str { terminated: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f, "Str",
                    "terminated", &__self_0),
            Self::ByteStr { terminated: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "ByteStr", "terminated", &__self_0),
            Self::CStr { terminated: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f, "CStr",
                    "terminated", &__self_0),
            Self::RawStr { n_hashes: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "RawStr", "n_hashes", &__self_0),
            Self::RawByteStr { n_hashes: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "RawByteStr", "n_hashes", &__self_0),
            Self::RawCStr { n_hashes: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "RawCStr", "n_hashes", &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for LiteralKind { }
#[automatically_derived]
impl ::core::cmp::PartialEq for LiteralKind {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::Int { base: __self_0, empty_int: __self_1 },
                    Self::Int { base: __arg1_0, empty_int: __arg1_1 }) =>
                    __self_1 == __arg1_1 && __self_0 == __arg1_0,
                (Self::Float { base: __self_0, empty_exponent: __self_1 },
                    Self::Float { base: __arg1_0, empty_exponent: __arg1_1 }) =>
                    __self_1 == __arg1_1 && __self_0 == __arg1_0,
                (Self::Char { terminated: __self_0 }, Self::Char {
                    terminated: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::Byte { terminated: __self_0 }, Self::Byte {
                    terminated: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::Str { terminated: __self_0 }, Self::Str {
                    terminated: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::ByteStr { terminated: __self_0 }, Self::ByteStr {
                    terminated: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::CStr { terminated: __self_0 }, Self::CStr {
                    terminated: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::RawStr { n_hashes: __self_0 }, Self::RawStr {
                    n_hashes: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::RawByteStr { n_hashes: __self_0 }, Self::RawByteStr {
                    n_hashes: __arg1_0 }) => __self_0 == __arg1_0,
                (Self::RawCStr { n_hashes: __self_0 }, Self::RawCStr {
                    n_hashes: __arg1_0 }) => __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LiteralKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Base>;
        let _: ::core::cmp::AssertParamIsEq<bool>;
        let _: ::core::cmp::AssertParamIsEq<Option<u8>>;
        let _: ::core::cmp::AssertParamIsEq<Option<u8>>;
        let _: ::core::cmp::AssertParamIsEq<Option<u8>>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for LiteralKind {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for LiteralKind {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        match (self, other) {
            (Self::Int { base: __self_0, empty_int: __self_1 }, Self::Int {
                base: __arg1_0, empty_int: __arg1_1 }) =>
                match ::core::cmp::Ord::cmp(__self_0, __arg1_0) {
                    ::core::cmp::Ordering::Equal =>
                        ::core::cmp::Ord::cmp(__self_1, __arg1_1),
                    cmp => cmp,
                },
            (Self::Float { base: __self_0, empty_exponent: __self_1 },
                Self::Float { base: __arg1_0, empty_exponent: __arg1_1 }) =>
                match ::core::cmp::Ord::cmp(__self_0, __arg1_0) {
                    ::core::cmp::Ordering::Equal =>
                        ::core::cmp::Ord::cmp(__self_1, __arg1_1),
                    cmp => cmp,
                },
            (Self::Char { terminated: __self_0 }, Self::Char {
                terminated: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::Byte { terminated: __self_0 }, Self::Byte {
                terminated: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::Str { terminated: __self_0 }, Self::Str {
                terminated: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::ByteStr { terminated: __self_0 }, Self::ByteStr {
                terminated: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::CStr { terminated: __self_0 }, Self::CStr {
                terminated: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::RawStr { n_hashes: __self_0 }, Self::RawStr {
                n_hashes: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::RawByteStr { n_hashes: __self_0 }, Self::RawByteStr {
                n_hashes: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::RawCStr { n_hashes: __self_0 }, Self::RawCStr {
                n_hashes: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            _ =>
                ::core::cmp::Ord::cmp(&::core::intrinsics::discriminant_value(self),
                    &::core::intrinsics::discriminant_value(other)),
        }
    }
}Ord)]
224pub enum LiteralKind {
225    /// `12_u8`, `0o100`, `0b120i99`, `1f32`.
226    Int { base: Base, empty_int: bool },
227    /// `12.34f32`, `1e3`, but not `1f32`.
228    Float { base: Base, empty_exponent: bool },
229    /// `'a'`, `'\\'`, `'''`, `';`
230    Char { terminated: bool },
231    /// `b'a'`, `b'\\'`, `b'''`, `b';`
232    Byte { terminated: bool },
233    /// `"abc"`, `"abc`
234    Str { terminated: bool },
235    /// `b"abc"`, `b"abc`
236    ByteStr { terminated: bool },
237    /// `c"abc"`, `c"abc`
238    CStr { terminated: bool },
239    /// `r"abc"`, `r#"abc"#`, `r####"ab"###"c"####`, `r#"a`. `None` indicates
240    /// an invalid literal.
241    RawStr { n_hashes: Option<u8> },
242    /// `br"abc"`, `br#"abc"#`, `br####"ab"###"c"####`, `br#"a`. `None`
243    /// indicates an invalid literal.
244    RawByteStr { n_hashes: Option<u8> },
245    /// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal.
246    RawCStr { n_hashes: Option<u8> },
247}
248
249/// `#"abc"#`, `##"a"` (fewer closing), or even `#"a` (unterminated).
250///
251/// Can capture fewer closing hashes than starting hashes,
252/// for more efficient lexing and better backwards diagnostics.
253#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for GuardedStr { }
#[automatically_derived]
impl ::core::clone::Clone for GuardedStr {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<u32>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for GuardedStr { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for GuardedStr {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "GuardedStr",
            "n_hashes", &self.n_hashes, "terminated", &self.terminated,
            "token_len", &&self.token_len)
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for GuardedStr { }
#[automatically_derived]
impl ::core::cmp::PartialEq for GuardedStr {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.n_hashes == other.n_hashes && self.terminated == other.terminated
            && self.token_len == other.token_len
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for GuardedStr {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<u32>;
        let _: ::core::cmp::AssertParamIsEq<bool>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for GuardedStr {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for GuardedStr {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        match ::core::cmp::Ord::cmp(&self.n_hashes, &other.n_hashes) {
            ::core::cmp::Ordering::Equal =>
                match ::core::cmp::Ord::cmp(&self.terminated,
                        &other.terminated) {
                    ::core::cmp::Ordering::Equal =>
                        ::core::cmp::Ord::cmp(&self.token_len, &other.token_len),
                    cmp => cmp,
                },
            cmp => cmp,
        }
    }
}Ord)]
254pub struct GuardedStr {
255    pub n_hashes: u32,
256    pub terminated: bool,
257    pub token_len: u32,
258}
259
260#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for RawStrError { }
#[automatically_derived]
impl ::core::clone::Clone for RawStrError {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<char>;
        let _: ::core::clone::AssertParamIsClone<u32>;
        let _: ::core::clone::AssertParamIsClone<Option<u32>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for RawStrError { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for RawStrError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::InvalidStarter { bad_char: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "InvalidStarter", "bad_char", &__self_0),
            Self::NoTerminator {
                expected: __self_0,
                found: __self_1,
                possible_terminator_offset: __self_2 } =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f,
                    "NoTerminator", "expected", __self_0, "found", __self_1,
                    "possible_terminator_offset", &__self_2),
            Self::TooManyDelimiters { found: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "TooManyDelimiters", "found", &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for RawStrError { }
#[automatically_derived]
impl ::core::cmp::PartialEq for RawStrError {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::InvalidStarter { bad_char: __self_0 },
                    Self::InvalidStarter { bad_char: __arg1_0 }) =>
                    __self_0 == __arg1_0,
                (Self::NoTerminator {
                    expected: __self_0,
                    found: __self_1,
                    possible_terminator_offset: __self_2 }, Self::NoTerminator {
                    expected: __arg1_0,
                    found: __arg1_1,
                    possible_terminator_offset: __arg1_2 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                        __self_2 == __arg1_2,
                (Self::TooManyDelimiters { found: __self_0 },
                    Self::TooManyDelimiters { found: __arg1_0 }) =>
                    __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for RawStrError {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<char>;
        let _: ::core::cmp::AssertParamIsEq<u32>;
        let _: ::core::cmp::AssertParamIsEq<Option<u32>>;
    }
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for RawStrError {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for RawStrError {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        match (self, other) {
            (Self::InvalidStarter { bad_char: __self_0 },
                Self::InvalidStarter { bad_char: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            (Self::NoTerminator {
                expected: __self_0,
                found: __self_1,
                possible_terminator_offset: __self_2 }, Self::NoTerminator {
                expected: __arg1_0,
                found: __arg1_1,
                possible_terminator_offset: __arg1_2 }) =>
                match ::core::cmp::Ord::cmp(__self_0, __arg1_0) {
                    ::core::cmp::Ordering::Equal =>
                        match ::core::cmp::Ord::cmp(__self_1, __arg1_1) {
                            ::core::cmp::Ordering::Equal =>
                                ::core::cmp::Ord::cmp(__self_2, __arg1_2),
                            cmp => cmp,
                        },
                    cmp => cmp,
                },
            (Self::TooManyDelimiters { found: __self_0 },
                Self::TooManyDelimiters { found: __arg1_0 }) =>
                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
            _ =>
                ::core::cmp::Ord::cmp(&::core::intrinsics::discriminant_value(self),
                    &::core::intrinsics::discriminant_value(other)),
        }
    }
}Ord)]
261pub enum RawStrError {
262    /// Non `#` characters exist between `r` and `"`, e.g. `r##~"abcde"##`
263    InvalidStarter { bad_char: char },
264    /// The string was not terminated, e.g. `r###"abcde"##`.
265    /// `possible_terminator_offset` is the number of characters after `r` or
266    /// `br` where they may have intended to terminate it.
267    NoTerminator { expected: u32, found: u32, possible_terminator_offset: Option<u32> },
268    /// More than 255 `#`s exist.
269    TooManyDelimiters { found: u32 },
270}
271
272/// Base of numeric literal encoding according to its prefix.
273#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Base { }
#[automatically_derived]
impl ::core::clone::Clone for Base {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Base { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for Base {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                Base::Binary => "Binary",
                Base::Octal => "Octal",
                Base::Decimal => "Decimal",
                Base::Hexadecimal => "Hexadecimal",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for Base { }
#[automatically_derived]
impl ::core::cmp::PartialEq for Base {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Base { }Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for Base {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for Base {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&::core::intrinsics::discriminant_value(self),
            &::core::intrinsics::discriminant_value(other))
    }
}Ord)]
274pub enum Base {
275    /// Literal starts with "0b".
276    Binary = 2,
277    /// Literal starts with "0o".
278    Octal = 8,
279    /// Literal doesn't contain a prefix.
280    Decimal = 10,
281    /// Literal starts with "0x".
282    Hexadecimal = 16,
283}
284
285/// `rustc` allows files to have a shebang, e.g. "#!/usr/bin/rustrun",
286/// but shebang isn't a part of rust syntax.
287pub fn strip_shebang(input: &str) -> Option<usize> {
288    // Shebang must start with `#!` literally, without any preceding whitespace.
289    // For simplicity we consider any line starting with `#!` a shebang,
290    // regardless of restrictions put on shebangs by specific platforms.
291    if let Some(input_tail) = input.strip_prefix("#!") {
292        // Ok, this is a shebang but if the next non-whitespace token is `[`,
293        // then it may be valid Rust code, so consider it Rust code.
294        let next_non_whitespace_token =
295            tokenize(input_tail, FrontmatterAllowed::No).map(|tok| tok.kind).find(|tok| {
296                !#[allow(non_exhaustive_omitted_patterns)] match tok {
    TokenKind::Whitespace | TokenKind::LineComment { doc_style: None } |
        TokenKind::BlockComment { doc_style: None, .. } => true,
    _ => false,
}matches!(
297                    tok,
298                    TokenKind::Whitespace
299                        | TokenKind::LineComment { doc_style: None }
300                        | TokenKind::BlockComment { doc_style: None, .. }
301                )
302            });
303        if next_non_whitespace_token != Some(TokenKind::OpenBracket) {
304            // No other choice than to consider this a shebang.
305            return Some(2 + input_tail.lines().next().unwrap_or_default().len());
306        }
307    }
308    None
309}
310
311/// Validates a raw string literal. Used for getting more information about a
312/// problem with a `RawStr`/`RawByteStr` with a `None` field.
313#[inline]
314pub fn validate_raw_str(input: &str, prefix_len: u32) -> Result<(), RawStrError> {
315    if true {
    if !!input.is_empty() {
        ::core::panicking::panic("assertion failed: !input.is_empty()")
    };
};debug_assert!(!input.is_empty());
316    let mut cursor = Cursor::new(input, FrontmatterAllowed::No);
317    // Move past the leading `r` or `br`.
318    for _ in 0..prefix_len {
319        cursor.bump().unwrap();
320    }
321    cursor.raw_double_quoted_string(prefix_len).map(|_| ())
322}
323
324/// Creates an iterator that produces tokens from the input string.
325///
326/// When parsing a full Rust document,
327/// first [`strip_shebang`] and then allow frontmatters with [`FrontmatterAllowed::Yes`].
328///
329/// When tokenizing a slice of a document, be sure to disallow frontmatters with [`FrontmatterAllowed::No`]
330pub fn tokenize(
331    input: &str,
332    frontmatter_allowed: FrontmatterAllowed,
333) -> impl Iterator<Item = Token> {
334    let mut cursor = Cursor::new(input, frontmatter_allowed);
335    std::iter::from_fn(move || {
336        let token = cursor.advance_token();
337        if token.kind != TokenKind::Eof { Some(token) } else { None }
338    })
339}
340
341/// True if `c` is considered a whitespace according to Rust language definition.
342/// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html)
343/// for definitions of these classes.
344pub fn is_whitespace(c: char) -> bool {
345    // This is Pattern_White_Space.
346    //
347    // Note that this set is stable (ie, it doesn't change with different
348    // Unicode versions), so it's ok to just hard-code the values.
349
350    #[allow(non_exhaustive_omitted_patterns)] match c {
    '\u{000A}' | '\u{000B}' | '\u{000C}' | '\u{000D}' | '\u{0085}' |
        '\u{2028}' | '\u{2029}' | '\u{200E}' | '\u{200F}' | '\u{0009}' |
        '\u{0020}' => true,
    _ => false,
}matches!(
351        c,
352        // End-of-line characters
353        | '\u{000A}' // line feed (\n)
354        | '\u{000B}' // vertical tab
355        | '\u{000C}' // form feed
356        | '\u{000D}' // carriage return (\r)
357        | '\u{0085}' // next line (from latin1)
358        | '\u{2028}' // LINE SEPARATOR
359        | '\u{2029}' // PARAGRAPH SEPARATOR
360
361        // `Default_Ignorable_Code_Point` characters
362        | '\u{200E}' // LEFT-TO-RIGHT MARK
363        | '\u{200F}' // RIGHT-TO-LEFT MARK
364
365        // Horizontal space characters
366        | '\u{0009}'   // tab (\t)
367        | '\u{0020}' // space
368    )
369}
370
371/// True if `c` is considered horizontal whitespace according to Rust language definition.
372pub fn is_horizontal_whitespace(c: char) -> bool {
373    // This is the horizontal space subset of `Pattern_White_Space` as
374    // categorized by UAX #31, Section 4.1.
375    //
376    // Note that this set is stable (ie, it doesn't change with different
377    // Unicode versions), so it's ok to just hard-code the values.
378
379    #[allow(non_exhaustive_omitted_patterns)] match c {
    '\u{0009}' | '\u{0020}' => true,
    _ => false,
}matches!(
380        c,
381        // Horizontal space characters
382        '\u{0009}'   // tab (\t)
383        | '\u{0020}' // space
384    )
385}
386
387/// True if `c` is valid as a first character of an identifier.
388/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
389/// a formal definition of valid identifier name.
390pub fn is_id_start(c: char) -> bool {
391    // This is XID_Start OR '_' (which formally is not a XID_Start).
392    c == '_' || unicode_ident::is_xid_start(c)
393}
394
395/// True if `c` is valid as a non-first character of an identifier.
396/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for
397/// a formal definition of valid identifier name.
398pub fn is_id_continue(c: char) -> bool {
399    unicode_ident::is_xid_continue(c)
400}
401
402/// The passed string is lexically an identifier.
403pub fn is_ident(string: &str) -> bool {
404    let mut chars = string.chars();
405    if let Some(start) = chars.next() {
406        is_id_start(start) && chars.all(is_id_continue)
407    } else {
408        false
409    }
410}
411
412pub enum FrontmatterAllowed {
413    Yes,
414    No,
415}
416
417/// Peekable iterator over a char sequence.
418///
419/// Next characters can be peeked via `first` method,
420/// and position can be shifted forward via `bump` method.
421pub struct Cursor<'a> {
422    len_remaining: usize,
423    /// Iterator over chars. Slightly faster than a &str.
424    chars: Chars<'a>,
425    pub(crate) frontmatter_allowed: FrontmatterAllowed,
426    #[cfg(debug_assertions)]
427    prev: char,
428}
429
430const EOF_CHAR: char = '\0';
431
432impl<'a> Cursor<'a> {
433    pub fn new(input: &'a str, frontmatter_allowed: FrontmatterAllowed) -> Cursor<'a> {
434        Cursor {
435            len_remaining: input.len(),
436            chars: input.chars(),
437            frontmatter_allowed,
438            #[cfg(debug_assertions)]
439            prev: EOF_CHAR,
440        }
441    }
442
443    pub fn as_str(&self) -> &'a str {
444        self.chars.as_str()
445    }
446
447    /// Returns the last eaten symbol (or `'\0'` in release builds).
448    /// (For debug assertions only.)
449    pub(crate) fn prev(&self) -> char {
450        #[cfg(debug_assertions)]
451        {
452            self.prev
453        }
454
455        #[cfg(not(debug_assertions))]
456        {
457            EOF_CHAR
458        }
459    }
460
461    /// Peeks the next symbol from the input stream without consuming it.
462    /// If requested position doesn't exist, `EOF_CHAR` is returned.
463    /// However, getting `EOF_CHAR` doesn't always mean actual end of file,
464    /// it should be checked with `is_eof` method.
465    pub fn first(&self) -> char {
466        // `.next()` optimizes better than `.nth(0)`
467        self.chars.clone().next().unwrap_or(EOF_CHAR)
468    }
469
470    /// Peeks the second symbol from the input stream without consuming it.
471    pub(crate) fn second(&self) -> char {
472        // `.next()` optimizes better than `.nth(1)`
473        let mut iter = self.chars.clone();
474        iter.next();
475        iter.next().unwrap_or(EOF_CHAR)
476    }
477
478    /// Peeks the third symbol from the input stream without consuming it.
479    pub fn third(&self) -> char {
480        // `.next()` optimizes better than `.nth(2)`
481        let mut iter = self.chars.clone();
482        iter.next();
483        iter.next();
484        iter.next().unwrap_or(EOF_CHAR)
485    }
486
487    /// Checks if there is nothing more to consume.
488    pub(crate) fn is_eof(&self) -> bool {
489        self.chars.as_str().is_empty()
490    }
491
492    /// Returns amount of already consumed symbols.
493    pub(crate) fn pos_within_token(&self) -> u32 {
494        (self.len_remaining - self.chars.as_str().len()) as u32
495    }
496
497    /// Resets the number of bytes consumed to 0.
498    pub(crate) fn reset_pos_within_token(&mut self) {
499        self.len_remaining = self.chars.as_str().len();
500    }
501
502    /// Moves to the next character.
503    pub(crate) fn bump(&mut self) -> Option<char> {
504        let c = self.chars.next()?;
505
506        #[cfg(debug_assertions)]
507        {
508            self.prev = c;
509        }
510
511        Some(c)
512    }
513
514    /// Moves to a substring by a number of bytes.
515    pub(crate) fn bump_bytes(&mut self, n: usize) {
516        self.chars = self.as_str()[n..].chars();
517    }
518
519    /// Eats symbols while predicate returns true or until the end of file is reached.
520    pub(crate) fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) {
521        // It was tried making optimized version of this for eg. line comments, but
522        // LLVM can inline all of this and compile it down to fast iteration over bytes.
523        while predicate(self.first()) && !self.is_eof() {
524            self.bump();
525        }
526    }
527
528    pub(crate) fn eat_until(&mut self, byte: u8) {
529        self.chars = match memchr::memchr(byte, self.as_str().as_bytes()) {
530            Some(index) => self.as_str()[index..].chars(),
531            None => "".chars(),
532        }
533    }
534
535    /// Parses a token from the input string.
536    pub fn advance_token(&mut self) -> Token {
537        let Some(first_char) = self.bump() else {
538            return Token::new(TokenKind::Eof, 0);
539        };
540
541        let token_kind = match first_char {
542            c if #[allow(non_exhaustive_omitted_patterns)] match self.frontmatter_allowed {
    FrontmatterAllowed::Yes => true,
    _ => false,
}matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes)
543                && is_whitespace(c) =>
544            {
545                let mut last = first_char;
546                while is_whitespace(self.first()) {
547                    let Some(c) = self.bump() else {
548                        break;
549                    };
550                    last = c;
551                }
552                // invalid frontmatter opening as whitespace preceding it isn't newline.
553                // combine the whitespace and the frontmatter to a single token as we shall
554                // error later.
555                if last != '\n' && self.as_str().starts_with("---") {
556                    self.bump();
557                    self.frontmatter(true)
558                } else {
559                    Whitespace
560                }
561            }
562            '-' if #[allow(non_exhaustive_omitted_patterns)] match self.frontmatter_allowed {
    FrontmatterAllowed::Yes => true,
    _ => false,
}matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes)
563                && self.as_str().starts_with("--") =>
564            {
565                // happy path
566                self.frontmatter(false)
567            }
568            // Slash, comment or block comment.
569            '/' => match self.first() {
570                '/' => self.line_comment(),
571                '*' => self.block_comment(),
572                _ => Slash,
573            },
574
575            // Whitespace sequence.
576            c if is_whitespace(c) => self.whitespace(),
577
578            // Raw identifier, raw string literal or identifier.
579            'r' => match (self.first(), self.second()) {
580                ('#', c1) if is_id_start(c1) => {
581                    self.bump(); // `#`
582                    self.eat_identifier();
583                    RawIdent
584                }
585                ('#', _) | ('"', _) => {
586                    let res = self.raw_double_quoted_string(1);
587                    let suffix_start = self.pos_within_token();
588                    if res.is_ok() {
589                        self.eat_literal_suffix();
590                    }
591                    let kind = RawStr { n_hashes: res.ok() };
592                    Literal { kind, suffix_start }
593                }
594                _ => self.ident_or_unknown_prefix(),
595            },
596
597            // Forced keyword identifier or identifier.
598            'k' => match (self.first(), self.second()) {
599                ('#', c1) if is_id_start(c1) => {
600                    self.bump(); // `#`
601                    self.eat_identifier();
602                    ForcedKeywordIdent
603                }
604                _ => self.ident_or_unknown_prefix(),
605            },
606
607            // Byte literal, byte string literal, raw byte string literal or identifier.
608            'b' => self.c_or_byte_string(
609                |terminated| ByteStr { terminated },
610                |n_hashes| RawByteStr { n_hashes },
611                Some(|terminated| Byte { terminated }),
612            ),
613
614            // c-string literal, raw c-string literal or identifier.
615            'c' => self.c_or_byte_string(
616                |terminated| CStr { terminated },
617                |n_hashes| RawCStr { n_hashes },
618                None,
619            ),
620
621            // Identifier (this should be checked after other variant that can
622            // start as identifier).
623            c if is_id_start(c) => self.ident_or_unknown_prefix(),
624
625            // Numeric literal.
626            c @ '0'..='9' => {
627                let literal_kind = self.number(c);
628                let suffix_start = self.pos_within_token();
629                self.eat_literal_suffix();
630                TokenKind::Literal { kind: literal_kind, suffix_start }
631            }
632
633            // Guarded string literal prefix: `#"` or `##`
634            '#' if #[allow(non_exhaustive_omitted_patterns)] match self.first() {
    '"' | '#' => true,
    _ => false,
}matches!(self.first(), '"' | '#') => {
635                self.bump();
636                TokenKind::GuardedStrPrefix
637            }
638
639            // One-symbol tokens.
640            ';' => Semi,
641            ',' => Comma,
642            '.' => Dot,
643            '(' => OpenParen,
644            ')' => CloseParen,
645            '{' => OpenBrace,
646            '}' => CloseBrace,
647            '[' => OpenBracket,
648            ']' => CloseBracket,
649            '@' => At,
650            '#' => Pound,
651            '~' => Tilde,
652            '?' => Question,
653            ':' => Colon,
654            '$' => Dollar,
655            '=' => Eq,
656            '!' => Bang,
657            '<' => Lt,
658            '>' => Gt,
659            '-' => Minus,
660            '&' => And,
661            '|' => Or,
662            '+' => Plus,
663            '*' => Star,
664            '^' => Caret,
665            '%' => Percent,
666
667            // Lifetime or character literal.
668            '\'' => self.lifetime_or_char(),
669
670            // String literal.
671            '"' => {
672                let terminated = self.double_quoted_string();
673                let suffix_start = self.pos_within_token();
674                if terminated {
675                    self.eat_literal_suffix();
676                }
677                let kind = Str { terminated };
678                Literal { kind, suffix_start }
679            }
680            // Identifier starting with an emoji. Only lexed for graceful error recovery.
681            c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(),
682            _ => Unknown,
683        };
684        if #[allow(non_exhaustive_omitted_patterns)] match self.frontmatter_allowed {
    FrontmatterAllowed::Yes => true,
    _ => false,
}matches!(self.frontmatter_allowed, FrontmatterAllowed::Yes)
685            && !#[allow(non_exhaustive_omitted_patterns)] match token_kind {
    Whitespace => true,
    _ => false,
}matches!(token_kind, Whitespace)
686        {
687            // stop allowing frontmatters after first non-whitespace token
688            self.frontmatter_allowed = FrontmatterAllowed::No;
689        }
690        let res = Token::new(token_kind, self.pos_within_token());
691        self.reset_pos_within_token();
692        res
693    }
694
695    /// Given that one `-` was eaten, eat the rest of the frontmatter.
696    fn frontmatter(&mut self, has_invalid_preceding_whitespace: bool) -> TokenKind {
697        if true {
    {
        match (&'-', &self.prev()) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!('-', self.prev());
698
699        let pos = self.pos_within_token();
700        self.eat_while(|c| c == '-');
701
702        // one `-` is eaten by the caller.
703        let length_opening = self.pos_within_token() - pos + 1;
704
705        // must be ensured by the caller
706        if true {
    if !(length_opening >= 3) {
        ::core::panicking::panic("assertion failed: length_opening >= 3")
    };
};debug_assert!(length_opening >= 3);
707
708        // whitespace between the opening and the infostring.
709        self.eat_while(|ch| ch != '\n' && is_horizontal_whitespace(ch));
710
711        // copied from `eat_identifier`, but allows `-` and `.` in infostring to allow something like
712        // `---Cargo.toml` as a valid opener
713        if is_id_start(self.first()) {
714            self.bump();
715            self.eat_while(|c| is_id_continue(c) || c == '-' || c == '.');
716        }
717
718        self.eat_while(|ch| ch != '\n' && is_horizontal_whitespace(ch));
719        let invalid_infostring = self.first() != '\n';
720
721        let mut found = false;
722        let nl_fence_pattern = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n{0:-<1$}", "",
                length_opening as usize))
    })format!("\n{:-<1$}", "", length_opening as usize);
723        if let Some(closing) = self.as_str().find(&nl_fence_pattern) {
724            // candidate found
725            self.bump_bytes(closing + nl_fence_pattern.len());
726            // in case like
727            // ---cargo
728            // --- blahblah
729            // or
730            // ---cargo
731            // ----
732            // combine those stuff into this frontmatter token such that it gets detected later.
733            self.eat_until(b'\n');
734            found = true;
735        }
736
737        if !found {
738            // recovery strategy: a closing statement might have preceding whitespace/newline
739            // but not have enough dashes to properly close. In this case, we eat until there,
740            // and report a mismatch in the parser.
741            let mut rest = self.as_str();
742            // We can look for a shorter closing (starting with four dashes but closing with three)
743            // and other indications that Rust has started and the infostring has ended.
744            let mut potential_closing = rest
745                .find("\n---")
746                // n.b. only in the case where there are dashes, we move the index to the line where
747                // the dashes start as we eat to include that line. For other cases those are Rust code
748                // and not included in the frontmatter.
749                .map(|x| x + 1)
750                .or_else(|| rest.find("\nuse "))
751                .or_else(|| rest.find("\n//!"))
752                .or_else(|| rest.find("\n#!["));
753
754            if potential_closing.is_none() {
755                // a less fortunate recovery if all else fails which finds any dashes preceded by whitespace
756                // on a standalone line. Might be wrong.
757                let mut base_index = 0;
758                while let Some(closing) = rest.find("---") {
759                    let preceding_chars_start = rest[..closing].rfind("\n").map_or(0, |i| i + 1);
760                    if rest[preceding_chars_start..closing].chars().all(is_horizontal_whitespace) {
761                        // candidate found
762                        potential_closing = Some(closing + base_index);
763                        break;
764                    } else {
765                        rest = &rest[closing + 3..];
766                        base_index += closing + 3;
767                    }
768                }
769            }
770
771            if let Some(potential_closing) = potential_closing {
772                // bump to the potential closing, and eat everything on that line.
773                self.bump_bytes(potential_closing);
774                self.eat_until(b'\n');
775            } else {
776                // eat everything. this will get reported as an unclosed frontmatter.
777                self.eat_while(|_| true);
778            }
779        }
780
781        Frontmatter { has_invalid_preceding_whitespace, invalid_infostring }
782    }
783
784    fn line_comment(&mut self) -> TokenKind {
785        if true {
    if !(self.prev() == '/' && self.first() == '/') {
        ::core::panicking::panic("assertion failed: self.prev() == \'/\' && self.first() == \'/\'")
    };
};debug_assert!(self.prev() == '/' && self.first() == '/');
786        self.bump();
787
788        let doc_style = match self.first() {
789            // `//!` is an inner line doc comment.
790            '!' => Some(DocStyle::Inner),
791            // `////` (more than 3 slashes) is not considered a doc comment.
792            '/' if self.second() != '/' => Some(DocStyle::Outer),
793            _ => None,
794        };
795
796        self.eat_until(b'\n');
797        LineComment { doc_style }
798    }
799
800    fn block_comment(&mut self) -> TokenKind {
801        if true {
    if !(self.prev() == '/' && self.first() == '*') {
        ::core::panicking::panic("assertion failed: self.prev() == \'/\' && self.first() == \'*\'")
    };
};debug_assert!(self.prev() == '/' && self.first() == '*');
802        self.bump();
803
804        let doc_style = match self.first() {
805            // `/*!` is an inner block doc comment.
806            '!' => Some(DocStyle::Inner),
807            // `/***` (more than 2 stars) is not considered a doc comment.
808            // `/**/` is not considered a doc comment.
809            '*' if !#[allow(non_exhaustive_omitted_patterns)] match self.second() {
    '*' | '/' => true,
    _ => false,
}matches!(self.second(), '*' | '/') => Some(DocStyle::Outer),
810            _ => None,
811        };
812
813        let mut depth = 1usize;
814        while let Some(c) = self.bump() {
815            match c {
816                '/' if self.first() == '*' => {
817                    self.bump();
818                    depth += 1;
819                }
820                '*' if self.first() == '/' => {
821                    self.bump();
822                    depth -= 1;
823                    if depth == 0 {
824                        // This block comment is closed, so for a construction like "/* */ */"
825                        // there will be a successfully parsed block comment "/* */"
826                        // and " */" will be processed separately.
827                        break;
828                    }
829                }
830                _ => (),
831            }
832        }
833
834        BlockComment { doc_style, terminated: depth == 0 }
835    }
836
837    fn whitespace(&mut self) -> TokenKind {
838        if true {
    if !is_whitespace(self.prev()) {
        ::core::panicking::panic("assertion failed: is_whitespace(self.prev())")
    };
};debug_assert!(is_whitespace(self.prev()));
839        self.eat_while(is_whitespace);
840        Whitespace
841    }
842
843    fn ident_or_unknown_prefix(&mut self) -> TokenKind {
844        if true {
    if !is_id_start(self.prev()) {
        ::core::panicking::panic("assertion failed: is_id_start(self.prev())")
    };
};debug_assert!(is_id_start(self.prev()));
845        // Start is already eaten, eat the rest of identifier.
846        self.eat_while(is_id_continue);
847        // Known prefixes must have been handled earlier. So if
848        // we see a prefix here, it is definitely an unknown prefix.
849        match self.first() {
850            '#' | '"' | '\'' => UnknownPrefix,
851            c if !c.is_ascii() && c.is_emoji_char() => self.invalid_ident(),
852            _ => Ident,
853        }
854    }
855
856    fn invalid_ident(&mut self) -> TokenKind {
857        // Start is already eaten, eat the rest of identifier.
858        self.eat_while(|c| {
859            const ZERO_WIDTH_JOINER: char = '\u{200d}';
860            is_id_continue(c) || (!c.is_ascii() && c.is_emoji_char()) || c == ZERO_WIDTH_JOINER
861        });
862        // An invalid identifier followed by '#' or '"' or '\'' could be
863        // interpreted as an invalid literal prefix. We don't bother doing that
864        // because the treatment of invalid identifiers and invalid prefixes
865        // would be the same.
866        InvalidIdent
867    }
868
869    fn c_or_byte_string(
870        &mut self,
871        mk_kind: fn(bool) -> LiteralKind,
872        mk_kind_raw: fn(Option<u8>) -> LiteralKind,
873        single_quoted: Option<fn(bool) -> LiteralKind>,
874    ) -> TokenKind {
875        match (self.first(), self.second(), single_quoted) {
876            ('\'', _, Some(single_quoted)) => {
877                self.bump();
878                let terminated = self.single_quoted_string();
879                let suffix_start = self.pos_within_token();
880                if terminated {
881                    self.eat_literal_suffix();
882                }
883                let kind = single_quoted(terminated);
884                Literal { kind, suffix_start }
885            }
886            ('"', _, _) => {
887                self.bump();
888                let terminated = self.double_quoted_string();
889                let suffix_start = self.pos_within_token();
890                if terminated {
891                    self.eat_literal_suffix();
892                }
893                let kind = mk_kind(terminated);
894                Literal { kind, suffix_start }
895            }
896            ('r', '"', _) | ('r', '#', _) => {
897                self.bump();
898                let res = self.raw_double_quoted_string(2);
899                let suffix_start = self.pos_within_token();
900                if res.is_ok() {
901                    self.eat_literal_suffix();
902                }
903                let kind = mk_kind_raw(res.ok());
904                Literal { kind, suffix_start }
905            }
906            _ => self.ident_or_unknown_prefix(),
907        }
908    }
909
910    fn number(&mut self, first_digit: char) -> LiteralKind {
911        if true {
    if !('0' <= self.prev() && self.prev() <= '9') {
        ::core::panicking::panic("assertion failed: \'0\' <= self.prev() && self.prev() <= \'9\'")
    };
};debug_assert!('0' <= self.prev() && self.prev() <= '9');
912        let mut base = Base::Decimal;
913        if first_digit == '0' {
914            // Attempt to parse encoding base.
915            match self.first() {
916                'b' => {
917                    base = Base::Binary;
918                    self.bump();
919                    if !self.eat_decimal_digits() {
920                        return Int { base, empty_int: true };
921                    }
922                }
923                'o' => {
924                    base = Base::Octal;
925                    self.bump();
926                    if !self.eat_decimal_digits() {
927                        return Int { base, empty_int: true };
928                    }
929                }
930                'x' => {
931                    base = Base::Hexadecimal;
932                    self.bump();
933                    if !self.eat_hexadecimal_digits() {
934                        return Int { base, empty_int: true };
935                    }
936                }
937                // Not a base prefix; consume additional digits.
938                '0'..='9' | '_' => {
939                    self.eat_decimal_digits();
940                }
941
942                // Also not a base prefix; nothing more to do here.
943                '.' | 'e' | 'E' => {}
944
945                // Just a 0.
946                _ => return Int { base, empty_int: false },
947            }
948        } else {
949            // No base prefix, parse number in the usual way.
950            self.eat_decimal_digits();
951        }
952
953        match self.first() {
954            // Don't be greedy if this is actually an
955            // integer literal followed by field/method access or a range pattern
956            // (`0..2` and `12.foo()`)
957            '.' if self.second() != '.' && !is_id_start(self.second()) => {
958                // might have stuff after the ., and if it does, it needs to start
959                // with a number
960                self.bump();
961                let mut empty_exponent = false;
962                if self.first().is_ascii_digit() {
963                    self.eat_decimal_digits();
964                    match self.first() {
965                        'e' | 'E' => {
966                            self.bump();
967                            empty_exponent = !self.eat_float_exponent();
968                        }
969                        _ => (),
970                    }
971                }
972                Float { base, empty_exponent }
973            }
974            'e' | 'E' => {
975                self.bump();
976                let empty_exponent = !self.eat_float_exponent();
977                Float { base, empty_exponent }
978            }
979            _ => Int { base, empty_int: false },
980        }
981    }
982
983    fn lifetime_or_char(&mut self) -> TokenKind {
984        if true {
    if !(self.prev() == '\'') {
        ::core::panicking::panic("assertion failed: self.prev() == \'\\\'\'")
    };
};debug_assert!(self.prev() == '\'');
985
986        let can_be_a_lifetime = if self.second() == '\'' {
987            // It's surely not a lifetime.
988            false
989        } else {
990            // If the first symbol is valid for identifier, it can be a lifetime.
991            // Also check if it's a number for a better error reporting (so '0 will
992            // be reported as invalid lifetime and not as unterminated char literal).
993            is_id_start(self.first()) || self.first().is_ascii_digit()
994        };
995
996        if !can_be_a_lifetime {
997            let terminated = self.single_quoted_string();
998            let suffix_start = self.pos_within_token();
999            if terminated {
1000                self.eat_literal_suffix();
1001            }
1002            let kind = Char { terminated };
1003            return Literal { kind, suffix_start };
1004        }
1005
1006        if self.first() == 'r' && self.second() == '#' && is_id_start(self.third()) {
1007            // Eat "r" and `#`, and identifier start characters.
1008            self.bump();
1009            self.bump();
1010            self.bump();
1011            self.eat_while(is_id_continue);
1012            return RawLifetime;
1013        }
1014
1015        // Either a lifetime or a character literal with
1016        // length greater than 1.
1017        let starts_with_number = self.first().is_ascii_digit();
1018
1019        // Skip the literal contents.
1020        // First symbol can be a number (which isn't a valid identifier start),
1021        // so skip it without any checks.
1022        self.bump();
1023        self.eat_while(is_id_continue);
1024
1025        match self.first() {
1026            // Check if after skipping literal contents we've met a closing
1027            // single quote (which means that user attempted to create a
1028            // string with single quotes).
1029            '\'' => {
1030                self.bump();
1031                let kind = Char { terminated: true };
1032                Literal { kind, suffix_start: self.pos_within_token() }
1033            }
1034            '#' if !starts_with_number => UnknownPrefixLifetime,
1035            _ => Lifetime { starts_with_number },
1036        }
1037    }
1038
1039    fn single_quoted_string(&mut self) -> bool {
1040        if true {
    if !(self.prev() == '\'') {
        ::core::panicking::panic("assertion failed: self.prev() == \'\\\'\'")
    };
};debug_assert!(self.prev() == '\'');
1041        // Check if it's a one-symbol literal.
1042        if self.second() == '\'' && self.first() != '\\' {
1043            self.bump();
1044            self.bump();
1045            return true;
1046        }
1047
1048        // Literal has more than one symbol.
1049
1050        // Parse until either quotes are terminated or error is detected.
1051        loop {
1052            match self.first() {
1053                // Quotes are terminated, finish parsing.
1054                '\'' => {
1055                    self.bump();
1056                    return true;
1057                }
1058                // Probably beginning of the comment, which we don't want to include
1059                // to the error report.
1060                '/' => break,
1061                // Newline without following '\'' means unclosed quote, stop parsing.
1062                '\n' if self.second() != '\'' => break,
1063                // End of file, stop parsing.
1064                EOF_CHAR if self.is_eof() => break,
1065                // Escaped slash is considered one character, so bump twice.
1066                '\\' => {
1067                    self.bump();
1068                    self.bump();
1069                }
1070                // Skip the character.
1071                _ => {
1072                    self.bump();
1073                }
1074            }
1075        }
1076        // String was not terminated.
1077        false
1078    }
1079
1080    /// Eats double-quoted string and returns true
1081    /// if string is terminated.
1082    fn double_quoted_string(&mut self) -> bool {
1083        if true {
    if !(self.prev() == '"') {
        ::core::panicking::panic("assertion failed: self.prev() == \'\"\'")
    };
};debug_assert!(self.prev() == '"');
1084        while let Some(c) = self.bump() {
1085            match c {
1086                '"' => {
1087                    return true;
1088                }
1089                '\\' if self.first() == '\\' || self.first() == '"' => {
1090                    // Bump again to skip escaped character.
1091                    self.bump();
1092                }
1093                _ => (),
1094            }
1095        }
1096        // End of file reached.
1097        false
1098    }
1099
1100    /// Attempt to lex for a guarded string literal.
1101    ///
1102    /// Used by `rustc_parse::lexer` to lex for guarded strings
1103    /// conditionally based on edition.
1104    ///
1105    /// Note: this will not reset the `Cursor` when a
1106    /// guarded string is not found. It is the caller's
1107    /// responsibility to do so.
1108    pub fn guarded_double_quoted_string(&mut self) -> Option<GuardedStr> {
1109        if true {
    if !(self.prev() != '#') {
        ::core::panicking::panic("assertion failed: self.prev() != \'#\'")
    };
};debug_assert!(self.prev() != '#');
1110
1111        let mut n_start_hashes: u32 = 0;
1112        while self.first() == '#' {
1113            n_start_hashes += 1;
1114            self.bump();
1115        }
1116
1117        if self.first() != '"' {
1118            return None;
1119        }
1120        self.bump();
1121        if true {
    if !(self.prev() == '"') {
        ::core::panicking::panic("assertion failed: self.prev() == \'\"\'")
    };
};debug_assert!(self.prev() == '"');
1122
1123        // Lex the string itself as a normal string literal
1124        // so we can recover that for older editions later.
1125        let terminated = self.double_quoted_string();
1126        if !terminated {
1127            let token_len = self.pos_within_token();
1128            self.reset_pos_within_token();
1129
1130            return Some(GuardedStr { n_hashes: n_start_hashes, terminated: false, token_len });
1131        }
1132
1133        // Consume closing '#' symbols.
1134        // Note that this will not consume extra trailing `#` characters:
1135        // `###"abcde"####` is lexed as a `GuardedStr { n_end_hashes: 3, .. }`
1136        // followed by a `#` token.
1137        let mut n_end_hashes = 0;
1138        while self.first() == '#' && n_end_hashes < n_start_hashes {
1139            n_end_hashes += 1;
1140            self.bump();
1141        }
1142
1143        // Reserved syntax, always an error, so it doesn't matter if
1144        // `n_start_hashes != n_end_hashes`.
1145
1146        self.eat_literal_suffix();
1147
1148        let token_len = self.pos_within_token();
1149        self.reset_pos_within_token();
1150
1151        Some(GuardedStr { n_hashes: n_start_hashes, terminated: true, token_len })
1152    }
1153
1154    /// Eats the double-quoted string and returns `n_hashes` and an error if encountered.
1155    fn raw_double_quoted_string(&mut self, prefix_len: u32) -> Result<u8, RawStrError> {
1156        // Wrap the actual function to handle the error with too many hashes.
1157        // This way, it eats the whole raw string.
1158        let n_hashes = self.raw_string_unvalidated(prefix_len)?;
1159        // Only up to 255 `#`s are allowed in raw strings
1160        match u8::try_from(n_hashes) {
1161            Ok(num) => Ok(num),
1162            Err(_) => Err(RawStrError::TooManyDelimiters { found: n_hashes }),
1163        }
1164    }
1165
1166    fn raw_string_unvalidated(&mut self, prefix_len: u32) -> Result<u32, RawStrError> {
1167        if true {
    if !(self.prev() == 'r') {
        ::core::panicking::panic("assertion failed: self.prev() == \'r\'")
    };
};debug_assert!(self.prev() == 'r');
1168        let start_pos = self.pos_within_token();
1169        let mut possible_terminator_offset = None;
1170        let mut max_hashes = 0;
1171
1172        // Count opening '#' symbols.
1173        let mut eaten = 0;
1174        while self.first() == '#' {
1175            eaten += 1;
1176            self.bump();
1177        }
1178        let n_start_hashes = eaten;
1179
1180        // Check that string is started.
1181        match self.bump() {
1182            Some('"') => (),
1183            c => {
1184                let c = c.unwrap_or(EOF_CHAR);
1185                return Err(RawStrError::InvalidStarter { bad_char: c });
1186            }
1187        }
1188
1189        // Skip the string contents and on each '#' character met, check if this is
1190        // a raw string termination.
1191        loop {
1192            self.eat_until(b'"');
1193
1194            if self.is_eof() {
1195                return Err(RawStrError::NoTerminator {
1196                    expected: n_start_hashes,
1197                    found: max_hashes,
1198                    possible_terminator_offset,
1199                });
1200            }
1201
1202            // Eat closing double quote.
1203            self.bump();
1204
1205            // Check that amount of closing '#' symbols
1206            // is equal to the amount of opening ones.
1207            // Note that this will not consume extra trailing `#` characters:
1208            // `r###"abcde"####` is lexed as a `RawStr { n_hashes: 3 }`
1209            // followed by a `#` token.
1210            let mut n_end_hashes = 0;
1211            while self.first() == '#' && n_end_hashes < n_start_hashes {
1212                n_end_hashes += 1;
1213                self.bump();
1214            }
1215
1216            if n_end_hashes == n_start_hashes {
1217                return Ok(n_start_hashes);
1218            } else if n_end_hashes > max_hashes {
1219                // Keep track of possible terminators to give a hint about
1220                // where there might be a missing terminator
1221                possible_terminator_offset =
1222                    Some(self.pos_within_token() - start_pos - n_end_hashes + prefix_len);
1223                max_hashes = n_end_hashes;
1224            }
1225        }
1226    }
1227
1228    fn eat_decimal_digits(&mut self) -> bool {
1229        let mut has_digits = false;
1230        loop {
1231            match self.first() {
1232                '_' => {
1233                    self.bump();
1234                }
1235                '0'..='9' => {
1236                    has_digits = true;
1237                    self.bump();
1238                }
1239                _ => break,
1240            }
1241        }
1242        has_digits
1243    }
1244
1245    fn eat_hexadecimal_digits(&mut self) -> bool {
1246        let mut has_digits = false;
1247        loop {
1248            match self.first() {
1249                '_' => {
1250                    self.bump();
1251                }
1252                '0'..='9' | 'a'..='f' | 'A'..='F' => {
1253                    has_digits = true;
1254                    self.bump();
1255                }
1256                _ => break,
1257            }
1258        }
1259        has_digits
1260    }
1261
1262    /// Eats the float exponent. Returns true if at least one digit was met,
1263    /// and returns false otherwise.
1264    fn eat_float_exponent(&mut self) -> bool {
1265        if true {
    if !(self.prev() == 'e' || self.prev() == 'E') {
        ::core::panicking::panic("assertion failed: self.prev() == \'e\' || self.prev() == \'E\'")
    };
};debug_assert!(self.prev() == 'e' || self.prev() == 'E');
1266        if self.first() == '-' || self.first() == '+' {
1267            self.bump();
1268        }
1269        self.eat_decimal_digits()
1270    }
1271
1272    // Eats the suffix of the literal, e.g. "u8".
1273    fn eat_literal_suffix(&mut self) {
1274        self.eat_identifier();
1275    }
1276
1277    // Eats the identifier. Note: succeeds on `_`, which isn't a valid
1278    // identifier.
1279    fn eat_identifier(&mut self) {
1280        if !is_id_start(self.first()) {
1281            return;
1282        }
1283        self.bump();
1284
1285        self.eat_while(is_id_continue);
1286    }
1287}