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