solar_parse/parser/
mod.rs

1use crate::{Lexer, PErr, PResult};
2use smallvec::SmallVec;
3use solar_ast::{
4    self as ast, AstPath, Box, DocComment, DocComments, PathSlice,
5    token::{Delimiter, Token, TokenKind},
6};
7use solar_data_structures::{BumpExt, fmt::or_list};
8use solar_interface::{
9    Ident, Result, Session, Span, Symbol,
10    diagnostics::DiagCtxt,
11    source_map::{FileName, SourceFile},
12};
13use std::{fmt, path::Path};
14
15mod expr;
16mod item;
17mod lit;
18mod stmt;
19mod ty;
20mod yul;
21
22/// Solidity and Yul parser.
23///
24/// # Examples
25///
26/// ```
27/// # mod solar { pub use {solar_ast as ast, solar_interface as interface, solar_parse as parse}; }
28/// # fn main() {}
29#[doc = include_str!("../../doc-examples/parser.rs")]
30/// ```
31pub struct Parser<'sess, 'ast> {
32    /// The parser session.
33    pub sess: &'sess Session,
34    /// The arena where the AST nodes are allocated.
35    pub arena: &'ast ast::Arena,
36
37    /// The current token.
38    pub token: Token,
39    /// The previous token.
40    pub prev_token: Token,
41    /// List of expected tokens. Cleared after each `bump` call.
42    expected_tokens: Vec<ExpectedToken>,
43    /// The span of the last unexpected token.
44    last_unexpected_token_span: Option<Span>,
45    /// The current doc-comments.
46    docs: Vec<DocComment>,
47
48    /// The token stream.
49    tokens: std::vec::IntoIter<Token>,
50
51    /// Whether the parser is in Yul mode.
52    ///
53    /// Currently, this can only happen when parsing a Yul "assembly" block.
54    in_yul: bool,
55    /// Whether the parser is currently parsing a contract block.
56    in_contract: bool,
57}
58
59#[derive(Clone, Debug, PartialEq, Eq)]
60enum ExpectedToken {
61    Token(TokenKind),
62    Keyword(Symbol),
63    Lit,
64    StrLit,
65    Ident,
66    Path,
67    ElementaryType,
68}
69
70impl fmt::Display for ExpectedToken {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        f.write_str(match self {
73            Self::Token(t) => return write!(f, "`{t}`"),
74            Self::Keyword(kw) => return write!(f, "`{kw}`"),
75            Self::StrLit => "string literal",
76            Self::Lit => "literal",
77            Self::Ident => "identifier",
78            Self::Path => "path",
79            Self::ElementaryType => "elementary type name",
80        })
81    }
82}
83
84impl ExpectedToken {
85    fn to_string_many(tokens: &[Self]) -> String {
86        or_list(tokens).to_string()
87    }
88
89    fn eq_kind(&self, other: TokenKind) -> bool {
90        match *self {
91            Self::Token(kind) => kind == other,
92            _ => false,
93        }
94    }
95}
96
97/// A sequence separator.
98#[derive(Debug)]
99struct SeqSep {
100    /// The separator token.
101    sep: Option<TokenKind>,
102    /// `true` if a trailing separator is allowed.
103    trailing_sep_allowed: bool,
104    /// `true` if a trailing separator is required.
105    trailing_sep_required: bool,
106}
107
108impl SeqSep {
109    fn trailing_enforced(t: TokenKind) -> Self {
110        Self { sep: Some(t), trailing_sep_required: true, trailing_sep_allowed: true }
111    }
112
113    #[allow(dead_code)]
114    fn trailing_allowed(t: TokenKind) -> Self {
115        Self { sep: Some(t), trailing_sep_required: false, trailing_sep_allowed: true }
116    }
117
118    fn trailing_disallowed(t: TokenKind) -> Self {
119        Self { sep: Some(t), trailing_sep_required: false, trailing_sep_allowed: false }
120    }
121
122    fn none() -> Self {
123        Self { sep: None, trailing_sep_required: false, trailing_sep_allowed: false }
124    }
125}
126
127impl<'sess, 'ast> Parser<'sess, 'ast> {
128    /// Creates a new parser.
129    pub fn new(sess: &'sess Session, arena: &'ast ast::Arena, tokens: Vec<Token>) -> Self {
130        let mut parser = Self {
131            sess,
132            arena,
133            token: Token::DUMMY,
134            prev_token: Token::DUMMY,
135            expected_tokens: Vec::with_capacity(8),
136            last_unexpected_token_span: None,
137            docs: Vec::with_capacity(4),
138            tokens: tokens.into_iter(),
139            in_yul: false,
140            in_contract: false,
141        };
142        parser.bump();
143        parser
144    }
145
146    /// Creates a new parser from a source code string.
147    pub fn from_source_code(
148        sess: &'sess Session,
149        arena: &'ast ast::Arena,
150        filename: FileName,
151        src: impl Into<String>,
152    ) -> Result<Self> {
153        Self::from_lazy_source_code(sess, arena, filename, || Ok(src.into()))
154    }
155
156    /// Creates a new parser from a file.
157    ///
158    /// The file will not be read if it has already been added into the source map.
159    pub fn from_file(sess: &'sess Session, arena: &'ast ast::Arena, path: &Path) -> Result<Self> {
160        Self::from_lazy_source_code(sess, arena, FileName::Real(path.to_path_buf()), || {
161            sess.source_map().file_loader().load_file(path)
162        })
163    }
164
165    /// Creates a new parser from a source code closure.
166    ///
167    /// The closure will not be called if the file name has already been added into the source map.
168    pub fn from_lazy_source_code(
169        sess: &'sess Session,
170        arena: &'ast ast::Arena,
171        filename: FileName,
172        get_src: impl FnOnce() -> std::io::Result<String>,
173    ) -> Result<Self> {
174        let file = sess
175            .source_map()
176            .new_source_file_with(filename, get_src)
177            .map_err(|e| sess.dcx.err(e.to_string()).emit())?;
178        Ok(Self::from_source_file(sess, arena, &file))
179    }
180
181    /// Creates a new parser from a source file.
182    ///
183    /// Note that the source file must be added to the source map before calling this function.
184    /// Prefer using [`from_source_code`](Self::from_source_code) or [`from_file`](Self::from_file)
185    /// instead.
186    pub fn from_source_file(
187        sess: &'sess Session,
188        arena: &'ast ast::Arena,
189        file: &SourceFile,
190    ) -> Self {
191        Self::from_lexer(arena, Lexer::from_source_file(sess, file))
192    }
193
194    /// Creates a new parser from a lexer.
195    pub fn from_lexer(arena: &'ast ast::Arena, lexer: Lexer<'sess, '_>) -> Self {
196        Self::new(lexer.sess, arena, lexer.into_tokens())
197    }
198
199    /// Returns the diagnostic context.
200    #[inline]
201    pub fn dcx(&self) -> &'sess DiagCtxt {
202        &self.sess.dcx
203    }
204
205    /// Allocates an object on the AST arena.
206    pub fn alloc<T>(&self, value: T) -> Box<'ast, T> {
207        self.arena.alloc(value)
208    }
209
210    /// Allocates a list of objects on the AST arena.
211    ///
212    /// # Panics
213    ///
214    /// Panics if the list is empty.
215    pub fn alloc_path(&self, values: &[Ident]) -> AstPath<'ast> {
216        PathSlice::from_mut_slice(self.arena.alloc_slice_copy(values))
217    }
218
219    /// Allocates a list of objects on the AST arena.
220    pub fn alloc_vec<T>(&self, values: Vec<T>) -> Box<'ast, [T]> {
221        self.arena.alloc_vec(values)
222    }
223
224    /// Allocates a list of objects on the AST arena.
225    pub fn alloc_smallvec<A: smallvec::Array>(&self, values: SmallVec<A>) -> Box<'ast, [A::Item]> {
226        self.arena.alloc_smallvec(values)
227    }
228
229    /// Returns an "unexpected token" error in a [`PResult`] for the current token.
230    #[inline]
231    #[track_caller]
232    pub fn unexpected<T>(&mut self) -> PResult<'sess, T> {
233        Err(self.unexpected_error())
234    }
235
236    /// Returns an "unexpected token" error for the current token.
237    #[inline]
238    #[track_caller]
239    pub fn unexpected_error(&mut self) -> PErr<'sess> {
240        #[cold]
241        #[inline(never)]
242        #[track_caller]
243        fn unexpected_ok(b: bool) -> ! {
244            unreachable!("`unexpected()` returned Ok({b})")
245        }
246        match self.expect_one_of(&[], &[]) {
247            Ok(b) => unexpected_ok(b),
248            Err(e) => e,
249        }
250    }
251
252    /// Expects and consumes the token `t`. Signals an error if the next token is not `t`.
253    #[track_caller]
254    pub fn expect(&mut self, tok: TokenKind) -> PResult<'sess, bool /* recovered */> {
255        if self.expected_tokens.is_empty() {
256            if self.check_noexpect(tok) {
257                self.bump();
258                Ok(false)
259            } else {
260                Err(self.unexpected_error_with(tok))
261            }
262        } else {
263            self.expect_one_of(&[tok], &[])
264        }
265    }
266
267    /// Creates a [`PErr`] for an unexpected token `t`.
268    #[track_caller]
269    fn unexpected_error_with(&mut self, t: TokenKind) -> PErr<'sess> {
270        let prev_span = if self.prev_token.span.is_dummy() {
271            // We don't want to point at the following span after a dummy span.
272            // This happens when the parser finds an empty token stream.
273            self.token.span
274        } else if self.token.is_eof() {
275            // EOF, don't want to point at the following char, but rather the last token.
276            self.prev_token.span
277        } else {
278            self.prev_token.span.shrink_to_hi()
279        };
280        let span = self.token.span;
281
282        let this_token_str = self.token.full_description();
283        let label_exp = format!("expected `{t}`");
284        let msg = format!("{label_exp}, found {this_token_str}");
285        let mut err = self.dcx().err(msg).span(span);
286        if !self.sess.source_map().is_multiline(prev_span.until(span)) {
287            // When the spans are in the same line, it means that the only content
288            // between them is whitespace, point only at the found token.
289            err = err.span_label(span, label_exp);
290        } else {
291            err = err.span_label(prev_span, label_exp);
292            err = err.span_label(span, "unexpected token");
293        }
294        err
295    }
296
297    /// Expect next token to be edible or inedible token. If edible,
298    /// then consume it; if inedible, then return without consuming
299    /// anything. Signal a fatal error if next token is unexpected.
300    #[track_caller]
301    pub fn expect_one_of(
302        &mut self,
303        edible: &[TokenKind],
304        inedible: &[TokenKind],
305    ) -> PResult<'sess, bool /* recovered */> {
306        if edible.contains(&self.token.kind) {
307            self.bump();
308            Ok(false)
309        } else if inedible.contains(&self.token.kind) {
310            // leave it in the input
311            Ok(false)
312        } else if self.token.kind != TokenKind::Eof
313            && self.last_unexpected_token_span == Some(self.token.span)
314        {
315            panic!("called unexpected twice on the same token");
316        } else {
317            self.expected_one_of_not_found(edible, inedible)
318        }
319    }
320
321    #[track_caller]
322    fn expected_one_of_not_found(
323        &mut self,
324        edible: &[TokenKind],
325        inedible: &[TokenKind],
326    ) -> PResult<'sess, bool> {
327        let mut expected = edible
328            .iter()
329            .chain(inedible)
330            .cloned()
331            .map(ExpectedToken::Token)
332            .chain(self.expected_tokens.iter().cloned())
333            .filter(|token| {
334                // Filter out suggestions that suggest the same token
335                // which was found and deemed incorrect.
336                fn is_ident_eq_keyword(found: TokenKind, expected: &ExpectedToken) -> bool {
337                    if let TokenKind::Ident(current_sym) = found
338                        && let ExpectedToken::Keyword(suggested_sym) = expected
339                    {
340                        return current_sym == *suggested_sym;
341                    }
342                    false
343                }
344
345                if !token.eq_kind(self.token.kind) {
346                    let eq = is_ident_eq_keyword(self.token.kind, token);
347                    // If the suggestion is a keyword and the found token is an ident,
348                    // the content of which are equal to the suggestion's content,
349                    // we can remove that suggestion (see the `return false` below).
350
351                    // If this isn't the case however, and the suggestion is a token the
352                    // content of which is the same as the found token's, we remove it as well.
353                    if !eq {
354                        if let ExpectedToken::Token(kind) = token
355                            && *kind == self.token.kind
356                        {
357                            return false;
358                        }
359                        return true;
360                    }
361                }
362                false
363            })
364            .collect::<Vec<_>>();
365        expected.sort_by_cached_key(ToString::to_string);
366        expected.dedup();
367
368        let expect = ExpectedToken::to_string_many(&expected);
369        let actual = self.token.full_description();
370        let (msg_exp, (mut label_span, label_exp)) = match expected.len() {
371            0 => (
372                format!("unexpected token: {actual}"),
373                (self.prev_token.span, "unexpected token after this".to_string()),
374            ),
375            1 => (
376                format!("expected {expect}, found {actual}"),
377                (self.prev_token.span.shrink_to_hi(), format!("expected {expect}")),
378            ),
379            len => {
380                let fmt = format!("expected one of {expect}, found {actual}");
381                let short_expect = if len > 6 { format!("{len} possible tokens") } else { expect };
382                let s = self.prev_token.span.shrink_to_hi();
383                (fmt, (s, format!("expected one of {short_expect}")))
384            }
385        };
386        if self.token.is_eof() {
387            // This is EOF; don't want to point at the following char, but rather the last token.
388            label_span = self.prev_token.span;
389        };
390
391        self.last_unexpected_token_span = Some(self.token.span);
392        let mut err = self.dcx().err(msg_exp).span(self.token.span);
393
394        if self.prev_token.span.is_dummy()
395            || !self
396                .sess
397                .source_map()
398                .is_multiline(self.token.span.shrink_to_hi().until(label_span.shrink_to_lo()))
399        {
400            // When the spans are in the same line, it means that the only content between
401            // them is whitespace, point at the found token in that case.
402            err = err.span_label(self.token.span, label_exp);
403        } else {
404            err = err.span_label(label_span, label_exp);
405            err = err.span_label(self.token.span, "unexpected token");
406        }
407
408        Err(err)
409    }
410
411    /// Expects and consumes a semicolon.
412    #[track_caller]
413    fn expect_semi(&mut self) -> PResult<'sess, ()> {
414        self.expect(TokenKind::Semi).map(drop)
415    }
416
417    /// Checks if the next token is `tok`, and returns `true` if so.
418    ///
419    /// This method will automatically add `tok` to `expected_tokens` if `tok` is not
420    /// encountered.
421    #[inline]
422    #[must_use]
423    fn check(&mut self, tok: TokenKind) -> bool {
424        let is_present = self.check_noexpect(tok);
425        if !is_present {
426            self.expected_tokens.push(ExpectedToken::Token(tok));
427        }
428        is_present
429    }
430
431    #[inline]
432    #[must_use]
433    fn check_noexpect(&self, tok: TokenKind) -> bool {
434        self.token.kind == tok
435    }
436
437    /// Consumes a token 'tok' if it exists. Returns whether the given token was present.
438    ///
439    /// the main purpose of this function is to reduce the cluttering of the suggestions list
440    /// which using the normal eat method could introduce in some cases.
441    #[must_use]
442    pub fn eat_noexpect(&mut self, tok: TokenKind) -> bool {
443        let is_present = self.check_noexpect(tok);
444        if is_present {
445            self.bump()
446        }
447        is_present
448    }
449
450    /// Consumes a token 'tok' if it exists. Returns whether the given token was present.
451    #[must_use]
452    pub fn eat(&mut self, tok: TokenKind) -> bool {
453        let is_present = self.check(tok);
454        if is_present {
455            self.bump()
456        }
457        is_present
458    }
459
460    /// If the next token is the given keyword, returns `true` without eating it.
461    /// An expectation is also added for diagnostics purposes.
462    #[must_use]
463    fn check_keyword(&mut self, kw: Symbol) -> bool {
464        self.expected_tokens.push(ExpectedToken::Keyword(kw));
465        self.token.is_keyword(kw)
466    }
467
468    /// If the next token is the given keyword, eats it and returns `true`.
469    /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes.
470    #[must_use]
471    pub fn eat_keyword(&mut self, kw: Symbol) -> bool {
472        if self.check_keyword(kw) {
473            self.bump();
474            true
475        } else {
476            false
477        }
478    }
479
480    /// If the given word is not a keyword, signals an error.
481    /// If the next token is not the given word, signals an error.
482    /// Otherwise, eats it.
483    #[track_caller]
484    fn expect_keyword(&mut self, kw: Symbol) -> PResult<'sess, ()> {
485        if !self.eat_keyword(kw) { self.unexpected() } else { Ok(()) }
486    }
487
488    #[must_use]
489    fn check_ident(&mut self) -> bool {
490        self.check_or_expected(self.token.is_ident(), ExpectedToken::Ident)
491    }
492
493    #[must_use]
494    fn check_nr_ident(&mut self) -> bool {
495        self.check_or_expected(self.token.is_non_reserved_ident(self.in_yul), ExpectedToken::Ident)
496    }
497
498    #[must_use]
499    fn check_path(&mut self) -> bool {
500        self.check_or_expected(self.token.is_ident(), ExpectedToken::Path)
501    }
502
503    #[must_use]
504    fn check_lit(&mut self) -> bool {
505        self.check_or_expected(self.token.is_lit(), ExpectedToken::Lit)
506    }
507
508    #[must_use]
509    fn check_str_lit(&mut self) -> bool {
510        self.check_or_expected(self.token.is_str_lit(), ExpectedToken::StrLit)
511    }
512
513    #[must_use]
514    fn check_elementary_type(&mut self) -> bool {
515        self.check_or_expected(self.token.is_elementary_type(), ExpectedToken::ElementaryType)
516    }
517
518    #[must_use]
519    fn check_or_expected(&mut self, ok: bool, t: ExpectedToken) -> bool {
520        if !ok {
521            self.expected_tokens.push(t);
522        }
523        ok
524    }
525
526    /// Parses a comma-separated sequence delimited by parentheses (e.g. `(x, y)`).
527    /// The function `f` must consume tokens until reaching the next separator or
528    /// closing bracket.
529    #[track_caller]
530    #[inline]
531    fn parse_paren_comma_seq<T>(
532        &mut self,
533        allow_empty: bool,
534        f: impl FnMut(&mut Self) -> PResult<'sess, T>,
535    ) -> PResult<'sess, Box<'ast, [T]>> {
536        self.parse_delim_comma_seq(Delimiter::Parenthesis, allow_empty, f)
537    }
538
539    /// Parses a comma-separated sequence, including both delimiters.
540    /// The function `f` must consume tokens until reaching the next separator or
541    /// closing bracket.
542    #[track_caller]
543    #[inline]
544    fn parse_delim_comma_seq<T>(
545        &mut self,
546        delim: Delimiter,
547        allow_empty: bool,
548        f: impl FnMut(&mut Self) -> PResult<'sess, T>,
549    ) -> PResult<'sess, Box<'ast, [T]>> {
550        self.parse_delim_seq(delim, SeqSep::trailing_disallowed(TokenKind::Comma), allow_empty, f)
551    }
552
553    /// Parses a comma-separated sequence.
554    /// The function `f` must consume tokens until reaching the next separator.
555    #[track_caller]
556    #[inline]
557    fn parse_nodelim_comma_seq<T>(
558        &mut self,
559        stop: TokenKind,
560        allow_empty: bool,
561        f: impl FnMut(&mut Self) -> PResult<'sess, T>,
562    ) -> PResult<'sess, Box<'ast, [T]>> {
563        self.parse_seq_to_before_end(
564            stop,
565            SeqSep::trailing_disallowed(TokenKind::Comma),
566            allow_empty,
567            f,
568        )
569        .map(|(v, _recovered)| v)
570    }
571
572    /// Parses a `sep`-separated sequence, including both delimiters.
573    /// The function `f` must consume tokens until reaching the next separator or
574    /// closing bracket.
575    #[track_caller]
576    #[inline]
577    fn parse_delim_seq<T>(
578        &mut self,
579        delim: Delimiter,
580        sep: SeqSep,
581        allow_empty: bool,
582        f: impl FnMut(&mut Self) -> PResult<'sess, T>,
583    ) -> PResult<'sess, Box<'ast, [T]>> {
584        self.parse_unspanned_seq(
585            TokenKind::OpenDelim(delim),
586            TokenKind::CloseDelim(delim),
587            sep,
588            allow_empty,
589            f,
590        )
591    }
592
593    /// Parses a sequence, including both delimiters. The function
594    /// `f` must consume tokens until reaching the next separator or
595    /// closing bracket.
596    #[track_caller]
597    #[inline]
598    fn parse_unspanned_seq<T>(
599        &mut self,
600        bra: TokenKind,
601        ket: TokenKind,
602        sep: SeqSep,
603        allow_empty: bool,
604        f: impl FnMut(&mut Self) -> PResult<'sess, T>,
605    ) -> PResult<'sess, Box<'ast, [T]>> {
606        self.expect(bra)?;
607        self.parse_seq_to_end(ket, sep, allow_empty, f)
608    }
609
610    /// Parses a sequence, including only the closing delimiter. The function
611    /// `f` must consume tokens until reaching the next separator or
612    /// closing bracket.
613    #[track_caller]
614    #[inline]
615    fn parse_seq_to_end<T>(
616        &mut self,
617        ket: TokenKind,
618        sep: SeqSep,
619        allow_empty: bool,
620        f: impl FnMut(&mut Self) -> PResult<'sess, T>,
621    ) -> PResult<'sess, Box<'ast, [T]>> {
622        let (val, recovered) = self.parse_seq_to_before_end(ket, sep, allow_empty, f)?;
623        if !recovered {
624            self.expect(ket)?;
625        }
626        Ok(val)
627    }
628
629    /// Parses a sequence, not including the delimiters. The function
630    /// `f` must consume tokens until reaching the next separator or
631    /// closing bracket.
632    #[track_caller]
633    #[inline]
634    fn parse_seq_to_before_end<T>(
635        &mut self,
636        ket: TokenKind,
637        sep: SeqSep,
638        allow_empty: bool,
639        f: impl FnMut(&mut Self) -> PResult<'sess, T>,
640    ) -> PResult<'sess, (Box<'ast, [T]>, bool /* recovered */)> {
641        self.parse_seq_to_before_tokens(&[ket], sep, allow_empty, f)
642    }
643
644    /// Checks if the next token is contained within `kets`, and returns `true` if so.
645    fn check_any(&mut self, kets: &[TokenKind]) -> bool {
646        kets.iter().any(|&k| self.check(k))
647    }
648
649    /// Parses a sequence until the specified delimiters. The function
650    /// `f` must consume tokens until reaching the next separator or
651    /// closing bracket.
652    #[track_caller]
653    fn parse_seq_to_before_tokens<T>(
654        &mut self,
655        kets: &[TokenKind],
656        sep: SeqSep,
657        allow_empty: bool,
658        mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
659    ) -> PResult<'sess, (Box<'ast, [T]>, bool /* recovered */)> {
660        let mut first = true;
661        let mut recovered = false;
662        let mut trailing = false;
663        let mut v = SmallVec::<[T; 8]>::new();
664
665        if !allow_empty {
666            v.push(f(self)?);
667            first = false;
668        }
669
670        while !self.check_any(kets) {
671            if let TokenKind::CloseDelim(..) | TokenKind::Eof = self.token.kind {
672                break;
673            }
674
675            if let Some(sep_kind) = sep.sep {
676                if first {
677                    // no separator for the first element
678                    first = false;
679                } else {
680                    // check for separator
681                    match self.expect(sep_kind) {
682                        Ok(recovered_) => {
683                            if recovered_ {
684                                recovered = true;
685                                break;
686                            }
687                        }
688                        Err(e) => return Err(e),
689                    }
690
691                    if self.check_any(kets) {
692                        trailing = true;
693                        break;
694                    }
695                }
696            }
697
698            v.push(f(self)?);
699        }
700
701        if let Some(sep_kind) = sep.sep {
702            let open_close_delim = first && allow_empty;
703            if !open_close_delim
704                && sep.trailing_sep_required
705                && !trailing
706                && let Err(e) = self.expect(sep_kind)
707            {
708                e.emit();
709            }
710            if !sep.trailing_sep_allowed && trailing {
711                let msg = format!("trailing `{sep_kind}` separator is not allowed");
712                self.dcx().err(msg).span(self.prev_token.span).emit();
713            }
714        }
715
716        Ok((self.alloc_smallvec(v), recovered))
717    }
718
719    /// Advance the parser by one token.
720    pub fn bump(&mut self) {
721        let next = self.next_token();
722        if next.is_comment_or_doc() {
723            return self.bump_trivia(next);
724        }
725        self.inlined_bump_with(next);
726    }
727
728    /// Advance the parser by one token using provided token as the next one.
729    ///
730    /// # Panics
731    ///
732    /// Panics if the provided token is a comment.
733    pub fn bump_with(&mut self, next: Token) {
734        self.inlined_bump_with(next);
735    }
736
737    /// This always-inlined version should only be used on hot code paths.
738    #[inline(always)]
739    fn inlined_bump_with(&mut self, next: Token) {
740        #[cfg(debug_assertions)]
741        if next.is_comment_or_doc() {
742            self.dcx().bug("`bump_with` should not be used with comments").span(next.span).emit();
743        }
744        self.prev_token = std::mem::replace(&mut self.token, next);
745        self.expected_tokens.clear();
746        self.docs.clear();
747    }
748
749    /// Bumps comments and docs.
750    ///
751    /// Pushes docs to `self.docs`. Retrieve them with `parse_doc_comments`.
752    #[cold]
753    fn bump_trivia(&mut self, next: Token) {
754        self.docs.clear();
755
756        debug_assert!(next.is_comment_or_doc());
757        self.prev_token = std::mem::replace(&mut self.token, next);
758        while let Some((is_doc, doc)) = self.token.comment() {
759            if is_doc {
760                self.docs.push(doc);
761            }
762            // Don't set `prev_token` on purpose.
763            self.token = self.next_token();
764        }
765
766        self.expected_tokens.clear();
767    }
768
769    /// Advances the internal `tokens` iterator, without updating the parser state.
770    ///
771    /// Use [`bump`](Self::bump) and [`token`](Self::token) instead.
772    #[inline(always)]
773    fn next_token(&mut self) -> Token {
774        self.tokens.next().unwrap_or(Token { kind: TokenKind::Eof, span: self.token.span })
775    }
776
777    /// Returns the token `dist` tokens ahead of the current one.
778    ///
779    /// [`Eof`](Token::EOF) will be returned if the look-ahead is any distance past the end of the
780    /// tokens.
781    #[inline]
782    pub fn look_ahead(&self, dist: usize) -> Token {
783        // Specialize for the common `dist` cases.
784        match dist {
785            0 => self.token,
786            1 => self.look_ahead_full(1),
787            2 => self.look_ahead_full(2),
788            dist => self.look_ahead_full(dist),
789        }
790    }
791
792    fn look_ahead_full(&self, dist: usize) -> Token {
793        self.tokens
794            .as_slice()
795            .iter()
796            .copied()
797            .filter(|t| !t.is_comment_or_doc())
798            .nth(dist - 1)
799            .unwrap_or(Token::EOF)
800    }
801
802    /// Calls `f` with the token `dist` tokens ahead of the current one.
803    ///
804    /// See [`look_ahead`](Self::look_ahead) for more information.
805    #[inline]
806    pub fn look_ahead_with<R>(&self, dist: usize, f: impl FnOnce(Token) -> R) -> R {
807        f(self.look_ahead(dist))
808    }
809
810    /// Runs `f` with the parser in a contract context.
811    #[inline]
812    fn in_contract<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
813        let old = std::mem::replace(&mut self.in_contract, true);
814        let res = f(self);
815        self.in_contract = old;
816        res
817    }
818
819    /// Runs `f` with the parser in a Yul context.
820    #[inline]
821    fn in_yul<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
822        let old = std::mem::replace(&mut self.in_yul, true);
823        let res = f(self);
824        self.in_yul = old;
825        res
826    }
827}
828
829/// Common parsing methods.
830impl<'sess, 'ast> Parser<'sess, 'ast> {
831    /// Provides a spanned parser.
832    #[track_caller]
833    pub fn parse_spanned<T>(
834        &mut self,
835        f: impl FnOnce(&mut Self) -> PResult<'sess, T>,
836    ) -> PResult<'sess, (Span, T)> {
837        let lo = self.token.span;
838        let res = f(self);
839        let span = lo.to(self.prev_token.span);
840        match res {
841            Ok(t) => Ok((span, t)),
842            Err(e) if e.span.is_dummy() => Err(e.span(span)),
843            Err(e) => Err(e),
844        }
845    }
846
847    /// Parses contiguous doc comments. Can be empty.
848    #[inline]
849    pub fn parse_doc_comments(&mut self) -> DocComments<'ast> {
850        if !self.docs.is_empty() { self.parse_doc_comments_inner() } else { Default::default() }
851    }
852
853    #[cold]
854    fn parse_doc_comments_inner(&mut self) -> DocComments<'ast> {
855        let docs = self.arena.alloc_slice_copy(&self.docs);
856        self.docs.clear();
857        docs.into()
858    }
859
860    /// Parses a qualified identifier: `foo.bar.baz`.
861    #[track_caller]
862    pub fn parse_path(&mut self) -> PResult<'sess, AstPath<'ast>> {
863        let first = self.parse_ident()?;
864        self.parse_path_with(first)
865    }
866
867    /// Parses a qualified identifier starting with the given identifier.
868    #[track_caller]
869    pub fn parse_path_with(&mut self, first: Ident) -> PResult<'sess, AstPath<'ast>> {
870        if self.in_yul {
871            self.parse_path_with_f(first, Self::parse_yul_path_ident)
872        } else {
873            self.parse_path_with_f(first, Self::parse_ident)
874        }
875    }
876
877    /// Parses either an identifier or a Yul EVM builtin.
878    fn parse_yul_path_ident(&mut self) -> PResult<'sess, Ident> {
879        let ident = self.ident_or_err(true)?;
880        if !ident.is_yul_evm_builtin() && ident.is_reserved(true) {
881            self.expected_ident_found_err().emit();
882        }
883        self.bump();
884        Ok(ident)
885    }
886
887    /// Parses a qualified identifier: `foo.bar.baz`.
888    #[track_caller]
889    pub fn parse_path_any(&mut self) -> PResult<'sess, AstPath<'ast>> {
890        let first = self.parse_ident_any()?;
891        self.parse_path_with_f(first, Self::parse_ident_any)
892    }
893
894    /// Parses a qualified identifier starting with the given identifier.
895    #[track_caller]
896    fn parse_path_with_f(
897        &mut self,
898        first: Ident,
899        mut f: impl FnMut(&mut Self) -> PResult<'sess, Ident>,
900    ) -> PResult<'sess, AstPath<'ast>> {
901        if !self.check_noexpect(TokenKind::Dot) {
902            return Ok(self.alloc_path(&[first]));
903        }
904
905        let mut path = SmallVec::<[_; 4]>::new();
906        path.push(first);
907        while self.eat(TokenKind::Dot) {
908            path.push(f(self)?);
909        }
910        Ok(self.alloc_path(&path))
911    }
912
913    /// Parses an identifier.
914    #[track_caller]
915    pub fn parse_ident(&mut self) -> PResult<'sess, Ident> {
916        self.parse_ident_common(true)
917    }
918
919    /// Parses an identifier. Does not check if the identifier is a reserved keyword.
920    #[track_caller]
921    pub fn parse_ident_any(&mut self) -> PResult<'sess, Ident> {
922        let ident = self.ident_or_err(true)?;
923        self.bump();
924        Ok(ident)
925    }
926
927    /// Parses an optional identifier.
928    #[track_caller]
929    pub fn parse_ident_opt(&mut self) -> PResult<'sess, Option<Ident>> {
930        if self.check_ident() { self.parse_ident().map(Some) } else { Ok(None) }
931    }
932
933    #[track_caller]
934    fn parse_ident_common(&mut self, recover: bool) -> PResult<'sess, Ident> {
935        let ident = self.ident_or_err(recover)?;
936        if ident.is_reserved(self.in_yul) {
937            let err = self.expected_ident_found_err();
938            if recover {
939                err.emit();
940            } else {
941                return Err(err);
942            }
943        }
944        self.bump();
945        Ok(ident)
946    }
947
948    /// Returns Ok if the current token is an identifier. Does not advance the parser.
949    #[track_caller]
950    fn ident_or_err(&mut self, recover: bool) -> PResult<'sess, Ident> {
951        match self.token.ident() {
952            Some(ident) => Ok(ident),
953            None => self.expected_ident_found(recover),
954        }
955    }
956
957    #[track_caller]
958    fn expected_ident_found(&mut self, recover: bool) -> PResult<'sess, Ident> {
959        self.expected_ident_found_other(self.token, recover)
960    }
961
962    #[track_caller]
963    fn expected_ident_found_other(&mut self, token: Token, recover: bool) -> PResult<'sess, Ident> {
964        let msg = format!("expected identifier, found {}", token.full_description());
965        let span = token.span;
966        let mut err = self.dcx().err(msg).span(span);
967
968        let mut recovered_ident = None;
969
970        let suggest_remove_comma = token.kind == TokenKind::Comma && self.look_ahead(1).is_ident();
971        if suggest_remove_comma {
972            if recover {
973                self.bump();
974                recovered_ident = self.ident_or_err(false).ok();
975            }
976            err = err.span_help(span, "remove this comma");
977        }
978
979        if recover && let Some(ident) = recovered_ident {
980            err.emit();
981            return Ok(ident);
982        }
983        Err(err)
984    }
985
986    #[track_caller]
987    fn expected_ident_found_err(&mut self) -> PErr<'sess> {
988        self.expected_ident_found(false).unwrap_err()
989    }
990}