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

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy