compiler/rustc_parse/src/parser/diagnostics.rs RUST 3,191 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 3,191.
1use std::mem::take;2use std::ops::{Deref, DerefMut};34use ast::token::IdentIsRaw;5use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind};6use rustc_ast::util::parser::AssocOp;7use rustc_ast::{8    self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode,9    Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind,10    Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind,11};12use rustc_ast_pretty::pprust;13use rustc_data_structures::fx::FxHashSet;14use rustc_errors::{15    Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions, msg,16    pluralize,17};18use rustc_span::symbol::used_keywords;19use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Spanned, Symbol, kw, sym};20use thin_vec::{ThinVec, thin_vec};21use tracing::{debug, trace};2223use super::pat::Expected;24use super::{25    BlockMode, CommaRecoveryMode, ExpTokenPair, Parser, PathStyle, Restrictions, SemiColonMode,26    SeqSep, TokenType,27};28use crate::diagnostics::{29    AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType,30    AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,31    ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,32    DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound,33    ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, ExprParenthesesNeeded, FoundPathInGenerics,34    GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,35    HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,36    IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, MisspelledKw,37    PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,38    StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt,39    SuggEscapeIdentifier, SuggRemoveComma, SuggestBindTypeParameter, SuggestIntroduceTypeParameter,40    TernaryOperator, TernaryOperatorSuggestion, UnexpectedConstInGenericParam,41    UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,42    UseEqInstead, WrapType,43};44use crate::exp;45use crate::parser::attr::InnerAttrPolicy;46use crate::parser::{FnContext, IsDotDotDot};4748/// Creates a placeholder argument.49pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param {50    let pat = Box::new(Pat {51        id: ast::DUMMY_NODE_ID,52        kind: PatKind::Ident(BindingMode::NONE, ident, None),53        span: ident.span,54    });55    let ty = Ty { kind: TyKind::Err(guar), span: ident.span, id: ast::DUMMY_NODE_ID };56    Param {57        attrs: AttrVec::default(),58        id: ast::DUMMY_NODE_ID,59        pat,60        span: ident.span,61        ty: Box::new(ty),62        is_placeholder: false,63    }64}6566pub(super) trait RecoverQPath: Sized + 'static {67    const PATH_STYLE: PathStyle = PathStyle::Expr;68    fn to_ty(&self) -> Option<Box<Ty>>;69    fn recovered(qself: Option<Box<QSelf>>, path: ast::Path) -> Self;70}7172impl<T: RecoverQPath> RecoverQPath for Box<T> {73    const PATH_STYLE: PathStyle = T::PATH_STYLE;74    fn to_ty(&self) -> Option<Box<Ty>> {75        T::to_ty(self)76    }77    fn recovered(qself: Option<Box<QSelf>>, path: ast::Path) -> Self {78        Box::new(T::recovered(qself, path))79    }80}8182impl RecoverQPath for Ty {83    const PATH_STYLE: PathStyle = PathStyle::Type;84    fn to_ty(&self) -> Option<Box<Ty>> {85        Some(Box::new(self.clone()))86    }87    fn recovered(qself: Option<Box<QSelf>>, path: ast::Path) -> Self {88        Self { span: path.span, kind: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID }89    }90}9192impl RecoverQPath for Pat {93    const PATH_STYLE: PathStyle = PathStyle::Pat;94    fn to_ty(&self) -> Option<Box<Ty>> {95        self.to_ty()96    }97    fn recovered(qself: Option<Box<QSelf>>, path: ast::Path) -> Self {98        Self { span: path.span, kind: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID }99    }100}101102impl RecoverQPath for Expr {103    fn to_ty(&self) -> Option<Box<Ty>> {104        self.to_ty()105    }106    fn recovered(qself: Option<Box<QSelf>>, path: ast::Path) -> Self {107        Self {108            span: path.span,109            kind: ExprKind::Path(qself, path),110            attrs: AttrVec::new(),111            id: ast::DUMMY_NODE_ID,112            tokens: None,113        }114    }115}116117/// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`.118pub(crate) enum ConsumeClosingDelim {119    Yes,120    No,121}122123#[derive(Clone, Copy)]124pub enum AttemptLocalParseRecovery {125    Yes,126    No,127}128129impl AttemptLocalParseRecovery {130    pub(super) fn yes(&self) -> bool {131        match self {132            AttemptLocalParseRecovery::Yes => true,133            AttemptLocalParseRecovery::No => false,134        }135    }136137    pub(super) fn no(&self) -> bool {138        match self {139            AttemptLocalParseRecovery::Yes => false,140            AttemptLocalParseRecovery::No => true,141        }142    }143}144145/// Information for emitting suggestions and recovering from146/// C-style `i++`, `--i`, etc.147#[derive(Debug, Copy, Clone)]148struct IncDecRecovery {149    /// Is this increment/decrement its own statement?150    standalone: IsStandalone,151    /// Is this an increment or decrement?152    op: IncOrDec,153    /// Is this pre- or postfix?154    fixity: UnaryFixity,155}156157/// Is an increment or decrement expression its own statement?158#[derive(Debug, Copy, Clone)]159enum IsStandalone {160    /// It's standalone, i.e., its own statement.161    Standalone,162    /// It's a subexpression, i.e., *not* standalone.163    Subexpr,164}165166#[derive(Debug, Copy, Clone, PartialEq, Eq)]167enum IncOrDec {168    Inc,169    Dec,170}171172#[derive(Debug, Copy, Clone, PartialEq, Eq)]173enum UnaryFixity {174    Pre,175    Post,176}177178impl IncOrDec {179    fn chr(&self) -> char {180        match self {181            Self::Inc => '+',182            Self::Dec => '-',183        }184    }185186    fn name(&self) -> &'static str {187        match self {188            Self::Inc => "increment",189            Self::Dec => "decrement",190        }191    }192}193194impl std::fmt::Display for UnaryFixity {195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {196        match self {197            Self::Pre => write!(f, "prefix"),198            Self::Post => write!(f, "postfix"),199        }200    }201}202203/// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`.204///205/// This is a specialized version of [`Symbol::find_similar`] that constructs an error when a206/// candidate is found.207fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> {208    lookup.name.find_similar(candidates).map(|(similar_kw, is_incorrect_case)| MisspelledKw {209        similar_kw: similar_kw.to_string(),210        is_incorrect_case,211        span: lookup.span,212    })213}214215struct MultiSugg {216    msg: String,217    patches: Vec<(Span, String)>,218    applicability: Applicability,219}220221impl MultiSugg {222    fn emit(self, err: &mut Diag<'_>) {223        err.multipart_suggestion(self.msg, self.patches, self.applicability);224    }225226    fn emit_verbose(self, err: &mut Diag<'_>) {227        err.multipart_suggestion(self.msg, self.patches, self.applicability);228    }229}230231/// SnapshotParser is used to create a snapshot of the parser232/// without causing duplicate errors being emitted when the `Parser`233/// is dropped.234pub struct SnapshotParser<'a> {235    parser: Parser<'a>,236}237238impl<'a> Deref for SnapshotParser<'a> {239    type Target = Parser<'a>;240241    fn deref(&self) -> &Self::Target {242        &self.parser243    }244}245246impl<'a> DerefMut for SnapshotParser<'a> {247    fn deref_mut(&mut self) -> &mut Self::Target {248        &mut self.parser249    }250}251252impl<'a> Parser<'a> {253    pub fn dcx(&self) -> DiagCtxtHandle<'a> {254        self.psess.dcx()255    }256257    /// Replace `self` with `snapshot.parser`.258    pub fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {259        *self = snapshot.parser;260    }261262    /// Create a snapshot of the `Parser`.263    pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {264        let snapshot = self.clone();265        SnapshotParser { parser: snapshot }266    }267268    pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {269        self.psess.source_map().span_to_snippet(span)270    }271272    /// Emits an error with suggestions if an identifier was expected but not found.273    ///274    /// Returns a possibly recovered identifier.275    pub(super) fn expected_ident_found(276        &mut self,277        recover: bool,278    ) -> PResult<'a, (Ident, IdentIsRaw)> {279        let valid_follow = &[280            TokenKind::Eq,281            TokenKind::Colon,282            TokenKind::Comma,283            TokenKind::Semi,284            TokenKind::PathSep,285            TokenKind::OpenBrace,286            TokenKind::OpenParen,287            TokenKind::CloseBrace,288            TokenKind::CloseParen,289        ];290        if let TokenKind::DocComment(..) = self.prev_token.kind291            && valid_follow.contains(&self.token.kind)292        {293            let err = self.dcx().create_err(DocCommentDoesNotDocumentAnything {294                span: self.prev_token.span,295                missing_comma: None,296            });297            return Err(err);298        }299300        let mut recovered_ident = None;301        // we take this here so that the correct original token is retained in302        // the diagnostic, regardless of eager recovery.303        let bad_token = self.token;304305        // suggest prepending a keyword in identifier position with `r#`306        let suggest_raw = if let Some((ident, IdentIsRaw::No)) = self.token.ident()307            && ident.is_raw_guess()308            && self.look_ahead(1, |t| valid_follow.contains(&t.kind))309        {310            recovered_ident = Some((ident, IdentIsRaw::Yes));311312            // `Symbol::to_string()` is different from `Symbol::into_diag_arg()`,313            // which uses `Symbol::to_ident_string()` and "helpfully" adds an implicit `r#`314            let ident_name = ident.name.to_string();315316            Some(SuggEscapeIdentifier { span: ident.span.shrink_to_lo(), ident_name })317        } else {318            None319        };320321        let suggest_remove_comma =322            if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) {323                if recover {324                    self.bump();325                    recovered_ident = self.ident_or_err(false).ok();326                };327328                Some(SuggRemoveComma { span: bad_token.span })329            } else {330                None331            };332333        let help_cannot_start_number = self.is_lit_bad_ident().map(|(len, valid_portion)| {334            let (invalid, valid) = self.token.span.split_at(len as u32);335336            recovered_ident = Some((Ident::new(valid_portion, valid), IdentIsRaw::No));337338            HelpIdentifierStartsWithNumber { num_span: invalid }339        });340341        let err = ExpectedIdentifier {342            span: bad_token.span,343            token: bad_token,344            suggest_raw,345            suggest_remove_comma,346            help_cannot_start_number,347        };348        let mut err = self.dcx().create_err(err);349350        // if the token we have is a `<`351        // it *might* be a misplaced generic352        // FIXME: could we recover with this?353        if self.token == token::Lt {354            // all keywords that could have generic applied355            let valid_prev_keywords =356                [kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait];357358            // If we've expected an identifier,359            // and the current token is a '<'360            // if the previous token is a valid keyword361            // that might use a generic, then suggest a correct362            // generic placement (later on)363            let maybe_keyword = self.prev_token;364            if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) {365                // if we have a valid keyword, attempt to parse generics366                // also obtain the keywords symbol367                match self.parse_generics() {368                    Ok(generic) => {369                        if let TokenKind::Ident(symbol, _) = maybe_keyword.kind {370                            let ident_name = symbol;371                            // at this point, we've found something like372                            // `fn <T>id`373                            // and current token should be Ident with the item name (i.e. the function name)374                            // if there is a `<` after the fn name, then don't show a suggestion, show help375376                            if !self.look_ahead(1, |t| *t == token::Lt)377                                && let Ok(snippet) =378                                    self.psess.source_map().span_to_snippet(generic.span)379                            {380                                err.multipart_suggestion(381                                        format!("place the generic parameter name after the {ident_name} name"),382                                        vec![383                                            (self.token.span.shrink_to_hi(), snippet),384                                            (generic.span, String::new())385                                        ],386                                        Applicability::MaybeIncorrect,387                                    );388                            } else {389                                err.help(format!(390                                    "place the generic parameter name after the {ident_name} name"391                                ));392                            }393                        }394                    }395                    Err(err) => {396                        // if there's an error parsing the generics,397                        // then don't do a misplaced generics suggestion398                        // and emit the expected ident error instead;399                        err.cancel();400                    }401                }402            }403        }404405        if let Some(recovered_ident) = recovered_ident406            && recover407        {408            err.emit();409            Ok(recovered_ident)410        } else {411            Err(err)412        }413    }414415    pub(super) fn expected_ident_found_err(&mut self) -> Diag<'a> {416        self.expected_ident_found(false).unwrap_err()417    }418419    /// Checks if the current token is a integer or float literal and looks like420    /// it could be a invalid identifier with digits at the start.421    ///422    /// Returns the number of characters (bytes) composing the invalid portion423    /// of the identifier and the valid portion of the identifier.424    pub(super) fn is_lit_bad_ident(&mut self) -> Option<(usize, Symbol)> {425        // ensure that the integer literal is followed by a *invalid*426        // suffix: this is how we know that it is a identifier with an427        // invalid beginning.428        if let token::Literal(Lit {429            kind: token::LitKind::Integer | token::LitKind::Float,430            symbol,431            suffix: Some(suffix), // no suffix makes it a valid literal432        }) = self.token.kind433            && rustc_ast::MetaItemLit::from_token(&self.token).is_none()434        {435            Some((symbol.as_str().len(), suffix))436        } else {437            None438        }439    }440441    pub(super) fn expected_one_of_not_found(442        &mut self,443        edible: &[ExpTokenPair],444        inedible: &[ExpTokenPair],445    ) -> PResult<'a, ErrorGuaranteed> {446        debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible);447        fn tokens_to_string(tokens: &[TokenType]) -> String {448            let mut i = tokens.iter();449            // This might be a sign we need a connect method on `Iterator`.450            let b = i.next().map_or_else(String::new, |t| t.to_string());451            i.enumerate().fold(b, |mut b, (i, a)| {452                if tokens.len() > 2 && i == tokens.len() - 2 {453                    b.push_str(", or ");454                } else if tokens.len() == 2 && i == tokens.len() - 2 {455                    b.push_str(" or ");456                } else {457                    b.push_str(", ");458                }459                b.push_str(&a.to_string());460                b461            })462        }463464        for exp in edible.iter().chain(inedible.iter()) {465            self.expected_token_types.insert(exp.token_type);466        }467        let mut expected: Vec<_> = self.expected_token_types.iter().collect();468        expected.sort_by_cached_key(|x| x.to_string());469        expected.dedup();470471        let sm = self.psess.source_map();472473        // Special-case "expected `;`" errors.474        if expected.contains(&TokenType::Semi) {475            // If the user is trying to write a ternary expression, recover it and476            // return an Err to prevent a cascade of irrelevant diagnostics.477            if self.prev_token == token::Question478                && let Err(e) = self.maybe_recover_from_ternary_operator(None)479            {480                return Err(e);481            }482483            if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP {484                // Likely inside a macro, can't provide meaningful suggestions.485            } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {486                // The current token is in the same line as the prior token, not recoverable.487            } else if [token::Comma, token::Colon].contains(&self.token.kind)488                && self.prev_token == token::CloseParen489            {490                // Likely typo: The current token is on a new line and is expected to be491                // `.`, `;`, `?`, or an operator after a close delimiter token.492                //493                // let a = std::process::Command::new("echo")494                //         .arg("1")495                //         ,arg("2")496                //         ^497                // https://github.com/rust-lang/rust/issues/72253498            } else if self.look_ahead(1, |t| {499                t == &token::CloseBrace || t.can_begin_expr() && *t != token::Colon500            }) && [token::Comma, token::Colon].contains(&self.token.kind)501            {502                // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is503                // either `,` or `:`, and the next token could either start a new statement or is a504                // block close. For example:505                //506                //   let x = 32:507                //   let y = 42;508                let guar = self.dcx().emit_err(ExpectedSemi {509                    span: self.token.span,510                    token: self.token,511                    unexpected_token_label: None,512                    sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),513                });514                self.bump();515                return Ok(guar);516            } else if self.look_ahead(0, |t| {517                t == &token::CloseBrace518                    || ((t.can_begin_expr() || t.can_begin_item())519                        && t != &token::Semi520                        && t != &token::Pound)521                    // Avoid triggering with too many trailing `#` in raw string.522                    || (sm.is_multiline(523                        self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),524                    ) && t == &token::Pound)525            }) && !expected.contains(&TokenType::Comma)526            {527                // Missing semicolon typo. This is triggered if the next token could either start a528                // new statement or is a block close. For example:529                //530                //   let x = 32531                //   let y = 42;532                let span = self.prev_token.span.shrink_to_hi();533                let guar = self.dcx().emit_err(ExpectedSemi {534                    span,535                    token: self.token,536                    unexpected_token_label: Some(self.token.span),537                    sugg: ExpectedSemiSugg::AddSemi(span),538                });539                return Ok(guar);540            }541        }542543        if self.token == TokenKind::EqEq544            && self.prev_token.is_ident()545            && expected.contains(&TokenType::Eq)546        {547            // Likely typo: `=` → `==` in let expr or enum item548            return Err(self.dcx().create_err(UseEqInstead { span: self.token.span }));549        }550551        if (self.token.is_keyword(kw::Move) || self.token.is_keyword(kw::Use))552            && self.prev_token.is_keyword(kw::Async)553        {554            // The 2015 edition is in use because parsing of `async move` or `async use` has failed.555            let span = self.prev_token.span.to(self.token.span);556            if self.token.is_keyword(kw::Move) {557                return Err(self.dcx().create_err(AsyncMoveBlockIn2015 { span }));558            } else {559                // kw::Use560                return Err(self.dcx().create_err(AsyncUseBlockIn2015 { span }));561            }562        }563564        let expect = tokens_to_string(&expected);565        let actual = super::token_descr(&self.token);566        let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {567            let fmt = format!("expected one of {expect}, found {actual}");568            let short_expect = if expected.len() > 6 {569                format!("{} possible tokens", expected.len())570            } else {571                expect572            };573            (fmt, (self.prev_token.span.shrink_to_hi(), format!("expected one of {short_expect}")))574        } else if expected.is_empty() {575            (576                format!("unexpected token: {actual}"),577                (self.prev_token.span, "unexpected token after this".to_string()),578            )579        } else {580            (581                format!("expected {expect}, found {actual}"),582                (self.prev_token.span.shrink_to_hi(), format!("expected {expect}")),583            )584        };585        self.last_unexpected_token_span = Some(self.token.span);586        // FIXME: translation requires list formatting (for `expect`)587        let mut err = self.dcx().struct_span_err(self.token.span, msg_exp);588589        self.label_expected_raw_ref(&mut err);590591        // Look for usages of '=>' where '>=' was probably intended592        if self.token == token::FatArrow593            && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le))594            && !expected595                .iter()596                .any(|tok| matches!(tok, TokenType::FatArrow | TokenType::CloseBrace))597        {598            err.span_suggestion_verbose(599                self.token.span,600                "you might have meant to write a \"greater than or equal to\" comparison",601                ">=",602                Applicability::MaybeIncorrect,603            );604        }605606        if let TokenKind::Ident(symbol, _) = &self.prev_token.kind {607            if ["def", "fun", "func", "function"].contains(&symbol.as_str()) {608                err.span_suggestion_short(609                    self.prev_token.span,610                    format!("write `fn` instead of `{symbol}` to declare a function"),611                    "fn",612                    Applicability::MachineApplicable,613                );614            }615        }616617        if let TokenKind::Ident(prev, _) = &self.prev_token.kind618            && let TokenKind::Ident(cur, _) = &self.token.kind619        {620            let concat = Symbol::intern(&format!("{prev}{cur}"));621            let ident = Ident::new(concat, DUMMY_SP);622            if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() {623                let concat_span = self.prev_token.span.to(self.token.span);624                err.span_suggestion_verbose(625                    concat_span,626                    format!("consider removing the space to spell keyword `{concat}`"),627                    concat,628                    Applicability::MachineApplicable,629                );630            }631        }632633        // Try to detect an intended c-string literal while using a pre-2021 edition. The heuristic634        // here is to identify a cooked, uninterpolated `c` id immediately followed by a string, or635        // a cooked, uninterpolated `cr` id immediately followed by a string or a `#`, in an edition636        // where c-string literals are not allowed. There is the very slight possibility of a false637        // positive for a `cr#` that wasn't intended to start a c-string literal, but identifying638        // that in the parser requires unbounded lookahead, so we only add a hint to the existing639        // error rather than replacing it entirely.640        if ((self.prev_token == TokenKind::Ident(sym::character('c'), IdentIsRaw::No)641            && matches!(&self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. })))642            || (self.prev_token == TokenKind::Ident(sym::cr, IdentIsRaw::No)643                && matches!(644                    &self.token.kind,645                    TokenKind::Literal(token::Lit { kind: token::Str, .. }) | token::Pound646                )))647            && self.prev_token.span.hi() == self.token.span.lo()648            && !self.token.span.at_least_rust_2021()649        {650            err.note("you may be trying to write a c-string literal");651            err.note("c-string literals require Rust 2021 or later");652            err.subdiagnostic(HelpUseLatestEdition::new());653        }654655        // `pub` may be used for an item or `pub(crate)`656        if self.prev_token.is_ident_named(sym::public)657            && (self.token.can_begin_item() || self.token == TokenKind::OpenParen)658        {659            err.span_suggestion_short(660                self.prev_token.span,661                "write `pub` instead of `public` to make the item public",662                "pub",663                Applicability::MachineApplicable,664            );665        }666667        if let token::DocComment(kind, style, _) = self.token.kind {668            // This is to avoid suggesting converting a doc comment to a regular comment669            // when missing a comma before the doc comment in lists (#142311):670            //671            // ```672            // enum Foo{673            //     A /// xxxxxxx674            //     B,675            // }676            // ```677            if !expected.contains(&TokenType::Comma) {678                // We have something like `expr //!val` where the user likely meant `expr // !val`679                let pos = self.token.span.lo() + BytePos(2);680                let span = self.token.span.with_lo(pos).with_hi(pos);681                err.span_suggestion_verbose(682                    span,683                    format!(684                        "add a space before {} to write a regular comment",685                        match (kind, style) {686                            (token::CommentKind::Line, ast::AttrStyle::Inner) => "`!`",687                            (token::CommentKind::Block, ast::AttrStyle::Inner) => "`!`",688                            (token::CommentKind::Line, ast::AttrStyle::Outer) => "the last `/`",689                            (token::CommentKind::Block, ast::AttrStyle::Outer) => "the last `*`",690                        },691                    ),692                    " ".to_string(),693                    Applicability::MaybeIncorrect,694                );695            }696        }697698        let sp = if self.token == token::Eof {699            // This is EOF; don't want to point at the following char, but rather the last token.700            self.prev_token.span701        } else {702            label_sp703        };704705        if self.check_too_many_raw_str_terminators(&mut err) {706            if expected.contains(&TokenType::Semi) && self.eat(exp!(Semi)) {707                let guar = err.emit();708                return Ok(guar);709            } else {710                return Err(err);711            }712        }713714        if self.prev_token.span == DUMMY_SP {715            // Account for macro context where the previous span might not be716            // available to avoid incorrect output (#54841).717            err.span_label(self.token.span, label_exp);718        } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) {719            // When the spans are in the same line, it means that the only content between720            // them is whitespace, point at the found token in that case:721            //722            // X |     () => { syntax error };723            //   |                    ^^^^^ expected one of 8 possible tokens here724            //725            // instead of having:726            //727            // X |     () => { syntax error };728            //   |                   -^^^^^ unexpected token729            //   |                   |730            //   |                   expected one of 8 possible tokens here731            err.span_label(self.token.span, label_exp);732        } else {733            err.span_label(sp, label_exp);734            err.span_label(self.token.span, "unexpected token");735        }736737        // Check for misspelled keywords if there are no suggestions added to the diagnostic.738        if let Suggestions::Enabled(list) = &err.suggestions739            && list.is_empty()740        {741            self.check_for_misspelled_kw(&mut err, &expected);742        }743        Err(err)744    }745746    pub(super) fn is_expected_raw_ref_mut(&self) -> bool {747        self.prev_token.is_keyword(kw::Raw)748            && self.expected_token_types.contains(TokenType::KwMut)749            && self.expected_token_types.contains(TokenType::KwConst)750            && self.token.can_begin_expr()751    }752753    /// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`.754    ///755    /// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this756    /// label may need added to other diagnostics emission paths as needed.757    pub(super) fn label_expected_raw_ref(&mut self, err: &mut Diag<'_>) {758        if self.is_expected_raw_ref_mut() {759            err.span_suggestions(760                self.prev_token.span.shrink_to_hi(),761                "`&raw` must be followed by `const` or `mut` to be a raw reference expression",762                [" const".to_string(), " mut".to_string()],763                Applicability::MaybeIncorrect,764            );765        }766    }767768    /// Checks if the current token or the previous token are misspelled keywords769    /// and adds a helpful suggestion.770    fn check_for_misspelled_kw(&self, err: &mut Diag<'_>, expected: &[TokenType]) {771        let Some((curr_ident, _)) = self.token.ident() else {772            return;773        };774        let expected_token_types: &[TokenType] =775            expected.len().checked_sub(10).map_or(&expected, |index| &expected[index..]);776        let expected_keywords: Vec<Symbol> =777            expected_token_types.iter().filter_map(|token| token.is_keyword()).collect();778779        // When there are a few keywords in the last ten elements of `self.expected_token_types`780        // and the current token is an identifier, it's probably a misspelled keyword. This handles781        // code like `async Move {}`, misspelled `if` in match guard, misspelled `else` in782        // `if`-`else` and misspelled `where` in a where clause.783        if !expected_keywords.is_empty()784            && !curr_ident.is_used_keyword()785            && let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords)786        {787            err.subdiagnostic(misspelled_kw);788            // We don't want other suggestions to be added as they are most likely meaningless789            // when there is a misspelled keyword.790            err.seal_suggestions();791        } else if let Some((prev_ident, _)) = self.prev_token.ident()792            && !prev_ident.is_used_keyword()793        {794            // We generate a list of all keywords at runtime rather than at compile time795            // so that it gets generated only when the diagnostic needs it.796            // Also, it is unlikely that this list is generated multiple times because the797            // parser halts after execution hits this path.798            let all_keywords = used_keywords(|| prev_ident.span.edition());799800            // Otherwise, check the previous token with all the keywords as possible candidates.801            // This handles code like `Struct Human;` and `While a < b {}`.802            // We check the previous token only when the current token is an identifier to avoid803            // false positives like suggesting keyword `for` for `extern crate foo {}`.804            if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) {805                err.subdiagnostic(misspelled_kw);806                // We don't want other suggestions to be added as they are most likely meaningless807                // when there is a misspelled keyword.808                err.seal_suggestions();809            }810        }811    }812813    /// The user has written `#[attr] expr` which is unsupported. (#106020)814    pub(super) fn attr_on_non_tail_expr(&self, expr: &Expr) -> ErrorGuaranteed {815        // Missing semicolon typo error.816        let span = self.prev_token.span.shrink_to_hi();817        let mut err = self.dcx().create_err(ExpectedSemi {818            span,819            token: self.token,820            unexpected_token_label: Some(self.token.span),821            sugg: ExpectedSemiSugg::AddSemi(span),822        });823        let attr_span = match &expr.attrs[..] {824            [] => unreachable!(),825            [only] => only.span,826            [first, rest @ ..] => {827                for attr in rest {828                    err.span_label(attr.span, "");829                }830                first.span831            }832        };833        err.span_label(834            attr_span,835            format!(836                "only `;` terminated statements or tail expressions are allowed after {}",837                if expr.attrs.len() == 1 { "this attribute" } else { "these attributes" },838            ),839        );840        if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {841            // We have842            // #[attr]843            // expr844            // #[not_attr]845            // other_expr846            err.span_label(span, "expected `;` here");847            err.multipart_suggestion(848                "alternatively, consider surrounding the expression with a block",849                vec![850                    (expr.span.shrink_to_lo(), "{ ".to_string()),851                    (expr.span.shrink_to_hi(), " }".to_string()),852                ],853                Applicability::MachineApplicable,854            );855856            // Special handling for `#[cfg(...)]` chains857            let mut snapshot = self.create_snapshot_for_diagnostic();858            if let [attr] = &expr.attrs[..]859                && let ast::AttrKind::Normal(attr_kind) = &attr.kind860                && let [segment] = &attr_kind.item.path.segments[..]861                && segment.ident.name == sym::cfg862                && let Some(args_span) = attr_kind.item.args.span()863                && let next_attr = match snapshot.parse_attribute(InnerAttrPolicy::Forbidden(None))864                {865                    Ok(next_attr) => next_attr,866                    Err(inner_err) => {867                        inner_err.cancel();868                        return err.emit();869                    }870                }871                && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind872                && let Some(next_attr_args_span) = next_attr_kind.item.args.span()873                && let [next_segment] = &next_attr_kind.item.path.segments[..]874                && next_segment.ident.name == sym::cfg875            {876                let next_expr = match snapshot.parse_expr() {877                    Ok(next_expr) => next_expr,878                    Err(inner_err) => {879                        inner_err.cancel();880                        return err.emit();881                    }882                };883                // We have for sure884                // #[cfg(..)]885                // expr886                // #[cfg(..)]887                // other_expr888                // So we suggest using `if cfg!(..) { expr } else if cfg!(..) { other_expr }`.889                let margin = self.psess.source_map().span_to_margin(next_expr.span).unwrap_or(0);890                let sugg = vec![891                    (attr.span.with_hi(segment.span().hi()), "if cfg!".to_string()),892                    (args_span.shrink_to_hi().with_hi(attr.span.hi()), " {".to_string()),893                    (expr.span.shrink_to_lo(), "    ".to_string()),894                    (895                        next_attr.span.with_hi(next_segment.span().hi()),896                        "} else if cfg!".to_string(),897                    ),898                    (899                        next_attr_args_span.shrink_to_hi().with_hi(next_attr.span.hi()),900                        " {".to_string(),901                    ),902                    (next_expr.span.shrink_to_lo(), "    ".to_string()),903                    (next_expr.span.shrink_to_hi(), format!("\n{}}}", " ".repeat(margin))),904                ];905                err.multipart_suggestion(906                    "it seems like you are trying to provide different expressions depending on \907                     `cfg`, consider using `if cfg!(..)`",908                    sugg,909                    Applicability::MachineApplicable,910                );911            }912        }913914        err.emit()915    }916917    fn check_too_many_raw_str_terminators(&mut self, err: &mut Diag<'_>) -> bool {918        let sm = self.psess.source_map();919        match (&self.prev_token.kind, &self.token.kind) {920            (921                TokenKind::Literal(Lit {922                    kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes),923                    ..924                }),925                TokenKind::Pound,926            ) if !sm.is_multiline(927                self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),928            ) =>929            {930                let n_hashes: u8 = *n_hashes;931                err.primary_message("too many `#` when terminating raw string");932                let str_span = self.prev_token.span;933                let mut span = self.token.span;934                let mut count = 0;935                while self.token == TokenKind::Pound936                    && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo()))937                {938                    span = span.with_hi(self.token.span.hi());939                    self.bump();940                    count += 1;941                }942                err.span(span);943                err.span_suggestion_verbose(944                    span,945                    format!("remove the extra `#`{}", pluralize!(count)),946                    "",947                    Applicability::MachineApplicable,948                );949                err.span_label(950                    str_span,951                    format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),952                );953                true954            }955            _ => false,956        }957    }958959    pub(super) fn maybe_suggest_struct_literal(960        &mut self,961        lo: Span,962        s: BlockCheckMode,963        maybe_struct_name: token::Token,964    ) -> Option<PResult<'a, Box<Block>>> {965        if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {966            // We might be having a struct literal where people forgot to include the path:967            // fn foo() -> Foo {968            //     field: value,969            // }970            debug!(?maybe_struct_name, ?self.token);971            let mut snapshot = self.create_snapshot_for_diagnostic();972            let path = Path { segments: ThinVec::new(), span: self.prev_token.span.shrink_to_lo() };973            let struct_expr = snapshot.parse_expr_struct(None, path, false);974            let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);975            return Some(match (struct_expr, block_tail) {976                (Ok(expr), Err(err)) => {977                    // We have encountered the following:978                    // fn foo() -> Foo {979                    //     field: value,980                    // }981                    // Suggest:982                    // fn foo() -> Foo { Path {983                    //     field: value,984                    // } }985                    err.cancel();986                    self.restore_snapshot(snapshot);987                    let guar = self.dcx().emit_err(StructLiteralBodyWithoutPath {988                        span: expr.span,989                        sugg: StructLiteralBodyWithoutPathSugg {990                            before: expr.span.shrink_to_lo(),991                            after: expr.span.shrink_to_hi(),992                        },993                    });994                    Ok(self.mk_block(995                        thin_vec![self.mk_stmt_err(expr.span, guar)],996                        s,997                        lo.to(self.prev_token.span),998                    ))999                }1000                (Err(err), Ok(tail)) => {1001                    // We have a block tail that contains a somehow valid expr.1002                    err.cancel();1003                    Ok(tail)1004                }1005                (Err(snapshot_err), Err(err)) => {1006                    // We don't know what went wrong, emit the normal error.1007                    snapshot_err.cancel();1008                    self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes);1009                    Err(err)1010                }1011                (Ok(_), Ok(tail)) => Ok(tail),1012            });1013        }1014        None1015    }10161017    pub(super) fn recover_closure_body(1018        &mut self,1019        mut err: Diag<'a>,1020        before: token::Token,1021        prev: token::Token,1022        token: token::Token,1023        lo: Span,1024        decl_hi: Span,1025    ) -> PResult<'a, Box<Expr>> {1026        err.span_label(lo.to(decl_hi), "while parsing the body of this closure");1027        let guar = match before.kind {1028            token::OpenBrace if token.kind != token::OpenBrace => {1029                // `{ || () }` should have been `|| { () }`1030                err.multipart_suggestion(1031                    "you might have meant to open the body of the closure, instead of enclosing \1032                     the closure in a block",1033                    vec![1034                        (before.span, String::new()),1035                        (prev.span.shrink_to_hi(), " {".to_string()),1036                    ],1037                    Applicability::MaybeIncorrect,1038                );1039                let guar = err.emit();1040                self.eat_to_tokens(&[exp!(CloseBrace)]);1041                guar1042            }1043            token::OpenParen if token.kind != token::OpenBrace => {1044                // We are within a function call or tuple, we can emit the error1045                // and recover.1046                self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]);10471048                err.multipart_suggestion(1049                    "you might have meant to open the body of the closure",1050                    vec![1051                        (prev.span.shrink_to_hi(), " {".to_string()),1052                        (self.token.span.shrink_to_lo(), "}".to_string()),1053                    ],1054                    Applicability::MaybeIncorrect,1055                );1056                err.emit()1057            }1058            _ if token.kind != token::OpenBrace => {1059                // We don't have a heuristic to correctly identify where the block1060                // should be closed.1061                err.multipart_suggestion(1062                    "you might have meant to open the body of the closure",1063                    vec![(prev.span.shrink_to_hi(), " {".to_string())],1064                    Applicability::HasPlaceholders,1065                );1066                return Err(err);1067            }1068            _ => return Err(err),1069        };1070        Ok(self.mk_expr_err(lo.to(self.token.span), guar))1071    }10721073    /// Eats and discards tokens until one of `closes` is encountered. Respects token trees,1074    /// passes through any errors encountered. Used for error recovery.1075    pub(super) fn eat_to_tokens(&mut self, closes: &[ExpTokenPair]) {1076        if let Err(err) = self1077            .parse_seq_to_before_tokens(closes, &[], SeqSep::none(), |p| Ok(p.parse_token_tree()))1078        {1079            err.cancel();1080        }1081    }10821083    /// This function checks if there are trailing angle brackets and produces1084    /// a diagnostic to suggest removing them.1085    ///1086    /// ```ignore (diagnostic)1087    /// let _ = [1, 2, 3].into_iter().collect::<Vec<usize>>>>();1088    ///                                                    ^^ help: remove extra angle brackets1089    /// ```1090    ///1091    /// If `true` is returned, then trailing brackets were recovered, tokens were consumed1092    /// up until one of the tokens in 'end' was encountered, and an error was emitted.1093    pub(super) fn check_trailing_angle_brackets(1094        &mut self,1095        segment: &PathSegment,1096        end: &[ExpTokenPair],1097    ) -> Option<ErrorGuaranteed> {1098        if !self.may_recover() {1099            return None;1100        }11011102        // This function is intended to be invoked after parsing a path segment where there are two1103        // cases:1104        //1105        // 1. A specific token is expected after the path segment.1106        //    eg. `x.foo(`, `x.foo::<u32>(` (parenthesis - method call),1107        //        `Foo::`, or `Foo::<Bar>::` (mod sep - continued path).1108        // 2. No specific token is expected after the path segment.1109        //    eg. `x.foo` (field access)1110        //1111        // This function is called after parsing `.foo` and before parsing the token `end` (if1112        // present). This includes any angle bracket arguments, such as `.foo::<u32>` or1113        // `Foo::<Bar>`.11141115        // We only care about trailing angle brackets if we previously parsed angle bracket1116        // arguments. This helps stop us incorrectly suggesting that extra angle brackets be1117        // removed in this case:1118        //1119        // `x.foo >> (3)` (where `x.foo` is a `u32` for example)1120        //1121        // This case is particularly tricky as we won't notice it just looking at the tokens -1122        // it will appear the same (in terms of upcoming tokens) as below (since the `::<u32>` will1123        // have already been parsed):1124        //1125        // `x.foo::<u32>>>(3)`1126        let parsed_angle_bracket_args =1127            segment.args.as_ref().is_some_and(|args| args.is_angle_bracketed());11281129        debug!(1130            "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}",1131            parsed_angle_bracket_args,1132        );1133        if !parsed_angle_bracket_args {1134            return None;1135        }11361137        // Keep the span at the start so we can highlight the sequence of `>` characters to be1138        // removed.1139        let lo = self.token.span;11401141        // We need to look-ahead to see if we have `>` characters without moving the cursor forward1142        // (since we might have the field access case and the characters we're eating are1143        // actual operators and not trailing characters - ie `x.foo >> 3`).1144        let mut position = 0;11451146        // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how1147        // many of each (so we can correctly pluralize our error messages) and continue to1148        // advance.1149        let mut number_of_shr = 0;1150        let mut number_of_gt = 0;1151        while self.look_ahead(position, |t| {1152            trace!("check_trailing_angle_brackets: t={:?}", t);1153            if *t == token::Shr {1154                number_of_shr += 1;1155                true1156            } else if *t == token::Gt {1157                number_of_gt += 1;1158                true1159            } else {1160                false1161            }1162        }) {1163            position += 1;1164        }11651166        // If we didn't find any trailing `>` characters, then we have nothing to error about.1167        debug!(1168            "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}",1169            number_of_gt, number_of_shr,1170        );1171        if number_of_gt < 1 && number_of_shr < 1 {1172            return None;1173        }11741175        // Finally, double check that we have our end token as otherwise this is the1176        // second case.1177        if self.look_ahead(position, |t| {1178            trace!("check_trailing_angle_brackets: t={:?}", t);1179            end.iter().any(|exp| exp.tok == t.kind)1180        }) {1181            // Eat from where we started until the end token so that parsing can continue1182            // as if we didn't have those extra angle brackets.1183            self.eat_to_tokens(end);1184            let span = lo.to(self.prev_token.span);11851186            let num_extra_brackets = number_of_gt + number_of_shr * 2;1187            return Some(self.dcx().emit_err(UnmatchedAngleBrackets { span, num_extra_brackets }));1188        }1189        None1190    }11911192    /// Check if a method call with an intended turbofish has been written without surrounding1193    /// angle brackets.1194    pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) {1195        if !self.may_recover() {1196            return;1197        }11981199        if self.token == token::PathSep && segment.args.is_none() {1200            let snapshot = self.create_snapshot_for_diagnostic();1201            self.bump();1202            let lo = self.token.span;1203            match self.parse_angle_args(None) {1204                Ok(args) => {1205                    let span = lo.to(self.prev_token.span);1206                    // Detect trailing `>` like in `x.collect::Vec<_>>()`.1207                    let mut trailing_span = self.prev_token.span.shrink_to_hi();1208                    while self.token == token::Shr || self.token == token::Gt {1209                        trailing_span = trailing_span.to(self.token.span);1210                        self.bump();1211                    }1212                    if self.token == token::OpenParen {1213                        // Recover from bad turbofish: `foo.collect::Vec<_>()`.1214                        segment.args = Some(AngleBracketedArgs { args, span }.into());12151216                        self.dcx().emit_err(GenericParamsWithoutAngleBrackets {1217                            span,1218                            sugg: GenericParamsWithoutAngleBracketsSugg {1219                                left: span.shrink_to_lo(),1220                                right: trailing_span,1221                            },1222                        });1223                    } else {1224                        // This doesn't look like an invalid turbofish, can't recover parse state.1225                        self.restore_snapshot(snapshot);1226                    }1227                }1228                Err(err) => {1229                    // We couldn't parse generic parameters, unlikely to be a turbofish. Rely on1230                    // generic parse error instead.1231                    err.cancel();1232                    self.restore_snapshot(snapshot);1233                }1234            }1235        }1236    }12371238    /// When writing a turbofish with multiple type parameters missing the leading `::`, we will1239    /// encounter a parse error when encountering the first `,`.1240    pub(super) fn check_mistyped_turbofish_with_multiple_type_params(1241        &mut self,1242        mut e: Diag<'a>,1243        expr: &mut Box<Expr>,1244    ) -> PResult<'a, ErrorGuaranteed> {1245        if let ExprKind::Binary(binop, _, _) = &expr.kind1246            && let ast::BinOpKind::Lt = binop.node1247            && self.eat(exp!(Comma))1248        {1249            let x = self.parse_seq_to_before_end(1250                exp!(Gt),1251                SeqSep::trailing_allowed(exp!(Comma)),1252                |p| match p.parse_generic_arg(None)? {1253                    Some(arg) => Ok(arg),1254                    // If we didn't eat a generic arg, then we should error.1255                    None => p.unexpected_any(),1256                },1257            );1258            match x {1259                Ok((_, _, Recovered::No)) => {1260                    if self.eat(exp!(Gt)) {1261                        // We made sense of it. Improve the error message.1262                        e.span_suggestion_verbose(1263                            binop.span.shrink_to_lo(),1264                            msg!("use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments"),1265                            "::",1266                            Applicability::MaybeIncorrect,1267                        );1268                        match self.parse_expr() {1269                            Ok(_) => {1270                                // The subsequent expression is valid. Mark1271                                // `expr` as erroneous and emit `e` now, but1272                                // return `Ok` so parsing can continue.1273                                let guar = e.emit();1274                                *expr = self.mk_expr_err(expr.span.to(self.prev_token.span), guar);1275                                return Ok(guar);1276                            }1277                            Err(err) => {1278                                err.cancel();1279                            }1280                        }1281                    }1282                }1283                Ok((_, _, Recovered::Yes(_))) => {}1284                Err(err) => {1285                    err.cancel();1286                }1287            }1288        }1289        Err(e)1290    }12911292    /// Suggest add the missing `let` before the identifier in stmt1293    /// `a: Ty = 1` -> `let a: Ty = 1`1294    pub(super) fn suggest_add_missing_let_for_stmt(&mut self, err: &mut Diag<'a>) {1295        if self.token == token::Colon {1296            let prev_span = self.prev_token.span.shrink_to_lo();1297            let snapshot = self.create_snapshot_for_diagnostic();1298            self.bump();1299            match self.parse_ty() {1300                Ok(_) => {1301                    if self.token == token::Eq {1302                        let sugg = SuggAddMissingLetStmt { span: prev_span };1303                        sugg.add_to_diag(err);1304                    }1305                }1306                Err(e) => {1307                    e.cancel();1308                }1309            }1310            self.restore_snapshot(snapshot);1311        }1312    }13131314    /// Check to see if a pair of chained operators looks like an attempt at chained comparison,1315    /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or1316    /// parenthesising the leftmost comparison. The return value indicates if recovery happened.1317    fn attempt_chained_comparison_suggestion(1318        &mut self,1319        err: &mut ComparisonOperatorsCannotBeChained,1320        inner_op: &Expr,1321        outer_op: &Spanned<AssocOp>,1322    ) -> bool {1323        if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {1324            if let ExprKind::Field(_, ident) = l1.kind1325                && !ident.is_numeric()1326                && !matches!(r1.kind, ExprKind::Lit(_))1327            {1328                // The parser has encountered `foo.bar<baz`, the likelihood of the turbofish1329                // suggestion being the only one to apply is high.1330                return false;1331            }1332            return match (op.node, &outer_op.node) {1333                // `x == y == z`1334                (BinOpKind::Eq, AssocOp::Binary(BinOpKind::Eq)) |1335                // `x < y < z` and friends.1336                (BinOpKind::Lt, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |1337                (BinOpKind::Le, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |1338                // `x > y > z` and friends.1339                (BinOpKind::Gt, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) |1340                (BinOpKind::Ge, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) => {1341                    let expr_to_str = |e: &Expr| {1342                        self.span_to_snippet(e.span).unwrap_or_else(|_| pprust::expr_to_string(e))1343                    };1344                    err.chaining_sugg =1345                        Some(ComparisonOperatorsCannotBeChainedSugg::SplitComparison {1346                            span: inner_op.span.shrink_to_hi(),1347                            middle_term: expr_to_str(r1),1348                        });1349                    false // Keep the current parse behavior, where the AST is `(x < y) < z`.1350                }1351                // `x == y < z`1352                (1353                    BinOpKind::Eq,1354                    AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge),1355                ) => {1356                    // Consume `z`/outer-op-rhs.1357                    let snapshot = self.create_snapshot_for_diagnostic();1358                    match self.parse_expr() {1359                        Ok(r2) => {1360                            // We are sure that outer-op-rhs could be consumed, the suggestion is1361                            // likely correct.1362                            err.chaining_sugg =1363                                Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {1364                                    left: r1.span.shrink_to_lo(),1365                                    right: r2.span.shrink_to_hi(),1366                                });1367                            true1368                        }1369                        Err(expr_err) => {1370                            expr_err.cancel();1371                            self.restore_snapshot(snapshot);1372                            true1373                        }1374                    }1375                }1376                // `x > y == z`1377                (1378                    BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge,1379                    AssocOp::Binary(BinOpKind::Eq),1380                ) => {1381                    let snapshot = self.create_snapshot_for_diagnostic();1382                    // At this point it is always valid to enclose the lhs in parentheses, no1383                    // further checks are necessary.1384                    match self.parse_expr() {1385                        Ok(_) => {1386                            err.chaining_sugg =1387                                Some(ComparisonOperatorsCannotBeChainedSugg::Parenthesize {1388                                    left: l1.span.shrink_to_lo(),1389                                    right: r1.span.shrink_to_hi(),1390                                });1391                            true1392                        }1393                        Err(expr_err) => {1394                            expr_err.cancel();1395                            self.restore_snapshot(snapshot);1396                            false1397                        }1398                    }1399                }1400                _ => false,1401            };1402        }1403        false1404    }14051406    /// Produces an error if comparison operators are chained (RFC #558).1407    /// We only need to check the LHS, not the RHS, because all comparison ops have same1408    /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`).1409    ///1410    /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used1411    /// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the1412    /// case.1413    ///1414    /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left1415    /// associative we can infer that we have:1416    ///1417    /// ```text1418    ///           outer_op1419    ///           /   \1420    ///     inner_op   r21421    ///        /  \1422    ///      l1    r11423    /// ```1424    pub(super) fn check_no_chained_comparison(1425        &mut self,1426        inner_op: &Expr,1427        outer_op: &Spanned<AssocOp>,1428    ) -> PResult<'a, Option<Box<Expr>>> {1429        debug_assert!(1430            outer_op.node.is_comparison(),1431            "check_no_chained_comparison: {:?} is not comparison",1432            outer_op.node,1433        );14341435        let mk_err_expr =1436            |this: &Self, span, guar| Ok(Some(this.mk_expr(span, ExprKind::Err(guar))));14371438        match &inner_op.kind {1439            ExprKind::Binary(op, l1, r1) if op.node.is_comparison() => {1440                let mut err = ComparisonOperatorsCannotBeChained {1441                    span: vec![op.span, self.prev_token.span],1442                    suggest_turbofish: None,1443                    help_turbofish: false,1444                    chaining_sugg: None,1445                };14461447                // Include `<` to provide this recommendation even in a case like1448                // `Foo<Bar<Baz<Qux, ()>>>`1449                if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Binary(BinOpKind::Lt)1450                    || outer_op.node == AssocOp::Binary(BinOpKind::Gt)1451                {1452                    if outer_op.node == AssocOp::Binary(BinOpKind::Lt) {1453                        let snapshot = self.create_snapshot_for_diagnostic();1454                        self.bump();1455                        // So far we have parsed `foo<bar<`, consume the rest of the type args.1456                        let modifiers = [(token::Lt, 1), (token::Gt, -1), (token::Shr, -2)];1457                        self.consume_tts(1, &modifiers);14581459                        if !matches!(self.token.kind, token::OpenParen | token::PathSep) {1460                            // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the1461                            // parser and bail out.1462                            self.restore_snapshot(snapshot);1463                        }1464                    }1465                    return if self.token == token::PathSep {1466                        // We have some certainty that this was a bad turbofish at this point.1467                        // `foo< bar >::`1468                        if let ExprKind::Binary(o, ..) = inner_op.kind1469                            && o.node == BinOpKind::Lt1470                        {1471                            err.suggest_turbofish = Some(op.span.shrink_to_lo());1472                        } else {1473                            err.help_turbofish = true;1474                        }14751476                        let snapshot = self.create_snapshot_for_diagnostic();1477                        self.bump(); // `::`14781479                        // Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.1480                        match self.parse_expr() {1481                            Ok(_) => {1482                                // 99% certain that the suggestion is correct, continue parsing.1483                                let guar = self.dcx().emit_err(err);1484                                // FIXME: actually check that the two expressions in the binop are1485                                // paths and resynthesize new fn call expression instead of using1486                                // `ExprKind::Err` placeholder.1487                                mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)1488                            }1489                            Err(expr_err) => {1490                                expr_err.cancel();1491                                // Not entirely sure now, but we bubble the error up with the1492                                // suggestion.1493                                self.restore_snapshot(snapshot);1494                                Err(self.dcx().create_err(err))1495                            }1496                        }1497                    } else if self.token == token::OpenParen {1498                        // We have high certainty that this was a bad turbofish at this point.1499                        // `foo< bar >(`1500                        if let ExprKind::Binary(o, ..) = inner_op.kind1501                            && o.node == BinOpKind::Lt1502                        {1503                            err.suggest_turbofish = Some(op.span.shrink_to_lo());1504                        } else {1505                            err.help_turbofish = true;1506                        }1507                        // Consume the fn call arguments.1508                        match self.consume_fn_args() {1509                            Err(()) => Err(self.dcx().create_err(err)),1510                            Ok(()) => {1511                                let guar = self.dcx().emit_err(err);1512                                // FIXME: actually check that the two expressions in the binop are1513                                // paths and resynthesize new fn call expression instead of using1514                                // `ExprKind::Err` placeholder.1515                                mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)1516                            }1517                        }1518                    } else {1519                        if !matches!(l1.kind, ExprKind::Lit(_))1520                            && !matches!(r1.kind, ExprKind::Lit(_))1521                        {1522                            // All we know is that this is `foo < bar >` and *nothing* else. Try to1523                            // be helpful, but don't attempt to recover.1524                            err.help_turbofish = true;1525                        }15261527                        // If it looks like a genuine attempt to chain operators (as opposed to a1528                        // misformatted turbofish, for instance), suggest a correct form.1529                        let recovered = self1530                            .attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);1531                        if recovered {1532                            let guar = self.dcx().emit_err(err);1533                            mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar)1534                        } else {1535                            // These cases cause too many knock-down errors, bail out (#61329).1536                            Err(self.dcx().create_err(err))1537                        }1538                    };1539                }1540                let recovered =1541                    self.attempt_chained_comparison_suggestion(&mut err, inner_op, outer_op);1542                let guar = self.dcx().emit_err(err);1543                if recovered {1544                    return mk_err_expr(self, inner_op.span.to(self.prev_token.span), guar);1545                }1546            }1547            _ => {}1548        }1549        Ok(None)1550    }15511552    fn consume_fn_args(&mut self) -> Result<(), ()> {1553        let snapshot = self.create_snapshot_for_diagnostic();1554        self.bump(); // `(`15551556        // Consume the fn call arguments.1557        let modifiers = [(token::OpenParen, 1), (token::CloseParen, -1)];1558        self.consume_tts(1, &modifiers);15591560        if self.token == token::Eof {1561            // Not entirely sure that what we consumed were fn arguments, rollback.1562            self.restore_snapshot(snapshot);1563            Err(())1564        } else {1565            // 99% certain that the suggestion is correct, continue parsing.1566            Ok(())1567        }1568    }15691570    pub(super) fn maybe_report_ambiguous_plus(&mut self, impl_dyn_multi: bool, ty: &Ty) {1571        if impl_dyn_multi {1572            self.dcx().emit_err(AmbiguousPlus {1573                span: ty.span,1574                suggestion: AddParen { lo: ty.span.shrink_to_lo(), hi: ty.span.shrink_to_hi() },1575            });1576        }1577    }15781579    /// Swift lets users write `Ty?` to mean `Option<Ty>`. Parse the construct and recover from it.1580    pub(super) fn maybe_recover_from_question_mark(&mut self, ty: Box<Ty>) -> Box<Ty> {1581        if self.token == token::Question {1582            self.bump();1583            let guar = self.dcx().emit_err(QuestionMarkInType {1584                span: self.prev_token.span,1585                sugg: QuestionMarkInTypeSugg {1586                    left: ty.span.shrink_to_lo(),1587                    right: self.prev_token.span,1588                },1589            });1590            self.mk_ty(ty.span.to(self.prev_token.span), TyKind::Err(guar))1591        } else {1592            ty1593        }1594    }15951596    /// Rust has no ternary operator (`cond ? then : else`). Parse it and try1597    /// to recover from it if `then` and `else` are valid expressions. Returns1598    /// an err if this appears to be a ternary expression.1599    /// If we have the span of the condition, we can provide a better error span1600    /// and code suggestion.1601    pub(super) fn maybe_recover_from_ternary_operator(1602        &mut self,1603        cond: Option<Span>,1604    ) -> PResult<'a, ()> {1605        if self.prev_token != token::Question {1606            return PResult::Ok(());1607        }16081609        let question = self.prev_token.span;1610        let lo = cond.unwrap_or(question).lo();1611        let snapshot = self.create_snapshot_for_diagnostic();16121613        if match self.parse_expr() {1614            Ok(_) => true,1615            Err(err) => {1616                err.cancel();1617                // The colon can sometimes be mistaken for type1618                // ascription. Catch when this happens and continue.1619                self.token == token::Colon1620            }1621        } {1622            if self.eat_noexpect(&token::Colon) {1623                let colon = self.prev_token.span;1624                match self.parse_expr() {1625                    Ok(expr) => {1626                        let sugg = cond.map(|cond| TernaryOperatorSuggestion {1627                            before_cond: cond.shrink_to_lo(),1628                            question,1629                            colon,1630                            end: expr.span.shrink_to_hi(),1631                        });1632                        return Err(self.dcx().create_err(TernaryOperator {1633                            span: self.prev_token.span.with_lo(lo),1634                            sugg,1635                            no_sugg: sugg.is_none(),1636                        }));1637                    }1638                    Err(err) => {1639                        err.cancel();1640                    }1641                };1642            }1643        }1644        self.restore_snapshot(snapshot);1645        Ok(())1646    }16471648    pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> {1649        // Do not add `+` to expected tokens.1650        if !self.token.is_like_plus() {1651            return Ok(());1652        }16531654        self.bump(); // `+`1655        let _bounds = self.parse_generic_bounds()?;1656        let sub = match &ty.kind {1657            TyKind::Ref(_lifetime, mut_ty) => {1658                let lo = mut_ty.ty.span.shrink_to_lo();1659                let hi = self.prev_token.span.shrink_to_hi();1660                BadTypePlusSub::AddParen { suggestion: AddParen { lo, hi } }1661            }1662            TyKind::Ptr(..) | TyKind::FnPtr(..) => {1663                BadTypePlusSub::ForgotParen { span: ty.span.to(self.prev_token.span) }1664            }1665            _ => BadTypePlusSub::ExpectPath { span: ty.span },1666        };16671668        self.dcx().emit_err(BadTypePlus { span: ty.span, sub });16691670        Ok(())1671    }16721673    pub(super) fn recover_from_prefix_increment(1674        &mut self,1675        operand_expr: Box<Expr>,1676        op_span: Span,1677        start_stmt: bool,1678    ) -> PResult<'a, Box<Expr>> {1679        let standalone = if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr };1680        let kind = IncDecRecovery { standalone, op: IncOrDec::Inc, fixity: UnaryFixity::Pre };1681        self.recover_from_inc_dec(operand_expr, kind, op_span)1682    }16831684    pub(super) fn recover_from_postfix_increment(1685        &mut self,1686        operand_expr: Box<Expr>,1687        op_span: Span,1688        start_stmt: bool,1689    ) -> PResult<'a, Box<Expr>> {1690        let kind = IncDecRecovery {1691            standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },1692            op: IncOrDec::Inc,1693            fixity: UnaryFixity::Post,1694        };1695        self.recover_from_inc_dec(operand_expr, kind, op_span)1696    }16971698    pub(super) fn recover_from_postfix_decrement(1699        &mut self,1700        operand_expr: Box<Expr>,1701        op_span: Span,1702        start_stmt: bool,1703    ) -> PResult<'a, Box<Expr>> {1704        let kind = IncDecRecovery {1705            standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },1706            op: IncOrDec::Dec,1707            fixity: UnaryFixity::Post,1708        };1709        self.recover_from_inc_dec(operand_expr, kind, op_span)1710    }17111712    fn recover_from_inc_dec(1713        &mut self,1714        base: Box<Expr>,1715        kind: IncDecRecovery,1716        op_span: Span,1717    ) -> PResult<'a, Box<Expr>> {1718        let mut err = self.dcx().struct_span_err(1719            op_span,1720            format!("Rust has no {} {} operator", kind.fixity, kind.op.name()),1721        );1722        err.span_label(op_span, format!("not a valid {} operator", kind.fixity));17231724        let help_base_case = |mut err: Diag<'_, _>, base| {1725            err.help(format!("use `{}= 1` instead", kind.op.chr()));1726            err.emit();1727            Ok(base)1728        };17291730        // (pre, post)1731        let spans = match kind.fixity {1732            UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()),1733            UnaryFixity::Post => (base.span.shrink_to_lo(), op_span),1734        };17351736        match kind.standalone {1737            IsStandalone::Standalone => {1738                self.inc_dec_standalone_suggest(kind, spans).emit_verbose(&mut err)1739            }1740            IsStandalone::Subexpr => {1741                let Ok(base_src) = self.span_to_snippet(base.span) else {1742                    return help_base_case(err, base);1743                };1744                match kind.fixity {1745                    UnaryFixity::Pre => {1746                        self.prefix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)1747                    }1748                    UnaryFixity::Post => {1749                        // won't suggest since we can not handle the precedences1750                        // for example: `a + b++` has been parsed (a + b)++ and we can not suggest here1751                        if !matches!(base.kind, ExprKind::Binary(_, _, _)) {1752                            self.postfix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)1753                        }1754                    }1755                }1756            }1757        }1758        Err(err)1759    }17601761    fn prefix_inc_dec_suggest(1762        &mut self,1763        base_src: String,1764        kind: IncDecRecovery,1765        (pre_span, post_span): (Span, Span),1766    ) -> MultiSugg {1767        MultiSugg {1768            msg: format!("use `{}= 1` instead", kind.op.chr()),1769            patches: vec![1770                (pre_span, "{ ".to_string()),1771                (post_span, format!(" {}= 1; {} }}", kind.op.chr(), base_src)),1772            ],1773            applicability: Applicability::MachineApplicable,1774        }1775    }17761777    fn postfix_inc_dec_suggest(1778        &mut self,1779        base_src: String,1780        kind: IncDecRecovery,1781        (pre_span, post_span): (Span, Span),1782    ) -> MultiSugg {1783        let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" };1784        MultiSugg {1785            msg: format!("use `{}= 1` instead", kind.op.chr()),1786            patches: vec![1787                (pre_span, format!("{{ let {tmp_var} = ")),1788                (post_span, format!("; {} {}= 1; {} }}", base_src, kind.op.chr(), tmp_var)),1789            ],1790            applicability: Applicability::HasPlaceholders,1791        }1792    }17931794    fn inc_dec_standalone_suggest(1795        &mut self,1796        kind: IncDecRecovery,1797        (pre_span, post_span): (Span, Span),1798    ) -> MultiSugg {1799        let mut patches = Vec::new();18001801        if !pre_span.is_empty() {1802            patches.push((pre_span, String::new()));1803        }18041805        patches.push((post_span, format!(" {}= 1", kind.op.chr())));1806        MultiSugg {1807            msg: format!("use `{}= 1` instead", kind.op.chr()),1808            patches,1809            applicability: Applicability::MachineApplicable,1810        }1811    }18121813    /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`.1814    /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem`1815    /// tail, and combines them into a `<Ty>::AssocItem` expression/pattern/type.1816    pub(super) fn maybe_recover_from_bad_qpath<T: RecoverQPath>(1817        &mut self,1818        base: T,1819    ) -> PResult<'a, T> {1820        // Do not add `::` to expected tokens.1821        if self.may_recover() && self.token == token::PathSep {1822            return self.recover_from_bad_qpath(base);1823        }1824        Ok(base)1825    }18261827    #[cold]1828    fn recover_from_bad_qpath<T: RecoverQPath>(&mut self, base: T) -> PResult<'a, T> {1829        if let Some(ty) = base.to_ty() {1830            return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);1831        }1832        Ok(base)1833    }18341835    /// Given an already parsed `Ty`, parses the `::AssocItem` tail and1836    /// combines them into a `<Ty>::AssocItem` expression/pattern/type.1837    pub(super) fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(1838        &mut self,1839        ty_span: Span,1840        ty: Box<Ty>,1841    ) -> PResult<'a, T> {1842        self.expect(exp!(PathSep))?;18431844        let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP };1845        self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?;1846        path.span = ty_span.to(self.prev_token.span);18471848        self.dcx().emit_err(BadQPathStage2 {1849            span: ty_span,1850            wrap: WrapType { lo: ty_span.shrink_to_lo(), hi: ty_span.shrink_to_hi() },1851        });18521853        let path_span = ty_span.shrink_to_hi(); // Use an empty path since `position == 0`.1854        Ok(T::recovered(Some(Box::new(QSelf { ty, path_span, position: 0 })), path))1855    }18561857    /// This function gets called in places where a semicolon is NOT expected and if there's a1858    /// semicolon it emits the appropriate error and returns true.1859    pub fn maybe_consume_incorrect_semicolon(&mut self, previous_item: Option<&Item>) -> bool {1860        if self.token != TokenKind::Semi {1861            return false;1862        }18631864        // Check previous item to add it to the diagnostic, for example to say1865        // `enum declarations are not followed by a semicolon`1866        let err = match previous_item {1867            Some(previous_item) => {1868                let name = match previous_item.kind {1869                    // Say "braced struct" because tuple-structs and1870                    // braceless-empty-struct declarations do take a semicolon.1871                    ItemKind::Struct(..) => "braced struct",1872                    _ => previous_item.kind.descr(),1873                };1874                IncorrectSemicolon { span: self.token.span, name, show_help: true }1875            }1876            None => IncorrectSemicolon { span: self.token.span, name: "", show_help: false },1877        };1878        self.dcx().emit_err(err);18791880        self.bump();1881        true1882    }18831884    /// Creates a `Diag` for an unexpected token `t`1885    pub(super) fn unexpected_err(&mut self, t: &TokenKind) -> Diag<'a> {1886        let token_str = pprust::token_kind_to_string(t);1887        let this_token_str = super::token_descr(&self.token);1888        let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) {1889            // Point at the end of the macro call when reaching end of macro arguments.1890            (token::Eof, Some(_)) => {1891                let sp = self.prev_token.span.shrink_to_hi();1892                (sp, sp)1893            }1894            // We don't want to point at the following span after DUMMY_SP.1895            // This happens when the parser finds an empty TokenStream.1896            _ if self.prev_token.span == DUMMY_SP => (self.token.span, self.token.span),1897            // EOF, don't want to point at the following char, but rather the last token.1898            (token::Eof, None) => (self.prev_token.span, self.token.span),1899            _ => (self.prev_token.span.shrink_to_hi(), self.token.span),1900        };1901        let msg = format!(1902            "expected `{}`, found {}",1903            token_str,1904            match (&self.token.kind, self.subparser_name) {1905                (token::Eof, Some(origin)) => format!("end of {origin}"),1906                _ => this_token_str,1907            },1908        );1909        let mut err = self.dcx().struct_span_err(sp, msg);1910        let label_exp = format!("expected `{token_str}`");1911        let sm = self.psess.source_map();1912        if !sm.is_multiline(prev_sp.until(sp)) {1913            // When the spans are in the same line, it means that the only content1914            // between them is whitespace, point only at the found token.1915            err.span_label(sp, label_exp);1916        } else {1917            err.span_label(prev_sp, label_exp);1918            err.span_label(sp, "unexpected token");1919        }1920        err1921    }19221923    pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {1924        if self.eat(exp!(Semi)) || self.recover_colon_as_semi() {1925            return Ok(());1926        }1927        self.expect(exp!(Semi)).map(drop) // Error unconditionally1928    }19291930    pub(super) fn recover_colon_as_semi(&mut self) -> bool {1931        let line_idx = |span: Span| {1932            self.psess1933                .source_map()1934                .span_to_lines(span)1935                .ok()1936                .and_then(|lines| Some(lines.lines.get(0)?.line_index))1937        };19381939        if self.may_recover()1940            && self.token == token::Colon1941            && self.look_ahead(1, |next| line_idx(self.token.span) < line_idx(next.span))1942        {1943            self.dcx().emit_err(ColonAsSemi { span: self.token.span });1944            self.bump();1945            return true;1946        }19471948        false1949    }19501951    /// Consumes alternative await syntaxes like `await!(<expr>)`, `await <expr>`,1952    /// `await? <expr>`, `await(<expr>)`, and `await { <expr> }`.1953    pub(super) fn recover_incorrect_await_syntax(1954        &mut self,1955        await_sp: Span,1956    ) -> PResult<'a, Box<Expr>> {1957        let (hi, expr, is_question) = if self.token == token::Bang {1958            // Handle `await!(<expr>)`.1959            self.recover_await_macro()?1960        } else {1961            self.recover_await_prefix(await_sp)?1962        };1963        let (sp, guar) = self.error_on_incorrect_await(await_sp, hi, &expr, is_question);1964        let expr = self.mk_expr_err(await_sp.to(sp), guar);1965        self.maybe_recover_from_bad_qpath(expr)1966    }19671968    fn recover_await_macro(&mut self) -> PResult<'a, (Span, Box<Expr>, bool)> {1969        self.expect(exp!(Bang))?;1970        self.expect(exp!(OpenParen))?;1971        let expr = self.parse_expr()?;1972        self.expect(exp!(CloseParen))?;1973        Ok((self.prev_token.span, expr, false))1974    }19751976    fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, Box<Expr>, bool)> {1977        let is_question = self.eat(exp!(Question)); // Handle `await? <expr>`.1978        let expr = if self.token == token::OpenBrace {1979            // Handle `await { <expr> }`.1980            // This needs to be handled separately from the next arm to avoid1981            // interpreting `await { <expr> }?` as `<expr>?.await`.1982            self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)1983        } else {1984            self.parse_expr()1985        }1986        .map_err(|mut err| {1987            err.span_label(await_sp, format!("while parsing this incorrect await expression"));1988            err1989        })?;1990        Ok((expr.span, expr, is_question))1991    }19921993    fn error_on_incorrect_await(1994        &self,1995        lo: Span,1996        hi: Span,1997        expr: &Expr,1998        is_question: bool,1999    ) -> (Span, ErrorGuaranteed) {2000        let span = lo.to(hi);

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.