solar_parse/parser/
mod.rs

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