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