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
22const PARSER_RECURSION_LIMIT: usize = 128;
26
27#[doc = include_str!("../../doc-examples/parser.rs")]
35pub struct Parser<'sess, 'ast> {
37 pub sess: &'sess Session,
39 pub arena: &'ast ast::Arena,
41
42 pub token: Token,
44 pub prev_token: Token,
46 expected_tokens: Vec<ExpectedToken>,
48 last_unexpected_token_span: Option<Span>,
50 docs: Vec<DocComment>,
52
53 tokens: std::vec::IntoIter<Token>,
55
56 in_yul: bool,
60 in_contract: bool,
62
63 recursion_depth: usize,
65}
66
67#[derive(Clone, Debug, PartialEq, Eq)]
68enum ExpectedToken {
69 Token(TokenKind),
70 Keyword(Symbol),
71 Lit,
72 StrLit,
73 Ident,
74 Path,
75 ElementaryType,
76}
77
78impl fmt::Display for ExpectedToken {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 f.write_str(match self {
81 Self::Token(t) => return write!(f, "`{t}`"),
82 Self::Keyword(kw) => return write!(f, "`{kw}`"),
83 Self::StrLit => "string literal",
84 Self::Lit => "literal",
85 Self::Ident => "identifier",
86 Self::Path => "path",
87 Self::ElementaryType => "elementary type name",
88 })
89 }
90}
91
92impl ExpectedToken {
93 fn to_string_many(tokens: &[Self]) -> String {
94 or_list(tokens).to_string()
95 }
96
97 fn eq_kind(&self, other: TokenKind) -> bool {
98 match *self {
99 Self::Token(kind) => kind == other,
100 _ => false,
101 }
102 }
103}
104
105#[derive(Debug)]
107struct SeqSep {
108 sep: Option<TokenKind>,
110 trailing_sep_allowed: bool,
112 trailing_sep_required: bool,
114}
115
116impl SeqSep {
117 fn trailing_enforced(t: TokenKind) -> Self {
118 Self { sep: Some(t), trailing_sep_required: true, trailing_sep_allowed: true }
119 }
120
121 #[allow(dead_code)]
122 fn trailing_allowed(t: TokenKind) -> Self {
123 Self { sep: Some(t), trailing_sep_required: false, trailing_sep_allowed: true }
124 }
125
126 fn trailing_disallowed(t: TokenKind) -> Self {
127 Self { sep: Some(t), trailing_sep_required: false, trailing_sep_allowed: false }
128 }
129
130 fn none() -> Self {
131 Self { sep: None, trailing_sep_required: false, trailing_sep_allowed: false }
132 }
133}
134
135impl<'sess, 'ast> Parser<'sess, 'ast> {
136 pub fn new(sess: &'sess Session, arena: &'ast ast::Arena, tokens: Vec<Token>) -> Self {
138 let mut parser = Self {
139 sess,
140 arena,
141 token: Token::DUMMY,
142 prev_token: Token::DUMMY,
143 expected_tokens: Vec::with_capacity(8),
144 last_unexpected_token_span: None,
145 docs: Vec::with_capacity(4),
146 tokens: tokens.into_iter(),
147 in_yul: false,
148 in_contract: false,
149 recursion_depth: 0,
150 };
151 parser.bump();
152 parser
153 }
154
155 pub fn from_source_code(
157 sess: &'sess Session,
158 arena: &'ast ast::Arena,
159 filename: FileName,
160 src: impl Into<String>,
161 ) -> Result<Self> {
162 Self::from_lazy_source_code(sess, arena, filename, || Ok(src.into()))
163 }
164
165 pub fn from_file(sess: &'sess Session, arena: &'ast ast::Arena, path: &Path) -> Result<Self> {
169 Self::from_lazy_source_code(sess, arena, FileName::Real(path.to_path_buf()), || {
170 sess.source_map().file_loader().load_file(path)
171 })
172 }
173
174 pub fn from_lazy_source_code(
178 sess: &'sess Session,
179 arena: &'ast ast::Arena,
180 filename: FileName,
181 get_src: impl FnOnce() -> std::io::Result<String>,
182 ) -> Result<Self> {
183 let file = sess
184 .source_map()
185 .new_source_file_with(filename, get_src)
186 .map_err(|e| sess.dcx.err(e.to_string()).emit())?;
187 Ok(Self::from_source_file(sess, arena, &file))
188 }
189
190 pub fn from_source_file(
196 sess: &'sess Session,
197 arena: &'ast ast::Arena,
198 file: &SourceFile,
199 ) -> Self {
200 Self::from_lexer(arena, Lexer::from_source_file(sess, file))
201 }
202
203 pub fn from_lexer(arena: &'ast ast::Arena, lexer: Lexer<'sess, '_>) -> Self {
205 Self::new(lexer.sess, arena, lexer.into_tokens())
206 }
207
208 #[inline]
210 pub fn dcx(&self) -> &'sess DiagCtxt {
211 &self.sess.dcx
212 }
213
214 pub fn alloc<T>(&self, value: T) -> Box<'ast, T> {
216 self.arena.alloc(value)
217 }
218
219 pub fn alloc_path(&self, values: &[Ident]) -> AstPath<'ast> {
225 PathSlice::from_mut_slice(self.arena.alloc_slice_copy(values))
226 }
227
228 pub fn alloc_vec<T>(&self, values: Vec<T>) -> Box<'ast, [T]> {
230 self.arena.alloc_vec(values)
231 }
232
233 pub fn alloc_smallvec<A: smallvec::Array>(&self, values: SmallVec<A>) -> Box<'ast, [A::Item]> {
235 self.arena.alloc_smallvec(values)
236 }
237
238 #[inline]
240 #[track_caller]
241 pub fn unexpected<T>(&mut self) -> PResult<'sess, T> {
242 Err(self.unexpected_error())
243 }
244
245 #[inline]
247 #[track_caller]
248 pub fn unexpected_error(&mut self) -> PErr<'sess> {
249 #[cold]
250 #[inline(never)]
251 #[track_caller]
252 fn unexpected_ok(b: bool) -> ! {
253 unreachable!("`unexpected()` returned Ok({b})")
254 }
255 match self.expect_one_of(&[], &[]) {
256 Ok(b) => unexpected_ok(b),
257 Err(e) => e,
258 }
259 }
260
261 #[track_caller]
263 pub fn expect(&mut self, tok: TokenKind) -> PResult<'sess, bool > {
264 if self.expected_tokens.is_empty() {
265 if self.check_noexpect(tok) {
266 self.bump();
267 Ok(false)
268 } else {
269 Err(self.unexpected_error_with(tok))
270 }
271 } else {
272 self.expect_one_of(&[tok], &[])
273 }
274 }
275
276 #[track_caller]
278 fn unexpected_error_with(&mut self, t: TokenKind) -> PErr<'sess> {
279 let prev_span = if self.prev_token.span.is_dummy() {
280 self.token.span
283 } else if self.token.is_eof() {
284 self.prev_token.span
286 } else {
287 self.prev_token.span.shrink_to_hi()
288 };
289 let span = self.token.span;
290
291 let this_token_str = self.token.full_description();
292 let label_exp = format!("expected `{t}`");
293 let msg = format!("{label_exp}, found {this_token_str}");
294 let mut err = self.dcx().err(msg).span(span);
295 if !self.sess.source_map().is_multiline(prev_span.until(span)) {
296 err = err.span_label(span, label_exp);
299 } else {
300 err = err.span_label(prev_span, label_exp);
301 err = err.span_label(span, "unexpected token");
302 }
303 err
304 }
305
306 #[track_caller]
310 pub fn expect_one_of(
311 &mut self,
312 edible: &[TokenKind],
313 inedible: &[TokenKind],
314 ) -> PResult<'sess, bool > {
315 if edible.contains(&self.token.kind) {
316 self.bump();
317 Ok(false)
318 } else if inedible.contains(&self.token.kind) {
319 Ok(false)
321 } else if self.token.kind != TokenKind::Eof
322 && self.last_unexpected_token_span == Some(self.token.span)
323 {
324 panic!("called unexpected twice on the same token");
325 } else {
326 self.expected_one_of_not_found(edible, inedible)
327 }
328 }
329
330 #[track_caller]
331 fn expected_one_of_not_found(
332 &mut self,
333 edible: &[TokenKind],
334 inedible: &[TokenKind],
335 ) -> PResult<'sess, bool> {
336 let mut expected = edible
337 .iter()
338 .chain(inedible)
339 .cloned()
340 .map(ExpectedToken::Token)
341 .chain(self.expected_tokens.iter().cloned())
342 .filter(|token| {
343 fn is_ident_eq_keyword(found: TokenKind, expected: &ExpectedToken) -> bool {
346 if let TokenKind::Ident(current_sym) = found
347 && let ExpectedToken::Keyword(suggested_sym) = expected
348 {
349 return current_sym == *suggested_sym;
350 }
351 false
352 }
353
354 if !token.eq_kind(self.token.kind) {
355 let eq = is_ident_eq_keyword(self.token.kind, token);
356 if !eq {
363 if let ExpectedToken::Token(kind) = token
364 && *kind == self.token.kind
365 {
366 return false;
367 }
368 return true;
369 }
370 }
371 false
372 })
373 .collect::<Vec<_>>();
374 expected.sort_by_cached_key(ToString::to_string);
375 expected.dedup();
376
377 let expect = ExpectedToken::to_string_many(&expected);
378 let actual = self.token.full_description();
379 let (msg_exp, (mut label_span, label_exp)) = match expected.len() {
380 0 => (
381 format!("unexpected token: {actual}"),
382 (self.prev_token.span, "unexpected token after this".to_string()),
383 ),
384 1 => (
385 format!("expected {expect}, found {actual}"),
386 (self.prev_token.span.shrink_to_hi(), format!("expected {expect}")),
387 ),
388 len => {
389 let fmt = format!("expected one of {expect}, found {actual}");
390 let short_expect = if len > 6 { format!("{len} possible tokens") } else { expect };
391 let s = self.prev_token.span.shrink_to_hi();
392 (fmt, (s, format!("expected one of {short_expect}")))
393 }
394 };
395 if self.token.is_eof() {
396 label_span = self.prev_token.span;
398 };
399
400 self.last_unexpected_token_span = Some(self.token.span);
401 let mut err = self.dcx().err(msg_exp).span(self.token.span);
402
403 if self.prev_token.span.is_dummy()
404 || !self
405 .sess
406 .source_map()
407 .is_multiline(self.token.span.shrink_to_hi().until(label_span.shrink_to_lo()))
408 {
409 err = err.span_label(self.token.span, label_exp);
412 } else {
413 err = err.span_label(label_span, label_exp);
414 err = err.span_label(self.token.span, "unexpected token");
415 }
416
417 Err(err)
418 }
419
420 #[track_caller]
422 fn expect_semi(&mut self) -> PResult<'sess, ()> {
423 self.expect(TokenKind::Semi).map(drop)
424 }
425
426 #[inline]
431 #[must_use]
432 fn check(&mut self, tok: TokenKind) -> bool {
433 let is_present = self.check_noexpect(tok);
434 if !is_present {
435 self.expected_tokens.push(ExpectedToken::Token(tok));
436 }
437 is_present
438 }
439
440 #[inline]
441 #[must_use]
442 fn check_noexpect(&self, tok: TokenKind) -> bool {
443 self.token.kind == tok
444 }
445
446 #[must_use]
451 pub fn eat_noexpect(&mut self, tok: TokenKind) -> bool {
452 let is_present = self.check_noexpect(tok);
453 if is_present {
454 self.bump()
455 }
456 is_present
457 }
458
459 #[must_use]
461 pub fn eat(&mut self, tok: TokenKind) -> bool {
462 let is_present = self.check(tok);
463 if is_present {
464 self.bump()
465 }
466 is_present
467 }
468
469 #[must_use]
472 fn check_keyword(&mut self, kw: Symbol) -> bool {
473 self.expected_tokens.push(ExpectedToken::Keyword(kw));
474 self.token.is_keyword(kw)
475 }
476
477 #[must_use]
480 pub fn eat_keyword(&mut self, kw: Symbol) -> bool {
481 if self.check_keyword(kw) {
482 self.bump();
483 true
484 } else {
485 false
486 }
487 }
488
489 #[track_caller]
493 fn expect_keyword(&mut self, kw: Symbol) -> PResult<'sess, ()> {
494 if !self.eat_keyword(kw) { self.unexpected() } else { Ok(()) }
495 }
496
497 #[must_use]
498 fn check_ident(&mut self) -> bool {
499 self.check_or_expected(self.token.is_ident(), ExpectedToken::Ident)
500 }
501
502 #[must_use]
503 fn check_nr_ident(&mut self) -> bool {
504 self.check_or_expected(self.token.is_non_reserved_ident(self.in_yul), ExpectedToken::Ident)
505 }
506
507 #[must_use]
508 fn check_path(&mut self) -> bool {
509 self.check_or_expected(self.token.is_ident(), ExpectedToken::Path)
510 }
511
512 #[must_use]
513 fn check_lit(&mut self) -> bool {
514 self.check_or_expected(self.token.is_lit(), ExpectedToken::Lit)
515 }
516
517 #[must_use]
518 fn check_str_lit(&mut self) -> bool {
519 self.check_or_expected(self.token.is_str_lit(), ExpectedToken::StrLit)
520 }
521
522 #[must_use]
523 fn check_elementary_type(&mut self) -> bool {
524 self.check_or_expected(self.token.is_elementary_type(), ExpectedToken::ElementaryType)
525 }
526
527 #[must_use]
528 fn check_or_expected(&mut self, ok: bool, t: ExpectedToken) -> bool {
529 if !ok {
530 self.expected_tokens.push(t);
531 }
532 ok
533 }
534
535 #[track_caller]
539 #[inline]
540 fn parse_paren_comma_seq<T>(
541 &mut self,
542 allow_empty: bool,
543 f: impl FnMut(&mut Self) -> PResult<'sess, T>,
544 ) -> PResult<'sess, Box<'ast, [T]>> {
545 self.parse_delim_comma_seq(Delimiter::Parenthesis, allow_empty, f)
546 }
547
548 #[track_caller]
552 #[inline]
553 fn parse_delim_comma_seq<T>(
554 &mut self,
555 delim: Delimiter,
556 allow_empty: bool,
557 f: impl FnMut(&mut Self) -> PResult<'sess, T>,
558 ) -> PResult<'sess, Box<'ast, [T]>> {
559 self.parse_delim_seq(delim, SeqSep::trailing_disallowed(TokenKind::Comma), allow_empty, f)
560 }
561
562 #[track_caller]
565 #[inline]
566 fn parse_nodelim_comma_seq<T>(
567 &mut self,
568 stop: TokenKind,
569 allow_empty: bool,
570 f: impl FnMut(&mut Self) -> PResult<'sess, T>,
571 ) -> PResult<'sess, Box<'ast, [T]>> {
572 self.parse_seq_to_before_end(
573 stop,
574 SeqSep::trailing_disallowed(TokenKind::Comma),
575 allow_empty,
576 f,
577 )
578 .map(|(v, _recovered)| v)
579 }
580
581 #[track_caller]
585 #[inline]
586 fn parse_delim_seq<T>(
587 &mut self,
588 delim: Delimiter,
589 sep: SeqSep,
590 allow_empty: bool,
591 f: impl FnMut(&mut Self) -> PResult<'sess, T>,
592 ) -> PResult<'sess, Box<'ast, [T]>> {
593 self.parse_unspanned_seq(
594 TokenKind::OpenDelim(delim),
595 TokenKind::CloseDelim(delim),
596 sep,
597 allow_empty,
598 f,
599 )
600 }
601
602 #[track_caller]
606 #[inline]
607 fn parse_unspanned_seq<T>(
608 &mut self,
609 bra: TokenKind,
610 ket: TokenKind,
611 sep: SeqSep,
612 allow_empty: bool,
613 f: impl FnMut(&mut Self) -> PResult<'sess, T>,
614 ) -> PResult<'sess, Box<'ast, [T]>> {
615 self.expect(bra)?;
616 self.parse_seq_to_end(ket, sep, allow_empty, f)
617 }
618
619 #[track_caller]
623 #[inline]
624 fn parse_seq_to_end<T>(
625 &mut self,
626 ket: TokenKind,
627 sep: SeqSep,
628 allow_empty: bool,
629 f: impl FnMut(&mut Self) -> PResult<'sess, T>,
630 ) -> PResult<'sess, Box<'ast, [T]>> {
631 let (val, recovered) = self.parse_seq_to_before_end(ket, sep, allow_empty, f)?;
632 if !recovered {
633 self.expect(ket)?;
634 }
635 Ok(val)
636 }
637
638 #[track_caller]
642 #[inline]
643 fn parse_seq_to_before_end<T>(
644 &mut self,
645 ket: TokenKind,
646 sep: SeqSep,
647 allow_empty: bool,
648 f: impl FnMut(&mut Self) -> PResult<'sess, T>,
649 ) -> PResult<'sess, (Box<'ast, [T]>, bool )> {
650 self.parse_seq_to_before_tokens(&[ket], sep, allow_empty, f)
651 }
652
653 fn check_any(&mut self, kets: &[TokenKind]) -> bool {
655 kets.iter().any(|&k| self.check(k))
656 }
657
658 #[track_caller]
662 fn parse_seq_to_before_tokens<T>(
663 &mut self,
664 kets: &[TokenKind],
665 sep: SeqSep,
666 allow_empty: bool,
667 mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
668 ) -> PResult<'sess, (Box<'ast, [T]>, bool )> {
669 let mut first = true;
670 let mut recovered = false;
671 let mut trailing = false;
672 let mut v = SmallVec::<[T; 8]>::new();
673
674 if !allow_empty {
675 v.push(f(self)?);
676 first = false;
677 }
678
679 while !self.check_any(kets) {
680 if let TokenKind::CloseDelim(..) | TokenKind::Eof = self.token.kind {
681 break;
682 }
683
684 if let Some(sep_kind) = sep.sep {
685 if first {
686 first = false;
688 } else {
689 match self.expect(sep_kind) {
691 Ok(recovered_) => {
692 if recovered_ {
693 recovered = true;
694 break;
695 }
696 }
697 Err(e) => return Err(e),
698 }
699
700 if self.check_any(kets) {
701 trailing = true;
702 break;
703 }
704 }
705 }
706
707 v.push(f(self)?);
708 }
709
710 if let Some(sep_kind) = sep.sep {
711 let open_close_delim = first && allow_empty;
712 if !open_close_delim
713 && sep.trailing_sep_required
714 && !trailing
715 && let Err(e) = self.expect(sep_kind)
716 {
717 e.emit();
718 }
719 if !sep.trailing_sep_allowed && trailing {
720 let msg = format!("trailing `{sep_kind}` separator is not allowed");
721 self.dcx().err(msg).span(self.prev_token.span).emit();
722 }
723 }
724
725 Ok((self.alloc_smallvec(v), recovered))
726 }
727
728 pub fn bump(&mut self) {
730 let next = self.next_token();
731 if next.is_comment_or_doc() {
732 return self.bump_trivia(next);
733 }
734 self.inlined_bump_with(next);
735 }
736
737 pub fn bump_with(&mut self, next: Token) {
743 self.inlined_bump_with(next);
744 }
745
746 #[inline(always)]
748 fn inlined_bump_with(&mut self, next: Token) {
749 #[cfg(debug_assertions)]
750 if next.is_comment_or_doc() {
751 self.dcx().bug("`bump_with` should not be used with comments").span(next.span).emit();
752 }
753 self.prev_token = std::mem::replace(&mut self.token, next);
754 self.expected_tokens.clear();
755 self.docs.clear();
756 }
757
758 #[cold]
762 fn bump_trivia(&mut self, next: Token) {
763 self.docs.clear();
764
765 debug_assert!(next.is_comment_or_doc());
766 self.prev_token = std::mem::replace(&mut self.token, next);
767 while let Some((is_doc, doc)) = self.token.comment() {
768 if is_doc {
769 self.docs.push(doc);
770 }
771 self.token = self.next_token();
773 }
774
775 self.expected_tokens.clear();
776 }
777
778 #[inline(always)]
782 fn next_token(&mut self) -> Token {
783 self.tokens.next().unwrap_or(Token { kind: TokenKind::Eof, span: self.token.span })
784 }
785
786 #[inline]
791 pub fn look_ahead(&self, dist: usize) -> Token {
792 match dist {
794 0 => self.token,
795 1 => self.look_ahead_full(1),
796 2 => self.look_ahead_full(2),
797 dist => self.look_ahead_full(dist),
798 }
799 }
800
801 fn look_ahead_full(&self, dist: usize) -> Token {
802 self.tokens
803 .as_slice()
804 .iter()
805 .copied()
806 .filter(|t| !t.is_comment_or_doc())
807 .nth(dist - 1)
808 .unwrap_or(Token::EOF)
809 }
810
811 #[inline]
815 pub fn look_ahead_with<R>(&self, dist: usize, f: impl FnOnce(Token) -> R) -> R {
816 f(self.look_ahead(dist))
817 }
818
819 #[inline]
821 fn in_contract<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
822 let old = std::mem::replace(&mut self.in_contract, true);
823 let res = f(self);
824 self.in_contract = old;
825 res
826 }
827
828 #[inline]
830 fn in_yul<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
831 let old = std::mem::replace(&mut self.in_yul, true);
832 let res = f(self);
833 self.in_yul = old;
834 res
835 }
836
837 #[inline]
839 pub fn with_recursion_limit<T>(
840 &mut self,
841 context: &str,
842 f: impl FnOnce(&mut Self) -> PResult<'sess, T>,
843 ) -> PResult<'sess, T> {
844 self.recursion_depth = self.recursion_depth.saturating_add(1);
846 if self.recursion_depth > PARSER_RECURSION_LIMIT {
847 let mut err = self.dcx().err("recursion limit reached").span(self.token.span);
848 if !self.prev_token.span.is_dummy() {
850 err = err.span_label(self.prev_token.span, format!("while parsing {context}"));
851 }
852 self.recursion_depth = self.recursion_depth.saturating_sub(1);
854 return Err(err);
855 }
856
857 let res = f(self);
858 self.recursion_depth = self.recursion_depth.saturating_sub(1);
859 res
860 }
861}
862
863impl<'sess, 'ast> Parser<'sess, 'ast> {
865 #[track_caller]
867 pub fn parse_spanned<T>(
868 &mut self,
869 f: impl FnOnce(&mut Self) -> PResult<'sess, T>,
870 ) -> PResult<'sess, (Span, T)> {
871 let lo = self.token.span;
872 let res = f(self);
873 let span = lo.to(self.prev_token.span);
874 match res {
875 Ok(t) => Ok((span, t)),
876 Err(e) if e.span.is_dummy() => Err(e.span(span)),
877 Err(e) => Err(e),
878 }
879 }
880
881 #[inline]
883 pub fn parse_doc_comments(&mut self) -> DocComments<'ast> {
884 if !self.docs.is_empty() { self.parse_doc_comments_inner() } else { Default::default() }
885 }
886
887 #[cold]
888 fn parse_doc_comments_inner(&mut self) -> DocComments<'ast> {
889 let docs = self.arena.alloc_slice_copy(&self.docs);
890 self.docs.clear();
891 docs.into()
892 }
893
894 #[track_caller]
896 pub fn parse_path(&mut self) -> PResult<'sess, AstPath<'ast>> {
897 let first = self.parse_ident()?;
898 self.parse_path_with(first)
899 }
900
901 #[track_caller]
903 pub fn parse_path_with(&mut self, first: Ident) -> PResult<'sess, AstPath<'ast>> {
904 if self.in_yul {
905 self.parse_path_with_f(first, Self::parse_yul_path_ident)
906 } else {
907 self.parse_path_with_f(first, Self::parse_ident)
908 }
909 }
910
911 fn parse_yul_path_ident(&mut self) -> PResult<'sess, Ident> {
913 let ident = self.ident_or_err(true)?;
914 if !ident.is_yul_evm_builtin() && ident.is_reserved(true) {
915 self.expected_ident_found_err().emit();
916 }
917 self.bump();
918 Ok(ident)
919 }
920
921 #[track_caller]
923 pub fn parse_path_any(&mut self) -> PResult<'sess, AstPath<'ast>> {
924 let first = self.parse_ident_any()?;
925 self.parse_path_with_f(first, Self::parse_ident_any)
926 }
927
928 #[track_caller]
930 fn parse_path_with_f(
931 &mut self,
932 first: Ident,
933 mut f: impl FnMut(&mut Self) -> PResult<'sess, Ident>,
934 ) -> PResult<'sess, AstPath<'ast>> {
935 if !self.check_noexpect(TokenKind::Dot) {
936 return Ok(self.alloc_path(&[first]));
937 }
938
939 let mut path = SmallVec::<[_; 4]>::new();
940 path.push(first);
941 while self.eat(TokenKind::Dot) {
942 path.push(f(self)?);
943 }
944 Ok(self.alloc_path(&path))
945 }
946
947 #[track_caller]
949 pub fn parse_ident(&mut self) -> PResult<'sess, Ident> {
950 self.parse_ident_common(true)
951 }
952
953 #[track_caller]
955 pub fn parse_ident_any(&mut self) -> PResult<'sess, Ident> {
956 let ident = self.ident_or_err(true)?;
957 self.bump();
958 Ok(ident)
959 }
960
961 #[track_caller]
963 pub fn parse_ident_opt(&mut self) -> PResult<'sess, Option<Ident>> {
964 if self.check_ident() { self.parse_ident().map(Some) } else { Ok(None) }
965 }
966
967 #[track_caller]
968 fn parse_ident_common(&mut self, recover: bool) -> PResult<'sess, Ident> {
969 let ident = self.ident_or_err(recover)?;
970 if ident.is_reserved(self.in_yul) {
971 let err = self.expected_ident_found_err();
972 if recover {
973 err.emit();
974 } else {
975 return Err(err);
976 }
977 }
978 self.bump();
979 Ok(ident)
980 }
981
982 #[track_caller]
984 fn ident_or_err(&mut self, recover: bool) -> PResult<'sess, Ident> {
985 match self.token.ident() {
986 Some(ident) => Ok(ident),
987 None => self.expected_ident_found(recover),
988 }
989 }
990
991 #[track_caller]
992 fn expected_ident_found(&mut self, recover: bool) -> PResult<'sess, Ident> {
993 self.expected_ident_found_other(self.token, recover)
994 }
995
996 #[track_caller]
997 fn expected_ident_found_other(&mut self, token: Token, recover: bool) -> PResult<'sess, Ident> {
998 let msg = format!("expected identifier, found {}", token.full_description());
999 let span = token.span;
1000 let mut err = self.dcx().err(msg).span(span);
1001
1002 let mut recovered_ident = None;
1003
1004 let suggest_remove_comma = token.kind == TokenKind::Comma && self.look_ahead(1).is_ident();
1005 if suggest_remove_comma {
1006 if recover {
1007 self.bump();
1008 recovered_ident = self.ident_or_err(false).ok();
1009 }
1010 err = err.span_help(span, "remove this comma");
1011 }
1012
1013 if recover && let Some(ident) = recovered_ident {
1014 err.emit();
1015 return Ok(ident);
1016 }
1017 Err(err)
1018 }
1019
1020 #[track_caller]
1021 fn expected_ident_found_err(&mut self) -> PErr<'sess> {
1022 self.expected_ident_found(false).unwrap_err()
1023 }
1024}