rustc_parse/parser/
diagnostics.rs

1use std::mem::take;
2use std::ops::{Deref, DerefMut};
3
4use ast::token::IdentIsRaw;
5use rustc_ast as ast;
6use rustc_ast::ptr::P;
7use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind};
8use rustc_ast::util::parser::AssocOp;
9use rustc_ast::{
10    AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
11    BlockCheckMode, Expr, ExprKind, GenericArg, Generics, Item, ItemKind, Param, Pat, PatKind,
12    Path, PathSegment, QSelf, Recovered, Ty, TyKind,
13};
14use rustc_ast_pretty::pprust;
15use rustc_data_structures::fx::FxHashSet;
16use rustc_errors::{
17    Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions,
18    pluralize,
19};
20use rustc_session::errors::ExprParenthesesNeeded;
21use rustc_span::edit_distance::find_best_match_for_name;
22use rustc_span::source_map::Spanned;
23use rustc_span::symbol::used_keywords;
24use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym};
25use thin_vec::{ThinVec, thin_vec};
26use tracing::{debug, trace};
27
28use super::pat::Expected;
29use super::{
30    BlockMode, CommaRecoveryMode, ExpTokenPair, Parser, PathStyle, Restrictions, SemiColonMode,
31    SeqSep, TokenType,
32};
33use crate::errors::{
34    AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType,
35    AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
36    ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
37    ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything,
38    DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
39    GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
40    HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,
41    IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody,
42    QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
43    StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
44    TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
45    UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
46};
47use crate::parser::attr::InnerAttrPolicy;
48use crate::{exp, fluent_generated as fluent};
49
50/// Creates a placeholder argument.
51pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param {
52    let pat = P(Pat {
53        id: ast::DUMMY_NODE_ID,
54        kind: PatKind::Ident(BindingMode::NONE, ident, None),
55        span: ident.span,
56        tokens: None,
57    });
58    let ty = Ty { kind: TyKind::Err(guar), span: ident.span, id: ast::DUMMY_NODE_ID, tokens: None };
59    Param {
60        attrs: AttrVec::default(),
61        id: ast::DUMMY_NODE_ID,
62        pat,
63        span: ident.span,
64        ty: P(ty),
65        is_placeholder: false,
66    }
67}
68
69pub(super) trait RecoverQPath: Sized + 'static {
70    const PATH_STYLE: PathStyle = PathStyle::Expr;
71    fn to_ty(&self) -> Option<P<Ty>>;
72    fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self;
73}
74
75impl RecoverQPath for Ty {
76    const PATH_STYLE: PathStyle = PathStyle::Type;
77    fn to_ty(&self) -> Option<P<Ty>> {
78        Some(P(self.clone()))
79    }
80    fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
81        Self {
82            span: path.span,
83            kind: TyKind::Path(qself, path),
84            id: ast::DUMMY_NODE_ID,
85            tokens: None,
86        }
87    }
88}
89
90impl RecoverQPath for Pat {
91    const PATH_STYLE: PathStyle = PathStyle::Pat;
92    fn to_ty(&self) -> Option<P<Ty>> {
93        self.to_ty()
94    }
95    fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
96        Self {
97            span: path.span,
98            kind: PatKind::Path(qself, path),
99            id: ast::DUMMY_NODE_ID,
100            tokens: None,
101        }
102    }
103}
104
105impl RecoverQPath for Expr {
106    fn to_ty(&self) -> Option<P<Ty>> {
107        self.to_ty()
108    }
109    fn recovered(qself: Option<P<QSelf>>, path: ast::Path) -> Self {
110        Self {
111            span: path.span,
112            kind: ExprKind::Path(qself, path),
113            attrs: AttrVec::new(),
114            id: ast::DUMMY_NODE_ID,
115            tokens: None,
116        }
117    }
118}
119
120/// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`.
121pub(crate) enum ConsumeClosingDelim {
122    Yes,
123    No,
124}
125
126#[derive(Clone, Copy)]
127pub enum AttemptLocalParseRecovery {
128    Yes,
129    No,
130}
131
132impl AttemptLocalParseRecovery {
133    pub(super) fn yes(&self) -> bool {
134        match self {
135            AttemptLocalParseRecovery::Yes => true,
136            AttemptLocalParseRecovery::No => false,
137        }
138    }
139
140    pub(super) fn no(&self) -> bool {
141        match self {
142            AttemptLocalParseRecovery::Yes => false,
143            AttemptLocalParseRecovery::No => true,
144        }
145    }
146}
147
148/// Information for emitting suggestions and recovering from
149/// C-style `i++`, `--i`, etc.
150#[derive(Debug, Copy, Clone)]
151struct IncDecRecovery {
152    /// Is this increment/decrement its own statement?
153    standalone: IsStandalone,
154    /// Is this an increment or decrement?
155    op: IncOrDec,
156    /// Is this pre- or postfix?
157    fixity: UnaryFixity,
158}
159
160/// Is an increment or decrement expression its own statement?
161#[derive(Debug, Copy, Clone)]
162enum IsStandalone {
163    /// It's standalone, i.e., its own statement.
164    Standalone,
165    /// It's a subexpression, i.e., *not* standalone.
166    Subexpr,
167}
168
169#[derive(Debug, Copy, Clone, PartialEq, Eq)]
170enum IncOrDec {
171    Inc,
172    Dec,
173}
174
175#[derive(Debug, Copy, Clone, PartialEq, Eq)]
176enum UnaryFixity {
177    Pre,
178    Post,
179}
180
181impl IncOrDec {
182    fn chr(&self) -> char {
183        match self {
184            Self::Inc => '+',
185            Self::Dec => '-',
186        }
187    }
188
189    fn name(&self) -> &'static str {
190        match self {
191            Self::Inc => "increment",
192            Self::Dec => "decrement",
193        }
194    }
195}
196
197impl std::fmt::Display for UnaryFixity {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        match self {
200            Self::Pre => write!(f, "prefix"),
201            Self::Post => write!(f, "postfix"),
202        }
203    }
204}
205
206#[derive(Debug, rustc_macros::Subdiagnostic)]
207#[suggestion(
208    parse_misspelled_kw,
209    applicability = "machine-applicable",
210    code = "{similar_kw}",
211    style = "verbose"
212)]
213struct MisspelledKw {
214    similar_kw: String,
215    #[primary_span]
216    span: Span,
217    is_incorrect_case: bool,
218}
219
220/// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`.
221fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> {
222    let lowercase = lookup.name.as_str().to_lowercase();
223    let lowercase_sym = Symbol::intern(&lowercase);
224    if candidates.contains(&lowercase_sym) {
225        Some(MisspelledKw { similar_kw: lowercase, span: lookup.span, is_incorrect_case: true })
226    } else if let Some(similar_sym) = find_best_match_for_name(candidates, lookup.name, None) {
227        Some(MisspelledKw {
228            similar_kw: similar_sym.to_string(),
229            span: lookup.span,
230            is_incorrect_case: false,
231        })
232    } else {
233        None
234    }
235}
236
237struct MultiSugg {
238    msg: String,
239    patches: Vec<(Span, String)>,
240    applicability: Applicability,
241}
242
243impl MultiSugg {
244    fn emit(self, err: &mut Diag<'_>) {
245        err.multipart_suggestion(self.msg, self.patches, self.applicability);
246    }
247
248    fn emit_verbose(self, err: &mut Diag<'_>) {
249        err.multipart_suggestion_verbose(self.msg, self.patches, self.applicability);
250    }
251}
252
253/// SnapshotParser is used to create a snapshot of the parser
254/// without causing duplicate errors being emitted when the `Parser`
255/// is dropped.
256pub struct SnapshotParser<'a> {
257    parser: Parser<'a>,
258}
259
260impl<'a> Deref for SnapshotParser<'a> {
261    type Target = Parser<'a>;
262
263    fn deref(&self) -> &Self::Target {
264        &self.parser
265    }
266}
267
268impl<'a> DerefMut for SnapshotParser<'a> {
269    fn deref_mut(&mut self) -> &mut Self::Target {
270        &mut self.parser
271    }
272}
273
274impl<'a> Parser<'a> {
275    pub fn dcx(&self) -> DiagCtxtHandle<'a> {
276        self.psess.dcx()
277    }
278
279    /// Replace `self` with `snapshot.parser`.
280    pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
281        *self = snapshot.parser;
282    }
283
284    /// Create a snapshot of the `Parser`.
285    pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {
286        let snapshot = self.clone();
287        SnapshotParser { parser: snapshot }
288    }
289
290    pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
291        self.psess.source_map().span_to_snippet(span)
292    }
293
294    /// Emits an error with suggestions if an identifier was expected but not found.
295    ///
296    /// Returns a possibly recovered identifier.
297    pub(super) fn expected_ident_found(
298        &mut self,
299        recover: bool,
300    ) -> PResult<'a, (Ident, IdentIsRaw)> {
301        let valid_follow = &[
302            TokenKind::Eq,
303            TokenKind::Colon,
304            TokenKind::Comma,
305            TokenKind::Semi,
306            TokenKind::PathSep,
307            TokenKind::OpenDelim(Delimiter::Brace),
308            TokenKind::OpenDelim(Delimiter::Parenthesis),
309            TokenKind::CloseDelim(Delimiter::Brace),
310            TokenKind::CloseDelim(Delimiter::Parenthesis),
311        ];
312        if let TokenKind::DocComment(..) = self.prev_token.kind
313            && valid_follow.contains(&self.token.kind)
314        {
315            let err = self.dcx().create_err(DocCommentDoesNotDocumentAnything {
316                span: self.prev_token.span,
317                missing_comma: None,
318            });
319            return Err(err);
320        }
321
322        let mut recovered_ident = None;
323        // we take this here so that the correct original token is retained in
324        // the diagnostic, regardless of eager recovery.
325        let bad_token = self.token.clone();
326
327        // suggest prepending a keyword in identifier position with `r#`
328        let suggest_raw = if let Some((ident, IdentIsRaw::No)) = self.token.ident()
329            && ident.is_raw_guess()
330            && self.look_ahead(1, |t| valid_follow.contains(&t.kind))
331        {
332            recovered_ident = Some((ident, IdentIsRaw::Yes));
333
334            // `Symbol::to_string()` is different from `Symbol::into_diag_arg()`,
335            // which uses `Symbol::to_ident_string()` and "helpfully" adds an implicit `r#`
336            let ident_name = ident.name.to_string();
337
338            Some(SuggEscapeIdentifier { span: ident.span.shrink_to_lo(), ident_name })
339        } else {
340            None
341        };
342
343        let suggest_remove_comma =
344            if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {
345                if recover {
346                    self.bump();
347                    recovered_ident = self.ident_or_err(false).ok();
348                };
349
350                Some(SuggRemoveComma { span: bad_token.span })
351            } else {
352                None
353            };
354
355        let help_cannot_start_number = self.is_lit_bad_ident().map(|(len, valid_portion)| {
356            let (invalid, valid) = self.token.span.split_at(len as u32);
357
358            recovered_ident = Some((Ident::new(valid_portion, valid), IdentIsRaw::No));
359
360            HelpIdentifierStartsWithNumber { num_span: invalid }
361        });
362
363        let err = ExpectedIdentifier {
364            span: bad_token.span,
365            token: bad_token,
366            suggest_raw,
367            suggest_remove_comma,
368            help_cannot_start_number,
369        };
370        let mut err = self.dcx().create_err(err);
371
372        // if the token we have is a `<`
373        // it *might* be a misplaced generic
374        // FIXME: could we recover with this?
375        if self.token == token::Lt {
376            // all keywords that could have generic applied
377            let valid_prev_keywords =
378                [kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait];
379
380            // If we've expected an identifier,
381            // and the current token is a '<'
382            // if the previous token is a valid keyword
383            // that might use a generic, then suggest a correct
384            // generic placement (later on)
385            let maybe_keyword = self.prev_token.clone();
386            if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) {
387                // if we have a valid keyword, attempt to parse generics
388                // also obtain the keywords symbol
389                match self.parse_generics() {
390                    Ok(generic) => {
391                        if let TokenKind::Ident(symbol, _) = maybe_keyword.kind {
392                            let ident_name = symbol;
393                            // at this point, we've found something like
394                            // `fn <T>id`
395                            // and current token should be Ident with the item name (i.e. the function name)
396                            // if there is a `<` after the fn name, then don't show a suggestion, show help
397
398                            if !self.look_ahead(1, |t| *t == token::Lt)
399                                && let Ok(snippet) =
400                                    self.psess.source_map().span_to_snippet(generic.span)
401                            {
402                                err.multipart_suggestion_verbose(
403                                        format!("place the generic parameter name after the {ident_name} name"),
404                                        vec![
405                                            (self.token.span.shrink_to_hi(), snippet),
406                                            (generic.span, String::new())
407                                        ],
408                                        Applicability::MaybeIncorrect,
409                                    );
410                            } else {
411                                err.help(format!(
412                                    "place the generic parameter name after the {ident_name} name"
413                                ));
414                            }
415                        }
416                    }
417                    Err(err) => {
418                        // if there's an error parsing the generics,
419                        // then don't do a misplaced generics suggestion
420                        // and emit the expected ident error instead;
421                        err.cancel();
422                    }
423                }
424            }
425        }
426
427        if let Some(recovered_ident) = recovered_ident
428            && recover
429        {
430            err.emit();
431            Ok(recovered_ident)
432        } else {
433            Err(err)
434        }
435    }
436
437    pub(super) fn expected_ident_found_err(&mut self) -> Diag<'a> {
438        self.expected_ident_found(false).unwrap_err()
439    }
440
441    /// Checks if the current token is a integer or float literal and looks like
442    /// it could be a invalid identifier with digits at the start.
443    ///
444    /// Returns the number of characters (bytes) composing the invalid portion
445    /// of the identifier and the valid portion of the identifier.
446    pub(super) fn is_lit_bad_ident(&mut self) -> Option<(usize, Symbol)> {
447        // ensure that the integer literal is followed by a *invalid*
448        // suffix: this is how we know that it is a identifier with an
449        // invalid beginning.
450        if let token::Literal(Lit {
451            kind: token::LitKind::Integer | token::LitKind::Float,
452            symbol,
453            suffix: Some(suffix), // no suffix makes it a valid literal
454        }) = self.token.kind
455            && rustc_ast::MetaItemLit::from_token(&self.token).is_none()
456        {
457            Some((symbol.as_str().len(), suffix))
458        } else {
459            None
460        }
461    }
462
463    pub(super) fn expected_one_of_not_found(
464        &mut self,
465        edible: &[ExpTokenPair<'_>],
466        inedible: &[ExpTokenPair<'_>],
467    ) -> PResult<'a, ErrorGuaranteed> {
468        debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible);
469        fn tokens_to_string(tokens: &[TokenType]) -> String {
470            let mut i = tokens.iter();
471            // This might be a sign we need a connect method on `Iterator`.
472            let b = i.next().map_or_else(String::new, |t| t.to_string());
473            i.enumerate().fold(b, |mut b, (i, a)| {
474                if tokens.len() > 2 && i == tokens.len() - 2 {
475                    b.push_str(", or ");
476                } else if tokens.len() == 2 && i == tokens.len() - 2 {
477                    b.push_str(" or ");
478                } else {
479                    b.push_str(", ");
480                }
481                b.push_str(&a.to_string());
482                b
483            })
484        }
485
486        for exp in edible.iter().chain(inedible.iter()) {
487            self.expected_token_types.insert(exp.token_type);
488        }
489        let mut expected: Vec<_> = self.expected_token_types.iter().collect();
490        expected.sort_by_cached_key(|x| x.to_string());
491        expected.dedup();
492
493        let sm = self.psess.source_map();
494
495        // Special-case "expected `;`" errors.
496        if expected.contains(&TokenType::Semi) {
497            // If the user is trying to write a ternary expression, recover it and
498            // return an Err to prevent a cascade of irrelevant diagnostics.
499            if self.prev_token == token::Question
500                && let Err(e) = self.maybe_recover_from_ternary_operator()
501            {
502                return Err(e);
503            }
504
505            if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {
506                // Likely inside a macro, can't provide meaningful suggestions.
507            } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
508                // The current token is in the same line as the prior token, not recoverable.
509            } else if [token::Comma, token::Colon].contains(&self.token.kind)
510                && self.prev_token == token::CloseDelim(Delimiter::Parenthesis)
511            {
512                // Likely typo: The current token is on a new line and is expected to be
513                // `.`, `;`, `?`, or an operator after a close delimiter token.
514                //
515                // let a = std::process::Command::new("echo")
516                //         .arg("1")
517                //         ,arg("2")
518                //         ^
519                // https://github.com/rust-lang/rust/issues/72253
520            } else if self.look_ahead(1, |t| {
521                t == &token::CloseDelim(Delimiter::Brace)
522                    || t.can_begin_expr() && *t != token::Colon
523            }) && [token::Comma, token::Colon].contains(&self.token.kind)
524            {
525                // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
526                // either `,` or `:`, and the next token could either start a new statement or is a
527                // block close. For example:
528                //
529                //   let x = 32:
530                //   let y = 42;
531                let guar = self.dcx().emit_err(ExpectedSemi {
532                    span: self.token.span,
533                    token: self.token.clone(),
534                    unexpected_token_label: None,
535                    sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
536                });
537                self.bump();
538                return Ok(guar);
539            } else if self.look_ahead(0, |t| {
540                t == &token::CloseDelim(Delimiter::Brace)
541                    || ((t.can_begin_expr() || t.can_begin_item())
542                        && t != &token::Semi
543                        && t != &token::Pound)
544                    // Avoid triggering with too many trailing `#` in raw string.
545                    || (sm.is_multiline(
546                        self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
547                    ) && t == &token::Pound)
548            }) && !expected.contains(&TokenType::Comma)
549            {
550                // Missing semicolon typo. This is triggered if the next token could either start a
551                // new statement or is a block close. For example:
552                //
553                //   let x = 32
554                //   let y = 42;
555                let span = self.prev_token.span.shrink_to_hi();
556                let guar = self.dcx().emit_err(ExpectedSemi {
557                    span,
558                    token: self.token.clone(),
559                    unexpected_token_label: Some(self.token.span),
560                    sugg: ExpectedSemiSugg::AddSemi(span),
561                });
562                return Ok(guar);
563            }
564        }
565
566        if self.token == TokenKind::EqEq
567            && self.prev_token.is_ident()
568            && expected.contains(&TokenType::Eq)
569        {
570            // Likely typo: `=` → `==` in let expr or enum item
571            return Err(self.dcx().create_err(UseEqInstead { span: self.token.span }));
572        }
573
574        if (self.token.is_keyword(kw::Move) || self.token.is_keyword(kw::Use))
575            && self.prev_token.is_keyword(kw::Async)
576        {
577            // The 2015 edition is in use because parsing of `async move` or `async use` has failed.
578            let span = self.prev_token.span.to(self.token.span);
579            if self.token.is_keyword(kw::Move) {
580                return Err(self.dcx().create_err(AsyncMoveBlockIn2015 { span }));
581            } else {
582                // kw::Use
583                return Err(self.dcx().create_err(AsyncUseBlockIn2015 { span }));
584            }
585        }
586
587        let expect = tokens_to_string(&expected);
588        let actual = super::token_descr(&self.token);
589        let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
590            let fmt = format!("expected one of {expect}, found {actual}");
591            let short_expect = if expected.len() > 6 {
592                format!("{} possible tokens", expected.len())
593            } else {
594                expect
595            };
596            (fmt, (self.prev_token.span.shrink_to_hi(), format!("expected one of {short_expect}")))
597        } else if expected.is_empty() {
598            (
599                format!("unexpected token: {actual}"),
600                (self.prev_token.span, "unexpected token after this".to_string()),
601            )
602        } else {
603            (
604                format!("expected {expect}, found {actual}"),
605                (self.prev_token.span.shrink_to_hi(), format!("expected {expect}")),
606            )
607        };
608        self.last_unexpected_token_span = Some(self.token.span);
609        // FIXME: translation requires list formatting (for `expect`)
610        let mut err = self.dcx().struct_span_err(self.token.span, msg_exp);
611
612        // Look for usages of '=>' where '>=' was probably intended
613        if self.token == token::FatArrow
614            && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le))
615            && !expected.iter().any(|tok| matches!(tok, TokenType::FatArrow | TokenType::Comma))
616        {
617            err.span_suggestion(
618                self.token.span,
619                "you might have meant to write a \"greater than or equal to\" comparison",
620                ">=",
621                Applicability::MaybeIncorrect,
622            );
623        }
624
625        if let TokenKind::Ident(symbol, _) = &self.prev_token.kind {
626            if ["def", "fun", "func", "function"].contains(&symbol.as_str()) {
627                err.span_suggestion_short(
628                    self.prev_token.span,
629                    format!("write `fn` instead of `{symbol}` to declare a function"),
630                    "fn",
631                    Applicability::MachineApplicable,
632                );
633            }
634        }
635
636        if let TokenKind::Ident(prev, _) = &self.prev_token.kind
637            && let TokenKind::Ident(cur, _) = &self.token.kind
638        {
639            let concat = Symbol::intern(&format!("{prev}{cur}"));
640            let ident = Ident::new(concat, DUMMY_SP);
641            if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() {
642                let concat_span = self.prev_token.span.to(self.token.span);
643                err.span_suggestion_verbose(
644                    concat_span,
645                    format!("consider removing the space to spell keyword `{concat}`"),
646                    concat,
647                    Applicability::MachineApplicable,
648                );
649            }
650        }
651
652        // Try to detect an intended c-string literal while using a pre-2021 edition. The heuristic
653        // here is to identify a cooked, uninterpolated `c` id immediately followed by a string, or
654        // a cooked, uninterpolated `cr` id immediately followed by a string or a `#`, in an edition
655        // where c-string literals are not allowed. There is the very slight possibility of a false
656        // positive for a `cr#` that wasn't intended to start a c-string literal, but identifying
657        // that in the parser requires unbounded lookahead, so we only add a hint to the existing
658        // error rather than replacing it entirely.
659        if ((self.prev_token == TokenKind::Ident(sym::c, IdentIsRaw::No)
660            && matches!(&self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. })))
661            || (self.prev_token == TokenKind::Ident(sym::cr, IdentIsRaw::No)
662                && matches!(
663                    &self.token.kind,
664                    TokenKind::Literal(token::Lit { kind: token::Str, .. }) | token::Pound
665                )))
666            && self.prev_token.span.hi() == self.token.span.lo()
667            && !self.token.span.at_least_rust_2021()
668        {
669            err.note("you may be trying to write a c-string literal");
670            err.note("c-string literals require Rust 2021 or later");
671            err.subdiagnostic(HelpUseLatestEdition::new());
672        }
673
674        // `pub` may be used for an item or `pub(crate)`
675        if self.prev_token.is_ident_named(sym::public)
676            && (self.token.can_begin_item()
677                || self.token == TokenKind::OpenDelim(Delimiter::Parenthesis))
678        {
679            err.span_suggestion_short(
680                self.prev_token.span,
681                "write `pub` instead of `public` to make the item public",
682                "pub",
683                Applicability::MachineApplicable,
684            );
685        }
686
687        if let token::DocComment(kind, style, _) = self.token.kind {
688            // We have something like `expr //!val` where the user likely meant `expr // !val`
689            let pos = self.token.span.lo() + BytePos(2);
690            let span = self.token.span.with_lo(pos).with_hi(pos);
691            err.span_suggestion_verbose(
692                span,
693                format!(
694                    "add a space before {} to write a regular comment",
695                    match (kind, style) {
696                        (token::CommentKind::Line, ast::AttrStyle::Inner) => "`!`",
697                        (token::CommentKind::Block, ast::AttrStyle::Inner) => "`!`",
698                        (token::CommentKind::Line, ast::AttrStyle::Outer) => "the last `/`",
699                        (token::CommentKind::Block, ast::AttrStyle::Outer) => "the last `*`",
700                    },
701                ),
702                " ".to_string(),
703                Applicability::MachineApplicable,
704            );
705        }
706
707        let sp = if self.token == token::Eof {
708            // This is EOF; don't want to point at the following char, but rather the last token.
709            self.prev_token.span
710        } else {
711            label_sp
712        };
713
714        if self.check_too_many_raw_str_terminators(&mut err) {
715            if expected.contains(&TokenType::Semi) && self.eat(exp!(Semi)) {
716                let guar = err.emit();
717                return Ok(guar);
718            } else {
719                return Err(err);
720            }
721        }
722
723        if self.prev_token.span == DUMMY_SP {
724            // Account for macro context where the previous span might not be
725            // available to avoid incorrect output (#54841).
726            err.span_label(self.token.span, label_exp);
727        } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) {
728            // When the spans are in the same line, it means that the only content between
729            // them is whitespace, point at the found token in that case:
730            //
731            // X |     () => { syntax error };
732            //   |                    ^^^^^ expected one of 8 possible tokens here
733            //
734            // instead of having:
735            //
736            // X |     () => { syntax error };
737            //   |                   -^^^^^ unexpected token
738            //   |                   |
739            //   |                   expected one of 8 possible tokens here
740            err.span_label(self.token.span, label_exp);
741        } else {
742            err.span_label(sp, label_exp);
743            err.span_label(self.token.span, "unexpected token");
744        }
745
746        // Check for misspelled keywords if there are no suggestions added to the diagnostic.
747        if matches!(&err.suggestions, Suggestions::Enabled(list) if list.is_empty()) {
748            self.check_for_misspelled_kw(&mut err, &expected);
749        }
750        Err(err)
751    }
752
753    /// Checks if the current token or the previous token are misspelled keywords
754    /// and adds a helpful suggestion.
755    fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) {
756        let Some((curr_ident, _)) = self.token.ident() else {
757            return;
758        };
759        let expected_token_types: &[TokenType] =
760            expected.len().checked_sub(10).map_or(&expected, |index| &expected[index..]);
761        let expected_keywords: Vec<Symbol> =
762            expected_token_types.iter().filter_map(|token| token.is_keyword()).collect();
763
764        // When there are a few keywords in the last ten elements of `self.expected_token_types`
765        // and the current token is an identifier, it's probably a misspelled keyword. This handles
766        // code like `async Move {}`, misspelled `if` in match guard, misspelled `else` in
767        // `if`-`else` and misspelled `where` in a where clause.
768        if !expected_keywords.is_empty()
769            && !curr_ident.is_used_keyword()
770            && let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords)
771        {
772            err.subdiagnostic(misspelled_kw);
773            // We don't want other suggestions to be added as they are most likely meaningless
774            // when there is a misspelled keyword.
775            err.seal_suggestions();
776        } else if let Some((prev_ident, _)) = self.prev_token.ident()
777            && !prev_ident.is_used_keyword()
778        {
779            // We generate a list of all keywords at runtime rather than at compile time
780            // so that it gets generated only when the diagnostic needs it.
781            // Also, it is unlikely that this list is generated multiple times because the
782            // parser halts after execution hits this path.
783            let all_keywords = used_keywords(|| prev_ident.span.edition());
784
785            // Otherwise, check the previous token with all the keywords as possible candidates.
786            // This handles code like `Struct Human;` and `While a < b {}`.
787            // We check the previous token only when the current token is an identifier to avoid
788            // false positives like suggesting keyword `for` for `extern crate foo {}`.
789            if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) {
790                err.subdiagnostic(misspelled_kw);
791                // We don't want other suggestions to be added as they are most likely meaningless
792                // when there is a misspelled keyword.
793                err.seal_suggestions();
794            }
795        }
796    }
797
798    /// The user has written `#[attr] expr` which is unsupported. (#106020)
799    pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) -> ErrorGuaranteed {
800        // Missing semicolon typo error.
801        let span = self.prev_token.span.shrink_to_hi();
802        let mut err = self.dcx().create_err(ExpectedSemi {
803            span,
804            token: self.token.clone(),
805            unexpected_token_label: Some(self.token.span),
806            sugg: ExpectedSemiSugg::AddSemi(span),
807        });
808        let attr_span = match &expr.attrs[..] {
809            [] => unreachable!(),
810            [only] => only.span,
811            [first, rest @ ..] => {
812                for attr in rest {
813                    err.span_label(attr.span, "");
814                }
815                first.span
816            }
817        };
818        err.span_label(
819            attr_span,
820            format!(
821                "only `;` terminated statements or tail expressions are allowed after {}",
822                if expr.attrs.len() == 1 { "this attribute" } else { "these attributes" },
823            ),
824        );
825        if self.token == token::Pound
826            && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
827        {
828            // We have
829            // #[attr]
830            // expr
831            // #[not_attr]
832            // other_expr
833            err.span_label(span, "expected `;` here");
834            err.multipart_suggestion(
835                "alternatively, consider surrounding the expression with a block",
836                vec![
837                    (expr.span.shrink_to_lo(), "{ ".to_string()),
838                    (expr.span.shrink_to_hi(), " }".to_string()),
839                ],
840                Applicability::MachineApplicable,
841            );
842
843            // Special handling for `#[cfg(...)]` chains
844            let mut snapshot = self.create_snapshot_for_diagnostic();
845            if let [attr] = &expr.attrs[..]
846                && let ast::AttrKind::Normal(attr_kind) = &attr.kind
847                && let [segment] = &attr_kind.item.path.segments[..]
848                && segment.ident.name == sym::cfg
849                && let Some(args_span) = attr_kind.item.args.span()
850                && let next_attr = match snapshot.parse_attribute(InnerAttrPolicy::Forbidden(None))
851                {
852                    Ok(next_attr) => next_attr,
853                    Err(inner_err) => {
854                        inner_err.cancel();
855                        return err.emit();
856                    }
857                }
858                && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind
859                && let Some(next_attr_args_span) = next_attr_kind.item.args.span()
860                && let [next_segment] = &next_attr_kind.item.path.segments[..]
861                && segment.ident.name == sym::cfg
862            {
863                let next_expr = match snapshot.parse_expr() {
864                    Ok(next_expr) => next_expr,
865                    Err(inner_err) => {
866                        inner_err.cancel();
867                        return err.emit();
868                    }
869                };
870                // We have for sure
871                // #[cfg(..)]
872                // expr
873                // #[cfg(..)]
874                // other_expr
875                // So we suggest using `if cfg!(..) { expr } else if cfg!(..) { other_expr }`.
876                let margin = self.psess.source_map().span_to_margin(next_expr.span).unwrap_or(0);
877                let sugg = vec![
878                    (attr.span.with_hi(segment.span().hi()), "if cfg!".to_string()),
879                    (args_span.shrink_to_hi().with_hi(attr.span.hi()), " {".to_string()),
880                    (expr.span.shrink_to_lo(), "    ".to_string()),
881                    (
882                        next_attr.span.with_hi(next_segment.span().hi()),
883                        "} else if cfg!".to_string(),
884                    ),
885                    (
886                        next_attr_args_span.shrink_to_hi().with_hi(next_attr.span.hi()),
887                        " {".to_string(),
888                    ),
889                    (next_expr.span.shrink_to_lo(), "    ".to_string()),
890                    (next_expr.span.shrink_to_hi(), format!("\n{}}}", " ".repeat(margin))),
891                ];
892                err.multipart_suggestion(
893                    "it seems like you are trying to provide different expressions depending on \
894                     `cfg`, consider using `if cfg!(..)`",
895                    sugg,
896                    Applicability::MachineApplicable,
897                );
898            }
899        }
900
901        err.emit()
902    }
903
904    fn check_too_many_raw_str_terminators(&mut self, err: &mut Diag<'_>) -> bool {
905        let sm = self.psess.source_map();
906        match (&self.prev_token.kind, &self.token.kind) {
907            (
908                TokenKind::Literal(Lit {
909                    kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes),
910                    ..
911                }),
912                TokenKind::Pound,
913            ) if !sm.is_multiline(
914                self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
915            ) =>
916            {
917                let n_hashes: u8 = *n_hashes;
918                err.primary_message("too many `#` when terminating raw string");
919                let str_span = self.prev_token.span;
920                let mut span = self.token.span;
921                let mut count = 0;
922                while self.token == TokenKind::Pound
923                    && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo()))
924                {
925                    span = span.with_hi(self.token.span.hi());
926                    self.bump();
927                    count += 1;
928                }
929                err.span(span);
930                err.span_suggestion(
931                    span,
932                    format!("remove the extra `#`{}", pluralize!(count)),
933                    "",
934                    Applicability::MachineApplicable,
935                );
936                err.span_label(
937                    str_span,
938                    format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),
939                );
940                true
941            }
942            _ => false,
943        }
944    }
945
946    pub(super) fn maybe_suggest_struct_literal(
947        &mut self,
948        lo: Span,
949        s: BlockCheckMode,
950        maybe_struct_name: token::Token,
951    ) -> Option<PResult<'a, P<Block>>> {
952        if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
953            // We might be having a struct literal where people forgot to include the path:
954            // fn foo() -> Foo {
955            //     field: value,
956            // }
957            debug!(?maybe_struct_name, ?self.token);
958            let mut snapshot = self.create_snapshot_for_diagnostic();
959            let path = Path {
960                segments: ThinVec::new(),
961                span: self.prev_token.span.shrink_to_lo(),
962                tokens: None,
963            };
964            let struct_expr = snapshot.parse_expr_struct(None, path, false);
965            let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
966            return Some(match (struct_expr, block_tail) {
967                (Ok(expr), Err(err)) => {
968                    // We have encountered the following:
969                    // fn foo() -> Foo {
970                    //     field: value,
971                    // }
972                    // Suggest:
973                    // fn foo() -> Foo { Path {
974                    //     field: value,
975                    // } }
976                    err.cancel();
977                    self.restore_snapshot(snapshot);
978                    let guar = self.dcx().emit_err(StructLiteralBodyWithoutPath {
979                        span: expr.span,
980                        sugg: StructLiteralBodyWithoutPathSugg {
981                            before: expr.span.shrink_to_lo(),
982                            after: expr.span.shrink_to_hi(),
983                        },
984                    });
985                    Ok(self.mk_block(
986                        thin_vec![self.mk_stmt_err(expr.span, guar)],
987                        s,
988                        lo.to(self.prev_token.span),
989                    ))
990                }
991                (Err(err), Ok(tail)) => {
992                    // We have a block tail that contains a somehow valid expr.
993                    err.cancel();
994                    Ok(tail)
995                }
996                (Err(snapshot_err), Err(err)) => {
997                    // We don't know what went wrong, emit the normal error.
998                    snapshot_err.cancel();
999                    self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes);
1000                    Err(err)
1001                }
1002                (Ok(_), Ok(tail)) => Ok(tail),
1003            });
1004        }
1005        None
1006    }
1007
1008    pub(super) fn recover_closure_body(
1009        &mut self,
1010        mut err: Diag<'a>,
1011        before: token::Token,
1012        prev: token::Token,
1013        token: token::Token,
1014        lo: Span,
1015        decl_hi: Span,
1016    ) -> PResult<'a, P<Expr>> {
1017        err.span_label(lo.to(decl_hi), "while parsing the body of this closure");
1018        let guar = match before.kind {
1019            token::OpenDelim(Delimiter::Brace)
1020                if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
1021            {
1022                // `{ || () }` should have been `|| { () }`
1023                err.multipart_suggestion(
1024                    "you might have meant to open the body of the closure, instead of enclosing \
1025                     the closure in a block",
1026                    vec![
1027                        (before.span, String::new()),
1028                        (prev.span.shrink_to_hi(), " {".to_string()),
1029                    ],
1030                    Applicability::MaybeIncorrect,
1031                );
1032                let guar = err.emit();
1033                self.eat_to_tokens(&[exp!(CloseBrace)]);
1034                guar
1035            }
1036            token::OpenDelim(Delimiter::Parenthesis)
1037                if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
1038            {
1039                // We are within a function call or tuple, we can emit the error
1040                // and recover.
1041                self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]);
1042
1043                err.multipart_suggestion_verbose(
1044                    "you might have meant to open the body of the closure",
1045                    vec![
1046                        (prev.span.shrink_to_hi(), " {".to_string()),
1047                        (self.token.span.shrink_to_lo(), "}".to_string()),
1048                    ],
1049                    Applicability::MaybeIncorrect,
1050                );
1051                err.emit()
1052            }
1053            _ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => {
1054                // We don't have a heuristic to correctly identify where the block
1055                // should be closed.
1056                err.multipart_suggestion_verbose(
1057                    "you might have meant to open the body of the closure",
1058                    vec![(prev.span.shrink_to_hi(), " {".to_string())],
1059                    Applicability::HasPlaceholders,
1060                );
1061                return Err(err);
1062            }
1063            _ => return Err(err),
1064        };
1065        Ok(self.mk_expr_err(lo.to(self.token.span), guar))
1066    }
1067
1068    /// Eats and discards tokens until one of `closes` is encountered. Respects token trees,
1069    /// passes through any errors encountered. Used for error recovery.
1070    pub(super) fn eat_to_tokens(&mut self, closes: &[ExpTokenPair<'_>]) {
1071        if let Err(err) = self
1072            .parse_seq_to_before_tokens(closes, &[], SeqSep::none(), |p| Ok(p.parse_token_tree()))
1073        {
1074            err.cancel();
1075        }
1076    }
1077
1078    /// This function checks if there are trailing angle brackets and produces
1079    /// a diagnostic to suggest removing them.
1080    ///
1081    /// ```ignore (diagnostic)
1082    /// let _ = [1, 2, 3].into_iter().collect::<Vec<usize>>>>();
1083    ///                                                    ^^ help: remove extra angle brackets
1084    /// ```
1085    ///
1086    /// If `true` is returned, then trailing brackets were recovered, tokens were consumed
1087    /// up until one of the tokens in 'end' was encountered, and an error was emitted.
1088    pub(super) fn check_trailing_angle_brackets(
1089        &mut self,
1090        segment: &PathSegment,
1091        end: &[ExpTokenPair<'_>],
1092    ) -> Option<ErrorGuaranteed> {
1093        if !self.may_recover() {
1094            return None;
1095        }
1096
1097        // This function is intended to be invoked after parsing a path segment where there are two
1098        // cases:
1099        //
1100        // 1. A specific token is expected after the path segment.
1101        //    eg. `x.foo(`, `x.foo::<u32>(` (parenthesis - method call),
1102        //        `Foo::`, or `Foo::<Bar>::` (mod sep - continued path).
1103        // 2. No specific token is expected after the path segment.
1104        //    eg. `x.foo` (field access)
1105        //
1106        // This function is called after parsing `.foo` and before parsing the token `end` (if
1107        // present). This includes any angle bracket arguments, such as `.foo::<u32>` or
1108        // `Foo::<Bar>`.
1109
1110        // We only care about trailing angle brackets if we previously parsed angle bracket
1111        // arguments. This helps stop us incorrectly suggesting that extra angle brackets be
1112        // removed in this case:
1113        //
1114        // `x.foo >> (3)` (where `x.foo` is a `u32` for example)
1115        //
1116        // This case is particularly tricky as we won't notice it just looking at the tokens -
1117        // it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will
1118        // have already been parsed):
1119        //
1120        // `x.foo::<u32>>>(3)`
1121        let parsed_angle_bracket_args =
1122            segment.args.as_ref().is_some_and(|args| args.is_angle_bracketed());
1123
1124        debug!(
1125            "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",
1126            parsed_angle_bracket_args,
1127        );
1128        if !parsed_angle_bracket_args {
1129            return None;
1130        }
1131
1132        // Keep the span at the start so we can highlight the sequence of `>` characters to be
1133        // removed.
1134        let lo = self.token.span;
1135
1136        // We need to look-ahead to see if we have `>` characters without moving the cursor forward
1137        // (since we might have the field access case and the characters we're eating are
1138        // actual operators and not trailing characters - ie `x.foo >> 3`).
1139        let mut position = 0;
1140
1141        // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how
1142        // many of each (so we can correctly pluralize our error messages) and continue to
1143        // advance.
1144        let mut number_of_shr = 0;
1145        let mut number_of_gt = 0;
1146        while self.look_ahead(position, |t| {
1147            trace!("check_trailing_angle_brackets: t={:?}", t);
1148            if *t == token::Shr {
1149                number_of_shr += 1;
1150                true
1151            } else if *t == token::Gt {
1152                number_of_gt += 1;
1153                true
1154            } else {
1155                false
1156            }
1157        }) {
1158            position += 1;
1159        }
1160
1161        // If we didn't find any trailing `>` characters, then we have nothing to error about.
1162        debug!(
1163            "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}",
1164            number_of_gt, number_of_shr,
1165        );
1166        if number_of_gt < 1 && number_of_shr < 1 {
1167            return None;
1168        }
1169
1170        // Finally, double check that we have our end token as otherwise this is the
1171        // second case.
1172        if self.look_ahead(position, |t| {
1173            trace!("check_trailing_angle_brackets: t={:?}", t);
1174            end.iter().any(|exp| exp.tok == &t.kind)
1175        }) {
1176            // Eat from where we started until the end token so that parsing can continue
1177            // as if we didn't have those extra angle brackets.
1178            self.eat_to_tokens(end);
1179            let span = lo.to(self.prev_token.span);
1180
1181            let num_extra_brackets = number_of_gt + number_of_shr * 2;
1182            return Some(self.dcx().emit_err(UnmatchedAngleBrackets { span, num_extra_brackets }));
1183        }
1184        None
1185    }
1186
1187    /// Check if a method call with an intended turbofish has been written without surrounding
1188    /// angle brackets.
1189    pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) {
1190        if !self.may_recover() {
1191            return;
1192        }
1193
1194        if self.token == token::PathSep && segment.args.is_none() {
1195            let snapshot = self.create_snapshot_for_diagnostic();
1196            self.bump();
1197            let lo = self.token.span;
1198            match self.parse_angle_args(None) {
1199                Ok(args) => {
1200                    let span = lo.to(self.prev_token.span);
1201                    // Detect trailing `>` like in `x.collect::Vec<_>>()`.
1202                    let mut trailing_span = self.prev_token.span.shrink_to_hi();
1203                    while self.token == token::Shr || self.token == token::Gt {
1204                        trailing_span = trailing_span.to(self.token.span);
1205                        self.bump();
1206                    }
1207                    if self.token == token::OpenDelim(Delimiter::Parenthesis) {
1208                        // Recover from bad turbofish: `foo.collect::Vec<_>()`.
1209                        segment.args = Some(AngleBracketedArgs { args, span }.into());
1210
1211                        self.dcx().emit_err(GenericParamsWithoutAngleBrackets {
1212                            span,
1213                            sugg: GenericParamsWithoutAngleBracketsSugg {
1214                                left: span.shrink_to_lo(),
1215                                right: trailing_span,
1216                            },
1217                        });
1218                    } else {
1219                        // This doesn't look like an invalid turbofish, can't recover parse state.
1220                        self.restore_snapshot(snapshot);
1221                    }
1222                }
1223                Err(err) => {
1224                    // We couldn't parse generic parameters, unlikely to be a turbofish. Rely on
1225                    // generic parse error instead.
1226                    err.cancel();
1227                    self.restore_snapshot(snapshot);
1228                }
1229            }
1230        }
1231    }
1232
1233    /// When writing a turbofish with multiple type parameters missing the leading `::`, we will
1234    /// encounter a parse error when encountering the first `,`.
1235    pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
1236        &mut self,
1237        mut e: Diag<'a>,
1238        expr: &mut P<Expr>,
1239    ) -> PResult<'a, ErrorGuaranteed> {
1240        if let ExprKind::Binary(binop, _, _) = &expr.kind
1241            && let ast::BinOpKind::Lt = binop.node
1242            && self.eat(exp!(Comma))
1243        {
1244            let x = self.parse_seq_to_before_end(
1245                exp!(Gt),
1246                SeqSep::trailing_allowed(exp!(Comma)),
1247                |p| match p.parse_generic_arg(None)? {
1248                    Some(arg) => Ok(arg),
1249                    // If we didn't eat a generic arg, then we should error.
1250                    None => p.unexpected_any(),
1251                },
1252            );
1253            match x {
1254                Ok((_, _, Recovered::No)) => {
1255                    if self.eat(exp!(Gt)) {
1256                        // We made sense of it. Improve the error message.
1257                        e.span_suggestion_verbose(
1258                            binop.span.shrink_to_lo(),
1259                            fluent::parse_sugg_turbofish_syntax,
1260                            "::",
1261                            Applicability::MaybeIncorrect,
1262                        );
1263                        match self.parse_expr() {
1264                            Ok(_) => {
1265                                // The subsequent expression is valid. Mark
1266                                // `expr` as erroneous and emit `e` now, but
1267                                // return `Ok` so parsing can continue.
1268                                let guar = e.emit();
1269                                *expr = self.mk_expr_err(expr.span.to(self.prev_token.span), guar);
1270                                return Ok(guar);
1271                            }
1272                            Err(err) => {
1273                                err.cancel();
1274                            }
1275                        }
1276                    }
1277                }
1278                Ok((_, _, Recovered::Yes(_))) => {}
1279                Err(err) => {
1280                    err.cancel();
1281                }
1282            }
1283        }
1284        Err(e)
1285    }
1286
1287    /// Suggest add the missing `let` before the identifier in stmt
1288    /// `a: Ty = 1` -> `let a: Ty = 1`
1289    pub(super) fn suggest_add_missing_let_for_stmt(&mut self, err: &mut Diag<'a>) {
1290        if self.token == token::Colon {
1291            let prev_span = self.prev_token.span.shrink_to_lo();
1292            let snapshot = self.create_snapshot_for_diagnostic();
1293            self.bump();
1294            match self.parse_ty() {
1295                Ok(_) => {
1296                    if self.token == token::Eq {
1297                        let sugg = SuggAddMissingLetStmt { span: prev_span };
1298                        sugg.add_to_diag(err);
1299                    }
1300                }
1301                Err(e) => {
1302                    e.cancel();
1303                }
1304            }
1305            self.restore_snapshot(snapshot);
1306        }
1307    }
1308
1309    /// Check to see if a pair of chained operators looks like an attempt at chained comparison,
1310    /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or
1311    /// parenthesising the leftmost comparison. The return value indicates if recovery happened.
1312    fn attempt_chained_comparison_suggestion(
1313        &mut self,
1314        err: &mut ComparisonOperatorsCannotBeChained,
1315        inner_op: &Expr,
1316        outer_op: &Spanned<AssocOp>,
1317    ) -> bool {
1318        if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {
1319            if let ExprKind::Field(_, ident) = l1.kind
1320                && !ident.is_numeric()
1321                && !matches!(r1.kind, ExprKind::Lit(_))
1322            {
1323                // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish
1324                // suggestion being the only one to apply is high.
1325                return false;
1326            }
1327            return match (op.node, &outer_op.node) {
1328                // `x == y == z`
1329                (BinOpKind::Eq, AssocOp::Binary(BinOpKind::Eq)) |
1330                // `x < y < z` and friends.
1331                (BinOpKind::Lt, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
1332                (BinOpKind::Le, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
1333                // `x > y > z` and friends.
1334                (BinOpKind::Gt, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) |
1335                (BinOpKind::Ge, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) => {
1336                    let expr_to_str = |e: &Expr| {
1337                        self.span_to_snippet(e.span)
1338                            .unwrap_or_else(|_| pprust::expr_to_string(e))
1339                    };
1340                    err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::SplitComparison {
1341                        span: inner_op.span.shrink_to_hi(),
1342                        middle_term: expr_to_str(r1),
1343                    });
1344                    false // Keep the current parse behavior, where the AST is `(x < y) < z`.
1345                }
1346                // `x == y < z`
1347                (
1348                    BinOpKind::Eq,
1349                    AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge)
1350                ) => {
1351                    // Consume `z`/outer-op-rhs.
1352                    let snapshot = self.create_snapshot_for_diagnostic();
1353                    match self.parse_expr() {
1354                        Ok(r2) => {
1355                            // We are sure that outer-op-rhs could be consumed, the suggestion is
1356                            // likely correct.
1357                            err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
1358                                left: r1.span.shrink_to_lo(),
1359                                right: r2.span.shrink_to_hi(),
1360                            });
1361                            true
1362                        }
1363                        Err(expr_err) => {
1364                            expr_err.cancel();
1365                            self.restore_snapshot(snapshot);
1366                            true
1367                        }
1368                    }
1369                }
1370                // `x > y == z`
1371                (
1372                    BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge,
1373                    AssocOp::Binary(BinOpKind::Eq)
1374                ) => {
1375                    let snapshot = self.create_snapshot_for_diagnostic();
1376                    // At this point it is always valid to enclose the lhs in parentheses, no
1377                    // further checks are necessary.
1378                    match self.parse_expr() {
1379                        Ok(_) => {
1380                            err.chaining_sugg = Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {
1381                                left: l1.span.shrink_to_lo(),
1382                                right: r1.span.shrink_to_hi(),
1383                            });
1384                            true
1385                        }
1386                        Err(expr_err) => {
1387                            expr_err.cancel();
1388                            self.restore_snapshot(snapshot);
1389                            false
1390                        }
1391                    }
1392                }
1393                _ => false
1394            };
1395        }
1396        false
1397    }
1398
1399    /// Produces an error if comparison operators are chained (RFC #558).
1400    /// We only need to check the LHS, not the RHS, because all comparison ops have same
1401    /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`).
1402    ///
1403    /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
1404    /// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the
1405    /// case.
1406    ///
1407    /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left
1408    /// associative we can infer that we have:
1409    ///
1410    /// ```text
1411    ///           outer_op
1412    ///           /   \
1413    ///     inner_op   r2
1414    ///        /  \
1415    ///      l1    r1
1416    /// ```
1417    pub(super) fn check_no_chained_comparison(
1418        &mut self,
1419        inner_op: &Expr,
1420        outer_op: &Spanned<AssocOp>,
1421    ) -> PResult<'a, Option<P<Expr>>> {
1422        debug_assert!(
1423            outer_op.node.is_comparison(),
1424            "check_no_chained_comparison: {:?} is not comparison",
1425            outer_op.node,
1426        );
1427
1428        let mk_err_expr =
1429            |this: &Self, span, guar| Ok(Some(this.mk_expr(span, ExprKind::Err(guar))));
1430
1431        match &inner_op.kind {
1432            ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => {
1433                let mut err = ComparisonOperatorsCannotBeChained {
1434                    span: vec![op.span, self.prev_token.span],
1435                    suggest_turbofish: None,
1436                    help_turbofish: false,
1437                    chaining_sugg: None,
1438                };
1439
1440                // Include `<` to provide this recommendation even in a case like
1441                // `Foo<Bar<Baz<Qux, ()>>>`
1442                if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Binary(BinOpKind::Lt)
1443                    || outer_op.node == AssocOp::Binary(BinOpKind::Gt)
1444                {
1445                    if outer_op.node == AssocOp::Binary(BinOpKind::Lt) {
1446                        let snapshot = self.create_snapshot_for_diagnostic();
1447                        self.bump();
1448                        // So far we have parsed `foo<bar<`, consume the rest of the type args.
1449                        let modifiers = [(token::Lt, 1), (token::Gt, -1), (token::Shr, -2)];
1450                        self.consume_tts(1, &modifiers);
1451
1452                        if !&[token::OpenDelim(Delimiter::Parenthesis), token::PathSep]
1453                            .contains(&self.token.kind)
1454                        {
1455                            // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
1456                            // parser and bail out.
1457                            self.restore_snapshot(snapshot);
1458                        }
1459                    }
1460                    return if self.token == token::PathSep {
1461                        // We have some certainty that this was a bad turbofish at this point.
1462                        // `foo< bar >::`
1463                        if let ExprKind::Binary(o, ..) = inner_op.kind
1464                            && o.node == BinOpKind::Lt
1465                        {
1466                            err.suggest_turbofish = Some(op.span.shrink_to_lo());
1467                        } else {
1468                            err.help_turbofish = true;
1469                        }
1470
1471                        let snapshot = self.create_snapshot_for_diagnostic();
1472                        self.bump(); // `::`
1473
1474                        // Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
1475                        match self.parse_expr() {
1476                            Ok(_) => {
1477                                // 99% certain that the suggestion is correct, continue parsing.
1478                                let guar = self.dcx().emit_err(err);
1479                                // FIXME: actually check that the two expressions in the binop are
1480                                // paths and resynthesize new fn call expression instead of using
1481                                // `ExprKind::Err` placeholder.
1482                                mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1483                            }
1484                            Err(expr_err) => {
1485                                expr_err.cancel();
1486                                // Not entirely sure now, but we bubble the error up with the
1487                                // suggestion.
1488                                self.restore_snapshot(snapshot);
1489                                Err(self.dcx().create_err(err))
1490                            }
1491                        }
1492                    } else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
1493                        // We have high certainty that this was a bad turbofish at this point.
1494                        // `foo< bar >(`
1495                        if let ExprKind::Binary(o, ..) = inner_op.kind
1496                            && o.node == BinOpKind::Lt
1497                        {
1498                            err.suggest_turbofish = Some(op.span.shrink_to_lo());
1499                        } else {
1500                            err.help_turbofish = true;
1501                        }
1502                        // Consume the fn call arguments.
1503                        match self.consume_fn_args() {
1504                            Err(()) => Err(self.dcx().create_err(err)),
1505                            Ok(()) => {
1506                                let guar = self.dcx().emit_err(err);
1507                                // FIXME: actually check that the two expressions in the binop are
1508                                // paths and resynthesize new fn call expression instead of using
1509                                // `ExprKind::Err` placeholder.
1510                                mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1511                            }
1512                        }
1513                    } else {
1514                        if !matches!(l1.kind, ExprKind::Lit(_))
1515                            && !matches!(r1.kind, ExprKind::Lit(_))
1516                        {
1517                            // All we know is that this is `foo < bar >` and *nothing* else. Try to
1518                            // be helpful, but don't attempt to recover.
1519                            err.help_turbofish = true;
1520                        }
1521
1522                        // If it looks like a genuine attempt to chain operators (as opposed to a
1523                        // misformatted turbofish, for instance), suggest a correct form.
1524                        let recovered = self
1525                            .attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
1526                        if recovered {
1527                            let guar = self.dcx().emit_err(err);
1528                            mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)
1529                        } else {
1530                            // These cases cause too many knock-down errors, bail out (#61329).
1531                            Err(self.dcx().create_err(err))
1532                        }
1533                    };
1534                }
1535                let recovered =
1536                    self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);
1537                let guar = self.dcx().emit_err(err);
1538                if recovered {
1539                    return mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar);
1540                }
1541            }
1542            _ => {}
1543        }
1544        Ok(None)
1545    }
1546
1547    fn consume_fn_args(&mut self) -> Result<(), ()> {
1548        let snapshot = self.create_snapshot_for_diagnostic();
1549        self.bump(); // `(`
1550
1551        // Consume the fn call arguments.
1552        let modifiers = [
1553            (token::OpenDelim(Delimiter::Parenthesis), 1),
1554            (token::CloseDelim(Delimiter::Parenthesis), -1),
1555        ];
1556        self.consume_tts(1, &modifiers);
1557
1558        if self.token == token::Eof {
1559            // Not entirely sure that what we consumed were fn arguments, rollback.
1560            self.restore_snapshot(snapshot);
1561            Err(())
1562        } else {
1563            // 99% certain that the suggestion is correct, continue parsing.
1564            Ok(())
1565        }
1566    }
1567
1568    pub(super) fn maybe_report_ambiguous_plus(&mut self, impl_dyn_multi: bool, ty: &Ty) {
1569        if impl_dyn_multi {
1570            self.dcx().emit_err(AmbiguousPlus {
1571                span: ty.span,
1572                suggestion: AddParen { lo: ty.span.shrink_to_lo(), hi: ty.span.shrink_to_hi() },
1573            });
1574        }
1575    }
1576
1577    /// Swift lets users write `Ty?` to mean `Option<Ty>`. Parse the construct and recover from it.
1578    pub(super) fn maybe_recover_from_question_mark(&mut self, ty: P<Ty>) -> P<Ty> {
1579        if self.token == token::Question {
1580            self.bump();
1581            let guar = self.dcx().emit_err(QuestionMarkInType {
1582                span: self.prev_token.span,
1583                sugg: QuestionMarkInTypeSugg {
1584                    left: ty.span.shrink_to_lo(),
1585                    right: self.prev_token.span,
1586                },
1587            });
1588            self.mk_ty(ty.span.to(self.prev_token.span), TyKind::Err(guar))
1589        } else {
1590            ty
1591        }
1592    }
1593
1594    /// Rust has no ternary operator (`cond ? then : else`). Parse it and try
1595    /// to recover from it if `then` and `else` are valid expressions. Returns
1596    /// an err if this appears to be a ternary expression.
1597    pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> PResult<'a, ()> {
1598        if self.prev_token != token::Question {
1599            return PResult::Ok(());
1600        }
1601
1602        let lo = self.prev_token.span.lo();
1603        let snapshot = self.create_snapshot_for_diagnostic();
1604
1605        if match self.parse_expr() {
1606            Ok(_) => true,
1607            Err(err) => {
1608                err.cancel();
1609                // The colon can sometimes be mistaken for type
1610                // ascription. Catch when this happens and continue.
1611                self.token == token::Colon
1612            }
1613        } {
1614            if self.eat_noexpect(&token::Colon) {
1615                match self.parse_expr() {
1616                    Ok(_) => {
1617                        return Err(self
1618                            .dcx()
1619                            .create_err(TernaryOperator { span: self.token.span.with_lo(lo) }));
1620                    }
1621                    Err(err) => {
1622                        err.cancel();
1623                    }
1624                };
1625            }
1626        }
1627        self.restore_snapshot(snapshot);
1628        Ok(())
1629    }
1630
1631    pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {
1632        // Do not add `+` to expected tokens.
1633        if !self.token.is_like_plus() {
1634            return Ok(());
1635        }
1636
1637        self.bump(); // `+`
1638        let _bounds = self.parse_generic_bounds()?;
1639        let sum_span = ty.span.to(self.prev_token.span);
1640
1641        let sub = match &ty.kind {
1642            TyKind::Ref(_lifetime, mut_ty) => {
1643                let lo = mut_ty.ty.span.shrink_to_lo();
1644                let hi = self.prev_token.span.shrink_to_hi();
1645                BadTypePlusSub::AddParen { suggestion: AddParen { lo, hi } }
1646            }
1647            TyKind::Ptr(..) | TyKind::BareFn(..) => BadTypePlusSub::ForgotParen { span: sum_span },
1648            _ => BadTypePlusSub::ExpectPath { span: sum_span },
1649        };
1650
1651        self.dcx().emit_err(BadTypePlus { ty: pprust::ty_to_string(ty), span: sum_span, sub });
1652
1653        Ok(())
1654    }
1655
1656    pub(super) fn recover_from_prefix_increment(
1657        &mut self,
1658        operand_expr: P<Expr>,
1659        op_span: Span,
1660        start_stmt: bool,
1661    ) -> PResult<'a, P<Expr>> {
1662        let standalone = if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr };
1663        let kind = IncDecRecovery { standalone, op: IncOrDec::Inc, fixity: UnaryFixity::Pre };
1664        self.recover_from_inc_dec(operand_expr, kind, op_span)
1665    }
1666
1667    pub(super) fn recover_from_postfix_increment(
1668        &mut self,
1669        operand_expr: P<Expr>,
1670        op_span: Span,
1671        start_stmt: bool,
1672    ) -> PResult<'a, P<Expr>> {
1673        let kind = IncDecRecovery {
1674            standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },
1675            op: IncOrDec::Inc,
1676            fixity: UnaryFixity::Post,
1677        };
1678        self.recover_from_inc_dec(operand_expr, kind, op_span)
1679    }
1680
1681    pub(super) fn recover_from_postfix_decrement(
1682        &mut self,
1683        operand_expr: P<Expr>,
1684        op_span: Span,
1685        start_stmt: bool,
1686    ) -> PResult<'a, P<Expr>> {
1687        let kind = IncDecRecovery {
1688            standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },
1689            op: IncOrDec::Dec,
1690            fixity: UnaryFixity::Post,
1691        };
1692        self.recover_from_inc_dec(operand_expr, kind, op_span)
1693    }
1694
1695    fn recover_from_inc_dec(
1696        &mut self,
1697        base: P<Expr>,
1698        kind: IncDecRecovery,
1699        op_span: Span,
1700    ) -> PResult<'a, P<Expr>> {
1701        let mut err = self.dcx().struct_span_err(
1702            op_span,
1703            format!("Rust has no {} {} operator", kind.fixity, kind.op.name()),
1704        );
1705        err.span_label(op_span, format!("not a valid {} operator", kind.fixity));
1706
1707        let help_base_case = |mut err: Diag<'_, _>, base| {
1708            err.help(format!("use `{}= 1` instead", kind.op.chr()));
1709            err.emit();
1710            Ok(base)
1711        };
1712
1713        // (pre, post)
1714        let spans = match kind.fixity {
1715            UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()),
1716            UnaryFixity::Post => (base.span.shrink_to_lo(), op_span),
1717        };
1718
1719        match kind.standalone {
1720            IsStandalone::Standalone => {
1721                self.inc_dec_standalone_suggest(kind, spans).emit_verbose(&mut err)
1722            }
1723            IsStandalone::Subexpr => {
1724                let Ok(base_src) = self.span_to_snippet(base.span) else {
1725                    return help_base_case(err, base);
1726                };
1727                match kind.fixity {
1728                    UnaryFixity::Pre => {
1729                        self.prefix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
1730                    }
1731                    UnaryFixity::Post => {
1732                        // won't suggest since we can not handle the precedences
1733                        // for example: `a + b++` has been parsed (a + b)++ and we can not suggest here
1734                        if !matches!(base.kind, ExprKind::Binary(_, _, _)) {
1735                            self.postfix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
1736                        }
1737                    }
1738                }
1739            }
1740        }
1741        Err(err)
1742    }
1743
1744    fn prefix_inc_dec_suggest(
1745        &mut self,
1746        base_src: String,
1747        kind: IncDecRecovery,
1748        (pre_span, post_span): (Span, Span),
1749    ) -> MultiSugg {
1750        MultiSugg {
1751            msg: format!("use `{}= 1` instead", kind.op.chr()),
1752            patches: vec![
1753                (pre_span, "{ ".to_string()),
1754                (post_span, format!(" {}= 1; {} }}", kind.op.chr(), base_src)),
1755            ],
1756            applicability: Applicability::MachineApplicable,
1757        }
1758    }
1759
1760    fn postfix_inc_dec_suggest(
1761        &mut self,
1762        base_src: String,
1763        kind: IncDecRecovery,
1764        (pre_span, post_span): (Span, Span),
1765    ) -> MultiSugg {
1766        let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" };
1767        MultiSugg {
1768            msg: format!("use `{}= 1` instead", kind.op.chr()),
1769            patches: vec![
1770                (pre_span, format!("{{ let {tmp_var} = ")),
1771                (post_span, format!("; {} {}= 1; {} }}", base_src, kind.op.chr(), tmp_var)),
1772            ],
1773            applicability: Applicability::HasPlaceholders,
1774        }
1775    }
1776
1777    fn inc_dec_standalone_suggest(
1778        &mut self,
1779        kind: IncDecRecovery,
1780        (pre_span, post_span): (Span, Span),
1781    ) -> MultiSugg {
1782        let mut patches = Vec::new();
1783
1784        if !pre_span.is_empty() {
1785            patches.push((pre_span, String::new()));
1786        }
1787
1788        patches.push((post_span, format!(" {}= 1", kind.op.chr())));
1789        MultiSugg {
1790            msg: format!("use `{}= 1` instead", kind.op.chr()),
1791            patches,
1792            applicability: Applicability::MachineApplicable,
1793        }
1794    }
1795
1796    /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`.
1797    /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem`
1798    /// tail, and combines them into a `<Ty>::AssocItem` expression/pattern/type.
1799    pub(super) fn maybe_recover_from_bad_qpath<T: RecoverQPath>(
1800        &mut self,
1801        base: P<T>,
1802    ) -> PResult<'a, P<T>> {
1803        if !self.may_recover() {
1804            return Ok(base);
1805        }
1806
1807        // Do not add `::` to expected tokens.
1808        if self.token == token::PathSep {
1809            if let Some(ty) = base.to_ty() {
1810                return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
1811            }
1812        }
1813        Ok(base)
1814    }
1815
1816    /// Given an already parsed `Ty`, parses the `::AssocItem` tail and
1817    /// combines them into a `<Ty>::AssocItem` expression/pattern/type.
1818    pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(
1819        &mut self,
1820        ty_span: Span,
1821        ty: P<Ty>,
1822    ) -> PResult<'a, P<T>> {
1823        self.expect(exp!(PathSep))?;
1824
1825        let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None };
1826        self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?;
1827        path.span = ty_span.to(self.prev_token.span);
1828
1829        self.dcx().emit_err(BadQPathStage2 {
1830            span: ty_span,
1831            wrap: WrapType { lo: ty_span.shrink_to_lo(), hi: ty_span.shrink_to_hi() },
1832        });
1833
1834        let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`.
1835        Ok(P(T::recovered(Some(P(QSelf { ty, path_span, position: 0 })), path)))
1836    }
1837
1838    /// This function gets called in places where a semicolon is NOT expected and if there's a
1839    /// semicolon it emits the appropriate error and returns true.
1840    pub fn maybe_consume_incorrect_semicolon(&mut self, previous_item: Option<&Item>) -> bool {
1841        if self.token != TokenKind::Semi {
1842            return false;
1843        }
1844
1845        // Check previous item to add it to the diagnostic, for example to say
1846        // `enum declarations are not followed by a semicolon`
1847        let err = match previous_item {
1848            Some(previous_item) => {
1849                let name = match previous_item.kind {
1850                    // Say "braced struct" because tuple-structs and
1851                    // braceless-empty-struct declarations do take a semicolon.
1852                    ItemKind::Struct(..) => "braced struct",
1853                    _ => previous_item.kind.descr(),
1854                };
1855                IncorrectSemicolon { span: self.token.span, name, show_help: true }
1856            }
1857            None => IncorrectSemicolon { span: self.token.span, name: "", show_help: false },
1858        };
1859        self.dcx().emit_err(err);
1860
1861        self.bump();
1862        true
1863    }
1864
1865    /// Creates a `Diag` for an unexpected token `t` and tries to recover if it is a
1866    /// closing delimiter.
1867    pub(super) fn unexpected_try_recover(&mut self, t: &TokenKind) -> PResult<'a, Recovered> {
1868        let token_str = pprust::token_kind_to_string(t);
1869        let this_token_str = super::token_descr(&self.token);
1870        let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) {
1871            // Point at the end of the macro call when reaching end of macro arguments.
1872            (token::Eof, Some(_)) => {
1873                let sp = self.prev_token.span.shrink_to_hi();
1874                (sp, sp)
1875            }
1876            // We don't want to point at the following span after DUMMY_SP.
1877            // This happens when the parser finds an empty TokenStream.
1878            _ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span),
1879            // EOF, don't want to point at the following char, but rather the last token.
1880            (token::Eof, None) => (self.prev_token.span, self.token.span),
1881            _ => (self.prev_token.span.shrink_to_hi(), self.token.span),
1882        };
1883        let msg = format!(
1884            "expected `{}`, found {}",
1885            token_str,
1886            match (&self.token.kind, self.subparser_name) {
1887                (token::Eof, Some(origin)) => format!("end of {origin}"),
1888                _ => this_token_str,
1889            },
1890        );
1891        let mut err = self.dcx().struct_span_err(sp, msg);
1892        let label_exp = format!("expected `{token_str}`");
1893        let sm = self.psess.source_map();
1894        if !sm.is_multiline(prev_sp.until(sp)) {
1895            // When the spans are in the same line, it means that the only content
1896            // between them is whitespace, point only at the found token.
1897            err.span_label(sp, label_exp);
1898        } else {
1899            err.span_label(prev_sp, label_exp);
1900            err.span_label(sp, "unexpected token");
1901        }
1902        Err(err)
1903    }
1904
1905    pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {
1906        if self.eat(exp!(Semi)) || self.recover_colon_as_semi() {
1907            return Ok(());
1908        }
1909        self.expect(exp!(Semi)).map(drop) // Error unconditionally
1910    }
1911
1912    pub(super) fn recover_colon_as_semi(&mut self) -> bool {
1913        let line_idx = |span: Span| {
1914            self.psess
1915                .source_map()
1916                .span_to_lines(span)
1917                .ok()
1918                .and_then(|lines| Some(lines.lines.get(0)?.line_index))
1919        };
1920
1921        if self.may_recover()
1922            && self.token == token::Colon
1923            && self.look_ahead(1, |next| line_idx(self.token.span) < line_idx(next.span))
1924        {
1925            self.dcx().emit_err(ColonAsSemi {
1926                span: self.token.span,
1927                type_ascription: self.psess.unstable_features.is_nightly_build(),
1928            });
1929            self.bump();
1930            return true;
1931        }
1932
1933        false
1934    }
1935
1936    /// Consumes alternative await syntaxes like `await!(<expr>)`, `await <expr>`,
1937    /// `await? <expr>`, `await(<expr>)`, and `await { <expr> }`.
1938    pub(super) fn recover_incorrect_await_syntax(
1939        &mut self,
1940        await_sp: Span,
1941    ) -> PResult<'a, P<Expr>> {
1942        let (hi, expr, is_question) = if self.token == token::Bang {
1943            // Handle `await!(<expr>)`.
1944            self.recover_await_macro()?
1945        } else {
1946            self.recover_await_prefix(await_sp)?
1947        };
1948        let (sp, guar) = self.error_on_incorrect_await(await_sp, hi, &expr, is_question);
1949        let expr = self.mk_expr_err(await_sp.to(sp), guar);
1950        self.maybe_recover_from_bad_qpath(expr)
1951    }
1952
1953    fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> {
1954        self.expect(exp!(Bang))?;
1955        self.expect(exp!(OpenParen))?;
1956        let expr = self.parse_expr()?;
1957        self.expect(exp!(CloseParen))?;
1958        Ok((self.prev_token.span, expr, false))
1959    }
1960
1961    fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> {
1962        let is_question = self.eat(exp!(Question)); // Handle `await? <expr>`.
1963        let expr = if self.token == token::OpenDelim(Delimiter::Brace) {
1964            // Handle `await { <expr> }`.
1965            // This needs to be handled separately from the next arm to avoid
1966            // interpreting `await { <expr> }?` as `<expr>?.await`.
1967            self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)
1968        } else {
1969            self.parse_expr()
1970        }
1971        .map_err(|mut err| {
1972            err.span_label(await_sp, format!("while parsing this incorrect await expression"));
1973            err
1974        })?;
1975        Ok((expr.span, expr, is_question))
1976    }
1977
1978    fn error_on_incorrect_await(
1979        &self,
1980        lo: Span,
1981        hi: Span,
1982        expr: &Expr,
1983        is_question: bool,
1984    ) -> (Span, ErrorGuaranteed) {
1985        let span = lo.to(hi);
1986        let guar = self.dcx().emit_err(IncorrectAwait {
1987            span,
1988            suggestion: AwaitSuggestion {
1989                removal: lo.until(expr.span),
1990                dot_await: expr.span.shrink_to_hi(),
1991                question_mark: if is_question { "?" } else { "" },
1992            },
1993        });
1994        (span, guar)
1995    }
1996
1997    /// If encountering `future.await()`, consumes and emits an error.
1998    pub(super) fn recover_from_await_method_call(&mut self) {
1999        if self.token == token::OpenDelim(Delimiter::Parenthesis)
2000            && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
2001        {
2002            // future.await()
2003            let lo = self.token.span;
2004            self.bump(); // (
2005            let span = lo.to(self.token.span);
2006            self.bump(); // )
2007
2008            self.dcx().emit_err(IncorrectUseOfAwait { span });
2009        }
2010    }
2011    ///
2012    /// If encountering `x.use()`, consumes and emits an error.
2013    pub(super) fn recover_from_use(&mut self) {
2014        if self.token == token::OpenDelim(Delimiter::Parenthesis)
2015            && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
2016        {
2017            // var.use()
2018            let lo = self.token.span;
2019            self.bump(); // (
2020            let span = lo.to(self.token.span);
2021            self.bump(); // )
2022
2023            self.dcx().emit_err(IncorrectUseOfUse { span });
2024        }
2025    }
2026
2027    pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
2028        let is_try = self.token.is_keyword(kw::Try);
2029        let is_questionmark = self.look_ahead(1, |t| t == &token::Bang); //check for !
2030        let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(Delimiter::Parenthesis)); //check for (
2031
2032        if is_try && is_questionmark && is_open {
2033            let lo = self.token.span;
2034            self.bump(); //remove try
2035            self.bump(); //remove !
2036            let try_span = lo.to(self.token.span); //we take the try!( span
2037            self.bump(); //remove (
2038            let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); //check if the block is empty
2039            self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::No); //eat the block
2040            let hi = self.token.span;
2041            self.bump(); //remove )
2042            let mut err = self.dcx().struct_span_err(lo.to(hi), "use of deprecated `try` macro");
2043            err.note("in the 2018 edition `try` is a reserved keyword, and the `try!()` macro is deprecated");
2044            let prefix = if is_empty { "" } else { "alternatively, " };
2045            if !is_empty {
2046                err.multipart_suggestion(
2047                    "you can use the `?` operator instead",
2048                    vec![(try_span, "".to_owned()), (hi, "?".to_owned())],
2049                    Applicability::MachineApplicable,
2050                );
2051            }
2052            err.span_suggestion(lo.shrink_to_lo(), format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#", Applicability::MachineApplicable);
2053            let guar = err.emit();
2054            Ok(self.mk_expr_err(lo.to(hi), guar))
2055        } else {
2056            Err(self.expected_expression_found()) // The user isn't trying to invoke the try! macro
2057        }
2058    }
2059
2060    /// When trying to close a generics list and encountering code like
2061    /// ```text
2062    /// impl<S: Into<std::borrow::Cow<'static, str>> From<S> for Canonical {}
2063    ///                                          // ^ missing > here
2064    /// ```
2065    /// we provide a structured suggestion on the error from `expect_gt`.
2066    pub(super) fn expect_gt_or_maybe_suggest_closing_generics(
2067        &mut self,
2068        params: &[ast::GenericParam],
2069    ) -> PResult<'a, ()> {
2070        let Err(mut err) = self.expect_gt() else {
2071            return Ok(());
2072        };
2073        // Attempt to find places where a missing `>` might belong.
2074        if let [.., ast::GenericParam { bounds, .. }] = params
2075            && let Some(poly) = bounds
2076                .iter()
2077                .filter_map(|bound| match bound {
2078                    ast::GenericBound::Trait(poly) => Some(poly),
2079                    _ => None,
2080                })
2081                .next_back()
2082        {
2083            err.span_suggestion_verbose(
2084                poly.span.shrink_to_hi(),
2085                "you might have meant to end the type parameters here",
2086                ">",
2087                Applicability::MaybeIncorrect,
2088            );
2089        }
2090        Err(err)
2091    }
2092
2093    pub(super) fn recover_seq_parse_error(
2094        &mut self,
2095        open: ExpTokenPair<'_>,
2096        close: ExpTokenPair<'_>,
2097        lo: Span,
2098        err: Diag<'a>,
2099    ) -> P<Expr> {
2100        let guar = err.emit();
2101        // Recover from parse error, callers expect the closing delim to be consumed.
2102        self.consume_block(open, close, ConsumeClosingDelim::Yes);
2103        self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err(guar))
2104    }
2105
2106    /// Eats tokens until we can be relatively sure we reached the end of the
2107    /// statement. This is something of a best-effort heuristic.
2108    ///
2109    /// We terminate when we find an unmatched `}` (without consuming it).
2110    pub(super) fn recover_stmt(&mut self) {
2111        self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
2112    }
2113
2114    /// If `break_on_semi` is `Break`, then we will stop consuming tokens after
2115    /// finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
2116    /// approximate -- it can mean we break too early due to macros, but that
2117    /// should only lead to sub-optimal recovery, not inaccurate parsing).
2118    ///
2119    /// If `break_on_block` is `Break`, then we will stop consuming tokens
2120    /// after finding (and consuming) a brace-delimited block.
2121    pub(super) fn recover_stmt_(
2122        &mut self,
2123        break_on_semi: SemiColonMode,
2124        break_on_block: BlockMode,
2125    ) {
2126        let mut brace_depth = 0;
2127        let mut bracket_depth = 0;
2128        let mut in_block = false;
2129        debug!("recover_stmt_ enter loop (semi={:?}, block={:?})", break_on_semi, break_on_block);
2130        loop {
2131            debug!("recover_stmt_ loop {:?}", self.token);
2132            match self.token.kind {
2133                token::OpenDelim(Delimiter::Brace) => {
2134                    brace_depth += 1;
2135                    self.bump();
2136                    if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0
2137                    {
2138                        in_block = true;
2139                    }
2140                }
2141                token::OpenDelim(Delimiter::Bracket) => {
2142                    bracket_depth += 1;
2143                    self.bump();
2144                }
2145                token::CloseDelim(Delimiter::Brace) => {
2146                    if brace_depth == 0 {
2147                        debug!("recover_stmt_ return - close delim {:?}", self.token);
2148                        break;
2149                    }
2150                    brace_depth -= 1;
2151                    self.bump();
2152                    if in_block && bracket_depth == 0 && brace_depth == 0 {
2153                        debug!("recover_stmt_ return - block end {:?}", self.token);
2154                        break;
2155                    }
2156                }
2157                token::CloseDelim(Delimiter::Bracket) => {
2158                    bracket_depth -= 1;
2159                    if bracket_depth < 0 {
2160                        bracket_depth = 0;
2161                    }
2162                    self.bump();
2163                }
2164                token::Eof => {
2165                    debug!("recover_stmt_ return - Eof");
2166                    break;
2167                }
2168                token::Semi => {
2169                    self.bump();
2170                    if break_on_semi == SemiColonMode::Break
2171                        && brace_depth == 0
2172                        && bracket_depth == 0
2173                    {
2174                        debug!("recover_stmt_ return - Semi");
2175                        break;
2176                    }
2177                }
2178                token::Comma
2179                    if break_on_semi == SemiColonMode::Comma
2180                        && brace_depth == 0
2181                        && bracket_depth == 0 =>
2182                {
2183                    break;
2184                }
2185                _ => self.bump(),
2186            }
2187        }
2188    }
2189
2190    pub(super) fn check_for_for_in_in_typo(&mut self, in_span: Span) {
2191        if self.eat_keyword(exp!(In)) {
2192            // a common typo: `for _ in in bar {}`
2193            self.dcx().emit_err(InInTypo {
2194                span: self.prev_token.span,
2195                sugg_span: in_span.until(self.prev_token.span),
2196            });
2197        }
2198    }
2199
2200    pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
2201        if let token::DocComment(..) = self.token.kind {
2202            self.dcx().emit_err(DocCommentOnParamType { span: self.token.span });
2203            self.bump();
2204        } else if self.token == token::Pound
2205            && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
2206        {
2207            let lo = self.token.span;
2208            // Skip every token until next possible arg.
2209            while self.token != token::CloseDelim(Delimiter::Bracket) {
2210                self.bump();
2211            }
2212            let sp = lo.to(self.token.span);
2213            self.bump();
2214            self.dcx().emit_err(AttributeOnParamType { span: sp });
2215        }
2216    }
2217
2218    pub(super) fn parameter_without_type(
2219        &mut self,
2220        err: &mut Diag<'_>,
2221        pat: P<ast::Pat>,
2222        require_name: bool,
2223        first_param: bool,
2224    ) -> Option<Ident> {
2225        // If we find a pattern followed by an identifier, it could be an (incorrect)
2226        // C-style parameter declaration.
2227        if self.check_ident()
2228            && self.look_ahead(1, |t| {
2229                *t == token::Comma || *t == token::CloseDelim(Delimiter::Parenthesis)
2230            })
2231        {
2232            // `fn foo(String s) {}`
2233            let ident = self.parse_ident().unwrap();
2234            let span = pat.span.with_hi(ident.span.hi());
2235
2236            err.span_suggestion(
2237                span,
2238                "declare the type after the parameter binding",
2239                "<identifier>: <type>",
2240                Applicability::HasPlaceholders,
2241            );
2242            return Some(ident);
2243        } else if require_name
2244            && (self.token == token::Comma
2245                || self.token == token::Lt
2246                || self.token == token::CloseDelim(Delimiter::Parenthesis))
2247        {
2248            let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)";
2249
2250            let (ident, self_sugg, param_sugg, type_sugg, self_span, param_span, type_span) =
2251                match pat.kind {
2252                    PatKind::Ident(_, ident, _) => (
2253                        ident,
2254                        "self: ",
2255                        ": TypeName".to_string(),
2256                        "_: ",
2257                        pat.span.shrink_to_lo(),
2258                        pat.span.shrink_to_hi(),
2259                        pat.span.shrink_to_lo(),
2260                    ),
2261                    // Also catches `fn foo(&a)`.
2262                    PatKind::Ref(ref inner_pat, mutab)
2263                        if matches!(inner_pat.clone().into_inner().kind, PatKind::Ident(..)) =>
2264                    {
2265                        match inner_pat.clone().into_inner().kind {
2266                            PatKind::Ident(_, ident, _) => {
2267                                let mutab = mutab.prefix_str();
2268                                (
2269                                    ident,
2270                                    "self: ",
2271                                    format!("{ident}: &{mutab}TypeName"),
2272                                    "_: ",
2273                                    pat.span.shrink_to_lo(),
2274                                    pat.span,
2275                                    pat.span.shrink_to_lo(),
2276                                )
2277                            }
2278                            _ => unreachable!(),
2279                        }
2280                    }
2281                    _ => {
2282                        // Otherwise, try to get a type and emit a suggestion.
2283                        if let Some(_) = pat.to_ty() {
2284                            err.span_suggestion_verbose(
2285                                pat.span.shrink_to_lo(),
2286                                "explicitly ignore the parameter name",
2287                                "_: ".to_string(),
2288                                Applicability::MachineApplicable,
2289                            );
2290                            err.note(rfc_note);
2291                        }
2292
2293                        return None;
2294                    }
2295                };
2296
2297            // `fn foo(a, b) {}`, `fn foo(a<x>, b<y>) {}` or `fn foo(usize, usize) {}`
2298            if first_param {
2299                err.span_suggestion_verbose(
2300                    self_span,
2301                    "if this is a `self` type, give it a parameter name",
2302                    self_sugg,
2303                    Applicability::MaybeIncorrect,
2304                );
2305            }
2306            // Avoid suggesting that `fn foo(HashMap<u32>)` is fixed with a change to
2307            // `fn foo(HashMap: TypeName<u32>)`.
2308            if self.token != token::Lt {
2309                err.span_suggestion_verbose(
2310                    param_span,
2311                    "if this is a parameter name, give it a type",
2312                    param_sugg,
2313                    Applicability::HasPlaceholders,
2314                );
2315            }
2316            err.span_suggestion_verbose(
2317                type_span,
2318                "if this is a type, explicitly ignore the parameter name",
2319                type_sugg,
2320                Applicability::MachineApplicable,
2321            );
2322            err.note(rfc_note);
2323
2324            // Don't attempt to recover by using the `X` in `X<Y>` as the parameter name.
2325            return if self.token == token::Lt { None } else { Some(ident) };
2326        }
2327        None
2328    }
2329
2330    pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> {
2331        let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?;
2332        self.expect(exp!(Colon))?;
2333        let ty = self.parse_ty()?;
2334
2335        self.dcx().emit_err(PatternMethodParamWithoutBody { span: pat.span });
2336
2337        // Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
2338        let pat =
2339            P(Pat { kind: PatKind::Wild, span: pat.span, id: ast::DUMMY_NODE_ID, tokens: None });
2340        Ok((pat, ty))
2341    }
2342
2343    pub(super) fn recover_bad_self_param(&mut self, mut param: Param) -> PResult<'a, Param> {
2344        let span = param.pat.span;
2345        let guar = self.dcx().emit_err(SelfParamNotFirst { span });
2346        param.ty.kind = TyKind::Err(guar);
2347        Ok(param)
2348    }
2349
2350    pub(super) fn consume_block(
2351        &mut self,
2352        open: ExpTokenPair<'_>,
2353        close: ExpTokenPair<'_>,
2354        consume_close: ConsumeClosingDelim,
2355    ) {
2356        let mut brace_depth = 0;
2357        loop {
2358            if self.eat(open) {
2359                brace_depth += 1;
2360            } else if self.check(close) {
2361                if brace_depth == 0 {
2362                    if let ConsumeClosingDelim::Yes = consume_close {
2363                        // Some of the callers of this method expect to be able to parse the
2364                        // closing delimiter themselves, so we leave it alone. Otherwise we advance
2365                        // the parser.
2366                        self.bump();
2367                    }
2368                    return;
2369                } else {
2370                    self.bump();
2371                    brace_depth -= 1;
2372                    continue;
2373                }
2374            } else if self.token == token::Eof {
2375                return;
2376            } else {
2377                self.bump();
2378            }
2379        }
2380    }
2381
2382    pub(super) fn expected_expression_found(&self) -> Diag<'a> {
2383        let (span, msg) = match (&self.token.kind, self.subparser_name) {
2384            (&token::Eof, Some(origin)) => {
2385                let sp = self.prev_token.span.shrink_to_hi();
2386                (sp, format!("expected expression, found end of {origin}"))
2387            }
2388            _ => (
2389                self.token.span,
2390                format!("expected expression, found {}", super::token_descr(&self.token)),
2391            ),
2392        };
2393        let mut err = self.dcx().struct_span_err(span, msg);
2394        let sp = self.psess.source_map().start_point(self.token.span);
2395        if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
2396            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
2397        }
2398        err.span_label(span, "expected expression");
2399        err
2400    }
2401
2402    fn consume_tts(
2403        &mut self,
2404        mut acc: i64, // `i64` because malformed code can have more closing delims than opening.
2405        // Not using `FxHashMap` due to `token::TokenKind: !Eq + !Hash`.
2406        modifier: &[(token::TokenKind, i64)],
2407    ) {
2408        while acc > 0 {
2409            if let Some((_, val)) = modifier.iter().find(|(t, _)| self.token == *t) {
2410                acc += *val;
2411            }
2412            if self.token == token::Eof {
2413                break;
2414            }
2415            self.bump();
2416        }
2417    }
2418
2419    /// Replace duplicated recovered parameters with `_` pattern to avoid unnecessary errors.
2420    ///
2421    /// This is necessary because at this point we don't know whether we parsed a function with
2422    /// anonymous parameters or a function with names but no types. In order to minimize
2423    /// unnecessary errors, we assume the parameters are in the shape of `fn foo(a, b, c)` where
2424    /// the parameters are *names* (so we don't emit errors about not being able to find `b` in
2425    /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`,
2426    /// we deduplicate them to not complain about duplicated parameter names.
2427    pub(super) fn deduplicate_recovered_params_names(&self, fn_inputs: &mut ThinVec<Param>) {
2428        let mut seen_inputs = FxHashSet::default();
2429        for input in fn_inputs.iter_mut() {
2430            let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err(_)) =
2431                (&input.pat.kind, &input.ty.kind)
2432            {
2433                Some(*ident)
2434            } else {
2435                None
2436            };
2437            if let Some(ident) = opt_ident {
2438                if seen_inputs.contains(&ident) {
2439                    input.pat.kind = PatKind::Wild;
2440                }
2441                seen_inputs.insert(ident);
2442            }
2443        }
2444    }
2445
2446    /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this
2447    /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks
2448    /// like the user has forgotten them.
2449    pub(super) fn handle_ambiguous_unbraced_const_arg(
2450        &mut self,
2451        args: &mut ThinVec<AngleBracketedArg>,
2452    ) -> PResult<'a, bool> {
2453        // If we haven't encountered a closing `>`, then the argument is malformed.
2454        // It's likely that the user has written a const expression without enclosing it
2455        // in braces, so we try to recover here.
2456        let arg = args.pop().unwrap();
2457        // FIXME: for some reason using `unexpected` or `expected_one_of_not_found` has
2458        // adverse side-effects to subsequent errors and seems to advance the parser.
2459        // We are causing this error here exclusively in case that a `const` expression
2460        // could be recovered from the current parser state, even if followed by more
2461        // arguments after a comma.
2462        let mut err = self.dcx().struct_span_err(
2463            self.token.span,
2464            format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)),
2465        );
2466        err.span_label(self.token.span, "expected one of `,` or `>`");
2467        match self.recover_const_arg(arg.span(), err) {
2468            Ok(arg) => {
2469                args.push(AngleBracketedArg::Arg(arg));
2470                if self.eat(exp!(Comma)) {
2471                    return Ok(true); // Continue
2472                }
2473            }
2474            Err(err) => {
2475                args.push(arg);
2476                // We will emit a more generic error later.
2477                err.delay_as_bug();
2478            }
2479        }
2480        Ok(false) // Don't continue.
2481    }
2482
2483    /// Attempt to parse a generic const argument that has not been enclosed in braces.
2484    /// There are a limited number of expressions that are permitted without being encoded
2485    /// in braces:
2486    /// - Literals.
2487    /// - Single-segment paths (i.e. standalone generic const parameters).
2488    /// All other expressions that can be parsed will emit an error suggesting the expression be
2489    /// wrapped in braces.
2490    pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
2491        let start = self.token.span;
2492        let attrs = self.parse_outer_attributes()?;
2493        let (expr, _) =
2494            self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
2495                err.span_label(
2496                    start.shrink_to_lo(),
2497                    "while parsing a const generic argument starting here",
2498                );
2499                err
2500            })?;
2501        if !self.expr_is_valid_const_arg(&expr) {
2502            self.dcx().emit_err(ConstGenericWithoutBraces {
2503                span: expr.span,
2504                sugg: ConstGenericWithoutBracesSugg {
2505                    left: expr.span.shrink_to_lo(),
2506                    right: expr.span.shrink_to_hi(),
2507                },
2508            });
2509        }
2510        Ok(expr)
2511    }
2512
2513    fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
2514        let snapshot = self.create_snapshot_for_diagnostic();
2515        let param = match self.parse_const_param(AttrVec::new()) {
2516            Ok(param) => param,
2517            Err(err) => {
2518                err.cancel();
2519                self.restore_snapshot(snapshot);
2520                return None;
2521            }
2522        };
2523
2524        let ident = param.ident.to_string();
2525        let sugg = match (ty_generics, self.psess.source_map().span_to_snippet(param.span())) {
2526            (Some(Generics { params, span: impl_generics, .. }), Ok(snippet)) => {
2527                Some(match &params[..] {
2528                    [] => UnexpectedConstParamDeclarationSugg::AddParam {
2529                        impl_generics: *impl_generics,
2530                        incorrect_decl: param.span(),
2531                        snippet,
2532                        ident,
2533                    },
2534                    [.., generic] => UnexpectedConstParamDeclarationSugg::AppendParam {
2535                        impl_generics_end: generic.span().shrink_to_hi(),
2536                        incorrect_decl: param.span(),
2537                        snippet,
2538                        ident,
2539                    },
2540                })
2541            }
2542            _ => None,
2543        };
2544        let guar =
2545            self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg });
2546
2547        let value = self.mk_expr_err(param.span(), guar);
2548        Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
2549    }
2550
2551    pub(super) fn recover_const_param_declaration(
2552        &mut self,
2553        ty_generics: Option<&Generics>,
2554    ) -> PResult<'a, Option<GenericArg>> {
2555        // We have to check for a few different cases.
2556        if let Some(arg) = self.recover_const_param_decl(ty_generics) {
2557            return Ok(Some(arg));
2558        }
2559
2560        // We haven't consumed `const` yet.
2561        let start = self.token.span;
2562        self.bump(); // `const`
2563
2564        // Detect and recover from the old, pre-RFC2000 syntax for const generics.
2565        let mut err = UnexpectedConstInGenericParam { span: start, to_remove: None };
2566        if self.check_const_arg() {
2567            err.to_remove = Some(start.until(self.token.span));
2568            self.dcx().emit_err(err);
2569            Ok(Some(GenericArg::Const(self.parse_const_arg()?)))
2570        } else {
2571            let after_kw_const = self.token.span;
2572            self.recover_const_arg(after_kw_const, self.dcx().create_err(err)).map(Some)
2573        }
2574    }
2575
2576    /// Try to recover from possible generic const argument without `{` and `}`.
2577    ///
2578    /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest
2579    /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion
2580    /// if we think that the resulting expression would be well formed.
2581    pub(super) fn recover_const_arg(
2582        &mut self,
2583        start: Span,
2584        mut err: Diag<'a>,
2585    ) -> PResult<'a, GenericArg> {
2586        let is_op_or_dot = AssocOp::from_token(&self.token)
2587            .and_then(|op| {
2588                if let AssocOp::Binary(
2589                    BinOpKind::Gt
2590                    | BinOpKind::Lt
2591                    | BinOpKind::Shr
2592                    | BinOpKind::Ge
2593                )
2594                // Don't recover from `foo::<bar = baz>`, because this could be an attempt to
2595                // assign a value to a defaulted generic parameter.
2596                | AssocOp::Assign
2597                | AssocOp::AssignOp(_) = op
2598                {
2599                    None
2600                } else {
2601                    Some(op)
2602                }
2603            })
2604            .is_some()
2605            || self.token == TokenKind::Dot;
2606        // This will be true when a trait object type `Foo +` or a path which was a `const fn` with
2607        // type params has been parsed.
2608        let was_op = matches!(self.prev_token.kind, token::Plus | token::Shr | token::Gt);
2609        if !is_op_or_dot && !was_op {
2610            // We perform these checks and early return to avoid taking a snapshot unnecessarily.
2611            return Err(err);
2612        }
2613        let snapshot = self.create_snapshot_for_diagnostic();
2614        if is_op_or_dot {
2615            self.bump();
2616        }
2617        match (|| {
2618            let attrs = self.parse_outer_attributes()?;
2619            self.parse_expr_res(Restrictions::CONST_EXPR, attrs)
2620        })() {
2621            Ok((expr, _)) => {
2622                // Find a mistake like `MyTrait<Assoc == S::Assoc>`.
2623                if snapshot.token == token::EqEq {
2624                    err.span_suggestion(
2625                        snapshot.token.span,
2626                        "if you meant to use an associated type binding, replace `==` with `=`",
2627                        "=",
2628                        Applicability::MaybeIncorrect,
2629                    );
2630                    let guar = err.emit();
2631                    let value = self.mk_expr_err(start.to(expr.span), guar);
2632                    return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
2633                } else if snapshot.token == token::Colon
2634                    && expr.span.lo() == snapshot.token.span.hi()
2635                    && matches!(expr.kind, ExprKind::Path(..))
2636                {
2637                    // Find a mistake like "foo::var:A".
2638                    err.span_suggestion(
2639                        snapshot.token.span,
2640                        "write a path separator here",
2641                        "::",
2642                        Applicability::MaybeIncorrect,
2643                    );
2644                    let guar = err.emit();
2645                    return Ok(GenericArg::Type(
2646                        self.mk_ty(start.to(expr.span), TyKind::Err(guar)),
2647                    ));
2648                } else if self.token == token::Comma || self.token.kind.should_end_const_arg() {
2649                    // Avoid the following output by checking that we consumed a full const arg:
2650                    // help: expressions must be enclosed in braces to be used as const generic
2651                    //       arguments
2652                    //    |
2653                    // LL |     let sr: Vec<{ (u32, _, _) = vec![] };
2654                    //    |                 ^                      ^
2655                    return Ok(self.dummy_const_arg_needs_braces(err, start.to(expr.span)));
2656                }
2657            }
2658            Err(err) => {
2659                err.cancel();
2660            }
2661        }
2662        self.restore_snapshot(snapshot);
2663        Err(err)
2664    }
2665
2666    /// Try to recover from an unbraced const argument whose first token [could begin a type][ty].
2667    ///
2668    /// [ty]: token::Token::can_begin_type
2669    pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty(
2670        &mut self,
2671        mut snapshot: SnapshotParser<'a>,
2672    ) -> Option<P<ast::Expr>> {
2673        match (|| {
2674            let attrs = self.parse_outer_attributes()?;
2675            snapshot.parse_expr_res(Restrictions::CONST_EXPR, attrs)
2676        })() {
2677            // Since we don't know the exact reason why we failed to parse the type or the
2678            // expression, employ a simple heuristic to weed out some pathological cases.
2679            Ok((expr, _)) if let token::Comma | token::Gt = snapshot.token.kind => {
2680                self.restore_snapshot(snapshot);
2681                Some(expr)
2682            }
2683            Ok(_) => None,
2684            Err(err) => {
2685                err.cancel();
2686                None
2687            }
2688        }
2689    }
2690
2691    /// Creates a dummy const argument, and reports that the expression must be enclosed in braces
2692    pub(super) fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg {
2693        err.multipart_suggestion(
2694            "expressions must be enclosed in braces to be used as const generic \
2695             arguments",
2696            vec![(span.shrink_to_lo(), "{ ".to_string()), (span.shrink_to_hi(), " }".to_string())],
2697            Applicability::MaybeIncorrect,
2698        );
2699        let guar = err.emit();
2700        let value = self.mk_expr_err(span, guar);
2701        GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
2702    }
2703
2704    /// Some special error handling for the "top-level" patterns in a match arm,
2705    /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
2706    pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
2707        &mut self,
2708        mut first_pat: P<Pat>,
2709        expected: Option<Expected>,
2710    ) -> P<Pat> {
2711        if token::Colon != self.token.kind {
2712            return first_pat;
2713        }
2714        if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
2715            || !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
2716        {
2717            let mut snapshot_type = self.create_snapshot_for_diagnostic();
2718            snapshot_type.bump(); // `:`
2719            match snapshot_type.parse_ty() {
2720                Err(inner_err) => {
2721                    inner_err.cancel();
2722                }
2723                Ok(ty) => {
2724                    let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
2725                        return first_pat;
2726                    };
2727                    err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2728                    self.restore_snapshot(snapshot_type);
2729                    let span = first_pat.span.to(ty.span);
2730                    first_pat = self.mk_pat(span, PatKind::Wild);
2731                    err.emit();
2732                }
2733            }
2734            return first_pat;
2735        }
2736        // The pattern looks like it might be a path with a `::` -> `:` typo:
2737        // `match foo { bar:baz => {} }`
2738        let colon_span = self.token.span;
2739        // We only emit "unexpected `:`" error here if we can successfully parse the
2740        // whole pattern correctly in that case.
2741        let mut snapshot_pat = self.create_snapshot_for_diagnostic();
2742        let mut snapshot_type = self.create_snapshot_for_diagnostic();
2743
2744        // Create error for "unexpected `:`".
2745        match self.expected_one_of_not_found(&[], &[]) {
2746            Err(mut err) => {
2747                // Skip the `:`.
2748                snapshot_pat.bump();
2749                snapshot_type.bump();
2750                match snapshot_pat.parse_pat_no_top_alt(expected, None) {
2751                    Err(inner_err) => {
2752                        inner_err.cancel();
2753                    }
2754                    Ok(mut pat) => {
2755                        // We've parsed the rest of the pattern.
2756                        let new_span = first_pat.span.to(pat.span);
2757                        let mut show_sugg = false;
2758                        // Try to construct a recovered pattern.
2759                        match &mut pat.kind {
2760                            PatKind::Struct(qself @ None, path, ..)
2761                            | PatKind::TupleStruct(qself @ None, path, _)
2762                            | PatKind::Path(qself @ None, path) => match &first_pat.kind {
2763                                PatKind::Ident(_, ident, _) => {
2764                                    path.segments.insert(0, PathSegment::from_ident(*ident));
2765                                    path.span = new_span;
2766                                    show_sugg = true;
2767                                    first_pat = pat;
2768                                }
2769                                PatKind::Path(old_qself, old_path) => {
2770                                    path.segments = old_path
2771                                        .segments
2772                                        .iter()
2773                                        .cloned()
2774                                        .chain(take(&mut path.segments))
2775                                        .collect();
2776                                    path.span = new_span;
2777                                    *qself = old_qself.clone();
2778                                    first_pat = pat;
2779                                    show_sugg = true;
2780                                }
2781                                _ => {}
2782                            },
2783                            PatKind::Ident(BindingMode::NONE, ident, None) => {
2784                                match &first_pat.kind {
2785                                    PatKind::Ident(_, old_ident, _) => {
2786                                        let path = PatKind::Path(
2787                                            None,
2788                                            Path {
2789                                                span: new_span,
2790                                                segments: thin_vec![
2791                                                    PathSegment::from_ident(*old_ident),
2792                                                    PathSegment::from_ident(*ident),
2793                                                ],
2794                                                tokens: None,
2795                                            },
2796                                        );
2797                                        first_pat = self.mk_pat(new_span, path);
2798                                        show_sugg = true;
2799                                    }
2800                                    PatKind::Path(old_qself, old_path) => {
2801                                        let mut segments = old_path.segments.clone();
2802                                        segments.push(PathSegment::from_ident(*ident));
2803                                        let path = PatKind::Path(
2804                                            old_qself.clone(),
2805                                            Path { span: new_span, segments, tokens: None },
2806                                        );
2807                                        first_pat = self.mk_pat(new_span, path);
2808                                        show_sugg = true;
2809                                    }
2810                                    _ => {}
2811                                }
2812                            }
2813                            _ => {}
2814                        }
2815                        if show_sugg {
2816                            err.span_suggestion_verbose(
2817                                colon_span.until(self.look_ahead(1, |t| t.span)),
2818                                "maybe write a path separator here",
2819                                "::",
2820                                Applicability::MaybeIncorrect,
2821                            );
2822                        } else {
2823                            first_pat = self.mk_pat(new_span, PatKind::Wild);
2824                        }
2825                        self.restore_snapshot(snapshot_pat);
2826                    }
2827                }
2828                match snapshot_type.parse_ty() {
2829                    Err(inner_err) => {
2830                        inner_err.cancel();
2831                    }
2832                    Ok(ty) => {
2833                        err.span_label(ty.span, "specifying the type of a pattern isn't supported");
2834                        self.restore_snapshot(snapshot_type);
2835                        let new_span = first_pat.span.to(ty.span);
2836                        first_pat = self.mk_pat(new_span, PatKind::Wild);
2837                    }
2838                }
2839                err.emit();
2840            }
2841            _ => {
2842                // Carry on as if we had not done anything. This should be unreachable.
2843            }
2844        };
2845        first_pat
2846    }
2847
2848    /// If `loop_header` is `Some` and an unexpected block label is encountered,
2849    /// it is suggested to be moved just before `loop_header`, else it is suggested to be removed.
2850    pub(crate) fn maybe_recover_unexpected_block_label(
2851        &mut self,
2852        loop_header: Option<Span>,
2853    ) -> bool {
2854        // Check for `'a : {`
2855        if !(self.check_lifetime()
2856            && self.look_ahead(1, |t| *t == token::Colon)
2857            && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace)))
2858        {
2859            return false;
2860        }
2861        let label = self.eat_label().expect("just checked if a label exists");
2862        self.bump(); // eat `:`
2863        let span = label.ident.span.to(self.prev_token.span);
2864        let mut diag = self
2865            .dcx()
2866            .struct_span_err(span, "block label not supported here")
2867            .with_span_label(span, "not supported here");
2868        if let Some(loop_header) = loop_header {
2869            diag.multipart_suggestion(
2870                "if you meant to label the loop, move this label before the loop",
2871                vec![
2872                    (label.ident.span.until(self.token.span), String::from("")),
2873                    (loop_header.shrink_to_lo(), format!("{}: ", label.ident)),
2874                ],
2875                Applicability::MachineApplicable,
2876            );
2877        } else {
2878            diag.tool_only_span_suggestion(
2879                label.ident.span.until(self.token.span),
2880                "remove this block label",
2881                "",
2882                Applicability::MachineApplicable,
2883            );
2884        }
2885        diag.emit();
2886        true
2887    }
2888
2889    /// Some special error handling for the "top-level" patterns in a match arm,
2890    /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
2891    pub(crate) fn maybe_recover_unexpected_comma(
2892        &mut self,
2893        lo: Span,
2894        rt: CommaRecoveryMode,
2895    ) -> PResult<'a, ()> {
2896        if self.token != token::Comma {
2897            return Ok(());
2898        }
2899
2900        // An unexpected comma after a top-level pattern is a clue that the
2901        // user (perhaps more accustomed to some other language) forgot the
2902        // parentheses in what should have been a tuple pattern; return a
2903        // suggestion-enhanced error here rather than choking on the comma later.
2904        let comma_span = self.token.span;
2905        self.bump();
2906        if let Err(err) = self.skip_pat_list() {
2907            // We didn't expect this to work anyway; we just wanted to advance to the
2908            // end of the comma-sequence so we know the span to suggest parenthesizing.
2909            err.cancel();
2910        }
2911        let seq_span = lo.to(self.prev_token.span);
2912        let mut err = self.dcx().struct_span_err(comma_span, "unexpected `,` in pattern");
2913        if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
2914            err.multipart_suggestion(
2915                format!(
2916                    "try adding parentheses to match on a tuple{}",
2917                    if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
2918                ),
2919                vec![
2920                    (seq_span.shrink_to_lo(), "(".to_string()),
2921                    (seq_span.shrink_to_hi(), ")".to_string()),
2922                ],
2923                Applicability::MachineApplicable,
2924            );
2925            if let CommaRecoveryMode::EitherTupleOrPipe = rt {
2926                err.span_suggestion(
2927                    seq_span,
2928                    "...or a vertical bar to match on multiple alternatives",
2929                    seq_snippet.replace(',', " |"),
2930                    Applicability::MachineApplicable,
2931                );
2932            }
2933        }
2934        Err(err)
2935    }
2936
2937    pub(crate) fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> {
2938        let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) };
2939        let qself_position = qself.as_ref().map(|qself| qself.position);
2940        for (i, segments) in path.segments.windows(2).enumerate() {
2941            if qself_position.is_some_and(|pos| i < pos) {
2942                continue;
2943            }
2944            if let [a, b] = segments {
2945                let (a_span, b_span) = (a.span(), b.span());
2946                let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo());
2947                if self.span_to_snippet(between_span).as_deref() == Ok(":: ") {
2948                    return Err(self.dcx().create_err(DoubleColonInBound {
2949                        span: path.span.shrink_to_hi(),
2950                        between: between_span,
2951                    }));
2952                }
2953            }
2954        }
2955        Ok(())
2956    }
2957
2958    /// Check for exclusive ranges written as `..<`
2959    pub(crate) fn maybe_err_dotdotlt_syntax(&self, maybe_lt: Token, mut err: Diag<'a>) -> Diag<'a> {
2960        if maybe_lt == token::Lt
2961            && (self.expected_token_types.contains(TokenType::Gt)
2962                || matches!(self.token.kind, token::Literal(..)))
2963        {
2964            err.span_suggestion(
2965                maybe_lt.span,
2966                "remove the `<` to write an exclusive range",
2967                "",
2968                Applicability::MachineApplicable,
2969            );
2970        }
2971        err
2972    }
2973
2974    /// This checks if this is a conflict marker, depending of the parameter passed.
2975    ///
2976    /// * `<<<<<<<`
2977    /// * `|||||||`
2978    /// * `=======`
2979    /// * `>>>>>>>`
2980    ///
2981    pub(super) fn is_vcs_conflict_marker(
2982        &mut self,
2983        long_kind: &TokenKind,
2984        short_kind: &TokenKind,
2985    ) -> bool {
2986        (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind))
2987            && self.look_ahead(3, |tok| tok == short_kind)
2988    }
2989
2990    fn conflict_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option<Span> {
2991        if self.is_vcs_conflict_marker(long_kind, short_kind) {
2992            let lo = self.token.span;
2993            for _ in 0..4 {
2994                self.bump();
2995            }
2996            return Some(lo.to(self.prev_token.span));
2997        }
2998        None
2999    }
3000
3001    pub(super) fn recover_vcs_conflict_marker(&mut self) {
3002        // <<<<<<<
3003        let Some(start) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt) else {
3004            return;
3005        };
3006        let mut spans = Vec::with_capacity(3);
3007        spans.push(start);
3008        // |||||||
3009        let mut middlediff3 = None;
3010        // =======
3011        let mut middle = None;
3012        // >>>>>>>
3013        let mut end = None;
3014        loop {
3015            if self.token == TokenKind::Eof {
3016                break;
3017            }
3018            if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or) {
3019                middlediff3 = Some(span);
3020            }
3021            if let Some(span) = self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq) {
3022                middle = Some(span);
3023            }
3024            if let Some(span) = self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt) {
3025                spans.push(span);
3026                end = Some(span);
3027                break;
3028            }
3029            self.bump();
3030        }
3031
3032        let mut err = self.dcx().struct_span_fatal(spans, "encountered diff marker");
3033        match middlediff3 {
3034            // We're using diff3
3035            Some(middlediff3) => {
3036                err.span_label(
3037                    start,
3038                    "between this marker and `|||||||` is the code that we're merging into",
3039                );
3040                err.span_label(middlediff3, "between this marker and `=======` is the base code (what the two refs diverged from)");
3041            }
3042            None => {
3043                err.span_label(
3044                    start,
3045                    "between this marker and `=======` is the code that we're merging into",
3046                );
3047            }
3048        };
3049
3050        if let Some(middle) = middle {
3051            err.span_label(middle, "between this marker and `>>>>>>>` is the incoming code");
3052        }
3053        if let Some(end) = end {
3054            err.span_label(end, "this marker concludes the conflict region");
3055        }
3056        err.note(
3057            "conflict markers indicate that a merge was started but could not be completed due \
3058             to merge conflicts\n\
3059             to resolve a conflict, keep only the code you want and then delete the lines \
3060             containing conflict markers",
3061        );
3062        err.help(
3063            "if you're having merge conflicts after pulling new code:\n\
3064             the top section is the code you already had and the bottom section is the remote code\n\
3065             if you're in the middle of a rebase:\n\
3066             the top section is the code being rebased onto and the bottom section is the code \
3067             coming from the current commit being rebased",
3068        );
3069
3070        err.note(
3071            "for an explanation on these markers from the `git` documentation:\n\
3072             visit <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>",
3073        );
3074
3075        err.emit();
3076    }
3077
3078    /// Parse and throw away a parenthesized comma separated
3079    /// sequence of patterns until `)` is reached.
3080    fn skip_pat_list(&mut self) -> PResult<'a, ()> {
3081        while !self.check(exp!(CloseParen)) {
3082            self.parse_pat_no_top_alt(None, None)?;
3083            if !self.eat(exp!(Comma)) {
3084                return Ok(());
3085            }
3086        }
3087        Ok(())
3088    }
3089}