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
22pub struct Parser<'sess, 'ast> {
24 pub sess: &'sess Session,
26 pub arena: &'ast ast::Arena,
28
29 pub token: Token,
31 pub prev_token: Token,
33 expected_tokens: Vec<ExpectedToken>,
35 last_unexpected_token_span: Option<Span>,
37 docs: Vec<DocComment>,
39
40 tokens: std::vec::IntoIter<Token>,
42
43 in_yul: bool,
47 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#[derive(Debug)]
91struct SeqSep {
92 sep: Option<TokenKind>,
94 trailing_sep_allowed: bool,
96 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 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 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 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 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 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 pub fn from_lexer(arena: &'ast ast::Arena, lexer: Lexer<'sess, '_>) -> Self {
193 Self::new(lexer.sess, arena, lexer.into_tokens())
194 }
195
196 #[inline]
198 pub fn dcx(&self) -> &'sess DiagCtxt {
199 &self.sess.dcx
200 }
201
202 pub fn alloc<T>(&self, value: T) -> Box<'ast, T> {
204 self.arena.alloc(value)
205 }
206
207 pub fn alloc_path(&self, values: &[Ident]) -> AstPath<'ast> {
213 PathSlice::from_mut_slice(self.arena.alloc_slice_copy(values))
214 }
215
216 pub fn alloc_vec<T>(&self, values: Vec<T>) -> Box<'ast, [T]> {
218 self.arena.alloc_vec(values)
219 }
220
221 pub fn alloc_smallvec<A: smallvec::Array>(&self, values: SmallVec<A>) -> Box<'ast, [A::Item]> {
223 self.arena.alloc_smallvec(values)
224 }
225
226 #[inline]
228 #[track_caller]
229 pub fn unexpected<T>(&mut self) -> PResult<'sess, T> {
230 Err(self.unexpected_error())
231 }
232
233 #[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 #[track_caller]
251 pub fn expect(&mut self, tok: TokenKind) -> PResult<'sess, bool > {
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 #[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 self.token.span
271 } else if self.token.is_eof() {
272 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 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 #[track_caller]
298 pub fn expect_one_of(
299 &mut self,
300 edible: &[TokenKind],
301 inedible: &[TokenKind],
302 ) -> PResult<'sess, bool > {
303 if edible.contains(&self.token.kind) {
304 self.bump();
305 Ok(false)
306 } else if inedible.contains(&self.token.kind) {
307 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 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 !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 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 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 #[track_caller]
410 fn expect_semi(&mut self) -> PResult<'sess, ()> {
411 self.expect(TokenKind::Semi).map(drop)
412 }
413
414 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 )> {
638 self.parse_seq_to_before_tokens(&[ket], sep, allow_empty, f)
639 }
640
641 fn check_any(&mut self, kets: &[TokenKind]) -> bool {
643 kets.iter().any(|&k| self.check(k))
644 }
645
646 #[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 )> {
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 first = false;
676 } else {
677 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 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 pub fn bump_with(&mut self, next: Token) {
731 self.inlined_bump_with(next);
732 }
733
734 #[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 #[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 self.token = self.next_token();
761 }
762
763 self.expected_tokens.clear();
764 }
765
766 #[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 #[inline]
779 pub fn look_ahead(&self, dist: usize) -> Token {
780 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 #[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 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 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
824impl<'sess, 'ast> Parser<'sess, 'ast> {
826 #[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 #[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 #[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 #[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 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 #[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 #[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 #[track_caller]
910 pub fn parse_ident(&mut self) -> PResult<'sess, Ident> {
911 self.parse_ident_common(true)
912 }
913
914 #[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 #[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 #[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}