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
23pub struct Parser<'sess, 'ast> {
25 pub sess: &'sess Session,
27 pub arena: &'ast ast::Arena,
29
30 pub token: Token,
32 pub prev_token: Token,
34 expected_tokens: Vec<ExpectedToken>,
36 last_unexpected_token_span: Option<Span>,
38 docs: Vec<DocComment>,
40
41 tokens: std::vec::IntoIter<Token>,
43
44 in_yul: bool,
48 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#[derive(Debug)]
92struct SeqSep {
93 sep: Option<TokenKind>,
95 trailing_sep_allowed: bool,
97 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 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 pub fn from_source_code(
141 sess: &'sess Session,
142 arena: &'ast ast::Arena,
143 filename: FileName,
144 src: impl Into<String>,
145 ) -> Result<Self> {
146 Self::from_lazy_source_code(sess, arena, filename, || Ok(src.into()))
147 }
148
149 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 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 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 pub fn from_lexer(arena: &'ast ast::Arena, lexer: Lexer<'sess, '_>) -> Self {
194 Self::new(lexer.sess, arena, lexer.into_tokens())
195 }
196
197 #[inline]
199 pub fn dcx(&self) -> &'sess DiagCtxt {
200 &self.sess.dcx
201 }
202
203 pub fn alloc<T>(&self, value: T) -> Box<'ast, T> {
205 self.arena.alloc(value)
206 }
207
208 pub fn alloc_path(&self, values: &[Ident]) -> AstPath<'ast> {
214 PathSlice::from_mut_slice(self.arena.alloc_slice_copy(values))
215 }
216
217 pub fn alloc_vec<T>(&self, values: Vec<T>) -> Box<'ast, [T]> {
219 self.arena.alloc_vec(values)
220 }
221
222 pub fn alloc_smallvec<A: smallvec::Array>(&self, values: SmallVec<A>) -> Box<'ast, [A::Item]> {
224 self.arena.alloc_smallvec(values)
225 }
226
227 #[inline]
229 #[track_caller]
230 pub fn unexpected<T>(&mut self) -> PResult<'sess, T> {
231 Err(self.unexpected_error())
232 }
233
234 #[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 #[track_caller]
252 pub fn expect(&mut self, tok: TokenKind) -> PResult<'sess, bool > {
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 #[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 self.token.span
272 } else if self.token.is_eof() {
273 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 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 #[track_caller]
299 pub fn expect_one_of(
300 &mut self,
301 edible: &[TokenKind],
302 inedible: &[TokenKind],
303 ) -> PResult<'sess, bool > {
304 if edible.contains(&self.token.kind) {
305 self.bump();
306 Ok(false)
307 } else if inedible.contains(&self.token.kind) {
308 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 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 !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 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 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 #[track_caller]
411 fn expect_semi(&mut self) -> PResult<'sess, ()> {
412 self.expect(TokenKind::Semi).map(drop)
413 }
414
415 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 )> {
643 self.parse_seq_to_before_tokens(&[ket], sep, allow_empty, f)
644 }
645
646 fn check_any(&mut self, kets: &[TokenKind]) -> bool {
648 kets.iter().any(|&k| self.check(k))
649 }
650
651 #[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 )> {
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 first = false;
681 } else {
682 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 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 pub fn bump_with(&mut self, next: Token) {
734 self.inlined_bump_with(next);
735 }
736
737 #[inline(always)]
739 fn inlined_bump_with(&mut self, next: Token) {
740 #[cfg(debug_assertions)]
741 if next.is_comment_or_doc() {
742 self.dcx().bug("`bump_with` should not be used with comments").span(next.span).emit();
743 }
744 self.prev_token = std::mem::replace(&mut self.token, next);
745 self.expected_tokens.clear();
746 self.docs.clear();
747 }
748
749 #[cold]
753 fn bump_trivia(&mut self, next: Token) {
754 self.docs.clear();
755
756 debug_assert!(next.is_comment_or_doc());
757 self.prev_token = std::mem::replace(&mut self.token, next);
758 while let Some((is_doc, doc)) = self.token.comment() {
759 if is_doc {
760 self.docs.push(doc);
761 }
762 self.token = self.next_token();
764 }
765
766 self.expected_tokens.clear();
767 }
768
769 #[inline(always)]
773 fn next_token(&mut self) -> Token {
774 self.tokens.next().unwrap_or(Token { kind: TokenKind::Eof, span: self.token.span })
775 }
776
777 #[inline]
782 pub fn look_ahead(&self, dist: usize) -> Token {
783 match dist {
785 0 => self.token,
786 1 => self.look_ahead_full(1),
787 2 => self.look_ahead_full(2),
788 dist => self.look_ahead_full(dist),
789 }
790 }
791
792 fn look_ahead_full(&self, dist: usize) -> Token {
793 self.tokens
794 .as_slice()
795 .iter()
796 .copied()
797 .filter(|t| !t.is_comment_or_doc())
798 .nth(dist - 1)
799 .unwrap_or(Token::EOF)
800 }
801
802 #[inline]
806 pub fn look_ahead_with<R>(&self, dist: usize, f: impl FnOnce(Token) -> R) -> R {
807 f(self.look_ahead(dist))
808 }
809
810 fn in_contract<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
812 let old = std::mem::replace(&mut self.in_contract, true);
813 let res = f(self);
814 self.in_contract = old;
815 res
816 }
817
818 fn in_yul<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
820 let old = std::mem::replace(&mut self.in_yul, true);
821 let res = f(self);
822 self.in_yul = old;
823 res
824 }
825}
826
827impl<'sess, 'ast> Parser<'sess, 'ast> {
829 #[track_caller]
831 pub fn parse_spanned<T>(
832 &mut self,
833 f: impl FnOnce(&mut Self) -> PResult<'sess, T>,
834 ) -> PResult<'sess, (Span, T)> {
835 let lo = self.token.span;
836 let res = f(self);
837 let span = lo.to(self.prev_token.span);
838 match res {
839 Ok(t) => Ok((span, t)),
840 Err(e) if e.span.is_dummy() => Err(e.span(span)),
841 Err(e) => Err(e),
842 }
843 }
844
845 #[inline]
847 pub fn parse_doc_comments(&mut self) -> DocComments<'ast> {
848 if !self.docs.is_empty() {
849 self.parse_doc_comments_inner()
850 } else {
851 Default::default()
852 }
853 }
854
855 #[cold]
856 fn parse_doc_comments_inner(&mut self) -> DocComments<'ast> {
857 let docs = self.arena.alloc_slice_copy(&self.docs);
858 self.docs.clear();
859 docs.into()
860 }
861
862 #[track_caller]
864 pub fn parse_path(&mut self) -> PResult<'sess, AstPath<'ast>> {
865 let first = self.parse_ident()?;
866 self.parse_path_with(first)
867 }
868
869 #[track_caller]
871 pub fn parse_path_with(&mut self, first: Ident) -> PResult<'sess, AstPath<'ast>> {
872 if self.in_yul {
873 self.parse_path_with_f(first, Self::parse_yul_path_ident)
874 } else {
875 self.parse_path_with_f(first, Self::parse_ident)
876 }
877 }
878
879 fn parse_yul_path_ident(&mut self) -> PResult<'sess, Ident> {
881 let ident = self.ident_or_err(true)?;
882 if !ident.is_yul_evm_builtin() && ident.is_reserved(true) {
883 self.expected_ident_found_err().emit();
884 }
885 self.bump();
886 Ok(ident)
887 }
888
889 #[track_caller]
891 pub fn parse_path_any(&mut self) -> PResult<'sess, AstPath<'ast>> {
892 let first = self.parse_ident_any()?;
893 self.parse_path_with_f(first, Self::parse_ident_any)
894 }
895
896 #[track_caller]
898 fn parse_path_with_f(
899 &mut self,
900 first: Ident,
901 mut f: impl FnMut(&mut Self) -> PResult<'sess, Ident>,
902 ) -> PResult<'sess, AstPath<'ast>> {
903 if !self.check_noexpect(TokenKind::Dot) {
904 return Ok(self.alloc_path(&[first]));
905 }
906
907 let mut path = SmallVec::<[_; 4]>::new();
908 path.push(first);
909 while self.eat(TokenKind::Dot) {
910 path.push(f(self)?);
911 }
912 Ok(self.alloc_path(&path))
913 }
914
915 #[track_caller]
917 pub fn parse_ident(&mut self) -> PResult<'sess, Ident> {
918 self.parse_ident_common(true)
919 }
920
921 #[track_caller]
923 pub fn parse_ident_any(&mut self) -> PResult<'sess, Ident> {
924 let ident = self.ident_or_err(true)?;
925 self.bump();
926 Ok(ident)
927 }
928
929 #[track_caller]
931 pub fn parse_ident_opt(&mut self) -> PResult<'sess, Option<Ident>> {
932 if self.check_ident() {
933 self.parse_ident().map(Some)
934 } else {
935 Ok(None)
936 }
937 }
938
939 #[track_caller]
940 fn parse_ident_common(&mut self, recover: bool) -> PResult<'sess, Ident> {
941 let ident = self.ident_or_err(recover)?;
942 if ident.is_reserved(self.in_yul) {
943 let err = self.expected_ident_found_err();
944 if recover {
945 err.emit();
946 } else {
947 return Err(err);
948 }
949 }
950 self.bump();
951 Ok(ident)
952 }
953
954 #[track_caller]
956 fn ident_or_err(&mut self, recover: bool) -> PResult<'sess, Ident> {
957 match self.token.ident() {
958 Some(ident) => Ok(ident),
959 None => self.expected_ident_found(recover),
960 }
961 }
962
963 #[track_caller]
964 fn expected_ident_found(&mut self, recover: bool) -> PResult<'sess, Ident> {
965 self.expected_ident_found_other(self.token, recover)
966 }
967
968 #[track_caller]
969 fn expected_ident_found_other(&mut self, token: Token, recover: bool) -> PResult<'sess, Ident> {
970 let msg = format!("expected identifier, found {}", token.full_description());
971 let span = token.span;
972 let mut err = self.dcx().err(msg).span(span);
973
974 let mut recovered_ident = None;
975
976 let suggest_remove_comma = token.kind == TokenKind::Comma && self.look_ahead(1).is_ident();
977 if suggest_remove_comma {
978 if recover {
979 self.bump();
980 recovered_ident = self.ident_or_err(false).ok();
981 }
982 err = err.span_help(span, "remove this comma");
983 }
984
985 if recover {
986 if let Some(ident) = recovered_ident {
987 err.emit();
988 return Ok(ident);
989 }
990 }
991 Err(err)
992 }
993
994 #[track_caller]
995 fn expected_ident_found_err(&mut self) -> PErr<'sess> {
996 self.expected_ident_found(false).unwrap_err()
997 }
998}