rustc_parse/parser/
stmt.rs

1use std::borrow::Cow;
2use std::mem;
3use std::ops::Bound;
4
5use ast::Label;
6use rustc_ast as ast;
7use rustc_ast::ptr::P;
8use rustc_ast::token::{self, Delimiter, TokenKind};
9use rustc_ast::util::classify::{self, TrailingBrace};
10use rustc_ast::{
11    AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, HasAttrs, Local,
12    LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
13};
14use rustc_errors::{Applicability, Diag, PResult};
15use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
16use thin_vec::{ThinVec, thin_vec};
17
18use super::attr::InnerAttrForbiddenReason;
19use super::diagnostics::AttemptLocalParseRecovery;
20use super::pat::{PatternLocation, RecoverComma};
21use super::path::PathStyle;
22use super::{
23    AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
24    Trailing, UsePreAttrPos,
25};
26use crate::errors::MalformedLoopLabel;
27use crate::{errors, exp, maybe_whole};
28
29impl<'a> Parser<'a> {
30    /// Parses a statement. This stops just before trailing semicolons on everything but items.
31    /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
32    ///
33    /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
34    /// whether or not we have attributes.
35    // Public for rustfmt usage.
36    pub(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
37        Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| {
38            e.emit();
39            self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
40            None
41        }))
42    }
43
44    /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
45    /// whether or not we have attributes.
46    // Public for `cfg_eval` macro expansion.
47    pub fn parse_stmt_without_recovery(
48        &mut self,
49        capture_semi: bool,
50        force_collect: ForceCollect,
51    ) -> PResult<'a, Option<Stmt>> {
52        let pre_attr_pos = self.collect_pos();
53        let attrs = self.parse_outer_attributes()?;
54        let lo = self.token.span;
55
56        maybe_whole!(self, NtStmt, |stmt| {
57            stmt.visit_attrs(|stmt_attrs| {
58                attrs.prepend_to_nt_inner(stmt_attrs);
59            });
60            Some(stmt.into_inner())
61        });
62
63        if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
64            self.bump();
65            let mut_let_span = lo.to(self.token.span);
66            self.dcx().emit_err(errors::InvalidVariableDeclaration {
67                span: mut_let_span,
68                sub: errors::InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
69            });
70        }
71
72        let stmt = if self.token.is_keyword(kw::Let) {
73            self.collect_tokens(None, attrs, force_collect, |this, attrs| {
74                this.expect_keyword(exp!(Let))?;
75                let local = this.parse_local(attrs)?;
76                let trailing = Trailing::from(capture_semi && this.token == token::Semi);
77                Ok((
78                    this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
79                    trailing,
80                    UsePreAttrPos::No,
81                ))
82            })?
83        } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
84            self.recover_stmt_local_after_let(
85                lo,
86                attrs,
87                errors::InvalidVariableDeclarationSub::MissingLet,
88                force_collect,
89            )?
90        } else if self.is_kw_followed_by_ident(kw::Auto) && self.may_recover() {
91            self.bump(); // `auto`
92            self.recover_stmt_local_after_let(
93                lo,
94                attrs,
95                errors::InvalidVariableDeclarationSub::UseLetNotAuto,
96                force_collect,
97            )?
98        } else if self.is_kw_followed_by_ident(sym::var) && self.may_recover() {
99            self.bump(); // `var`
100            self.recover_stmt_local_after_let(
101                lo,
102                attrs,
103                errors::InvalidVariableDeclarationSub::UseLetNotVar,
104                force_collect,
105            )?
106        } else if self.check_path()
107            && !self.token.is_qpath_start()
108            && !self.is_path_start_item()
109            && !self.is_builtin()
110        {
111            // We have avoided contextual keywords like `union`, items with `crate` visibility,
112            // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
113            // that starts like a path (1 token), but it fact not a path.
114            // Also, we avoid stealing syntax from `parse_item_`.
115            //
116            // `UsePreAttrPos::Yes` here means the attribute belongs unconditionally to the
117            // expression, not the statement. (But the statement attributes/tokens are obtained
118            // from the expression anyway, because `Stmt` delegates `HasAttrs`/`HasTokens` to
119            // the things within `StmtKind`.)
120            let stmt = self.collect_tokens(
121                Some(pre_attr_pos),
122                AttrWrapper::empty(),
123                force_collect,
124                |this, _empty_attrs| {
125                    Ok((this.parse_stmt_path_start(lo, attrs)?, Trailing::No, UsePreAttrPos::Yes))
126                },
127            );
128            match stmt {
129                Ok(stmt) => stmt,
130                Err(mut err) => {
131                    self.suggest_add_missing_let_for_stmt(&mut err);
132                    return Err(err);
133                }
134            }
135        } else if let Some(item) = self.parse_item_common(
136            attrs.clone(), // FIXME: unwanted clone of attrs
137            false,
138            true,
139            FnParseMode { req_name: |_| true, req_body: true },
140            force_collect,
141        )? {
142            self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
143        } else if self.eat(exp!(Semi)) {
144            // Do not attempt to parse an expression if we're done here.
145            self.error_outer_attrs(attrs);
146            self.mk_stmt(lo, StmtKind::Empty)
147        } else if self.token != token::CloseDelim(Delimiter::Brace) {
148            // Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case
149            // above.
150            let e = self.collect_tokens(
151                Some(pre_attr_pos),
152                AttrWrapper::empty(),
153                force_collect,
154                |this, _empty_attrs| {
155                    let (expr, _) = this.parse_expr_res(Restrictions::STMT_EXPR, attrs)?;
156                    Ok((expr, Trailing::No, UsePreAttrPos::Yes))
157                },
158            )?;
159            if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(exp!(Else)) {
160                let bl = self.parse_block()?;
161                // Destructuring assignment ... else.
162                // This is not allowed, but point it out in a nice way.
163                self.dcx().emit_err(errors::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
164            }
165            self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
166        } else {
167            self.error_outer_attrs(attrs);
168            return Ok(None);
169        };
170
171        self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt);
172        Ok(Some(stmt))
173    }
174
175    fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
176        let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
177            let path = this.parse_path(PathStyle::Expr)?;
178
179            if this.eat(exp!(Not)) {
180                let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
181                return Ok((
182                    stmt_mac,
183                    Trailing::from(this.token == token::Semi),
184                    UsePreAttrPos::No,
185                ));
186            }
187
188            let expr = if this.eat(exp!(OpenBrace)) {
189                this.parse_expr_struct(None, path, true)?
190            } else {
191                let hi = this.prev_token.span;
192                this.mk_expr(lo.to(hi), ExprKind::Path(None, path))
193            };
194
195            let expr = this.with_res(Restrictions::STMT_EXPR, |this| {
196                this.parse_expr_dot_or_call_with(attrs, expr, lo)
197            })?;
198            // `DUMMY_SP` will get overwritten later in this function
199            Ok((
200                this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)),
201                Trailing::No,
202                UsePreAttrPos::No,
203            ))
204        })?;
205
206        if let StmtKind::Expr(expr) = stmt.kind {
207            // Perform this outside of the `collect_tokens` closure, since our
208            // outer attributes do not apply to this part of the expression.
209            let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
210                this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr)
211            })?;
212            Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
213        } else {
214            Ok(stmt)
215        }
216    }
217
218    /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`.
219    /// At this point, the `!` token after the path has already been eaten.
220    fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
221        let args = self.parse_delim_args()?;
222        let hi = self.prev_token.span;
223
224        let style = match args.delim {
225            Delimiter::Brace => MacStmtStyle::Braces,
226            _ => MacStmtStyle::NoBraces,
227        };
228
229        let mac = P(MacCall { path, args });
230
231        let kind = if (style == MacStmtStyle::Braces
232            && self.token != token::Dot
233            && self.token != token::Question)
234            || self.token == token::Semi
235            || self.token == token::Eof
236        {
237            StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
238        } else {
239            // Since none of the above applied, this is an expression statement macro.
240            let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
241            let e = self.maybe_recover_from_bad_qpath(e)?;
242            let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
243            let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?;
244            StmtKind::Expr(e)
245        };
246        Ok(self.mk_stmt(lo.to(hi), kind))
247    }
248
249    /// Error on outer attributes in this context.
250    /// Also error if the previous token was a doc comment.
251    fn error_outer_attrs(&self, attrs: AttrWrapper) {
252        if !attrs.is_empty()
253            && let attrs @ [.., last] = &*attrs.take_for_recovery(self.psess)
254        {
255            if last.is_doc_comment() {
256                self.dcx().emit_err(errors::DocCommentDoesNotDocumentAnything {
257                    span: last.span,
258                    missing_comma: None,
259                });
260            } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
261                self.dcx().emit_err(errors::ExpectedStatementAfterOuterAttr { span: last.span });
262            }
263        }
264    }
265
266    fn recover_stmt_local_after_let(
267        &mut self,
268        lo: Span,
269        attrs: AttrWrapper,
270        subdiagnostic: fn(Span) -> errors::InvalidVariableDeclarationSub,
271        force_collect: ForceCollect,
272    ) -> PResult<'a, Stmt> {
273        let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
274            let local = this.parse_local(attrs)?;
275            // FIXME - maybe capture semicolon in recovery?
276            Ok((
277                this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
278                Trailing::No,
279                UsePreAttrPos::No,
280            ))
281        })?;
282        self.dcx()
283            .emit_err(errors::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
284        Ok(stmt)
285    }
286
287    /// Parses a local variable declaration.
288    fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
289        let lo = self.prev_token.span;
290
291        if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
292            self.dcx().emit_err(errors::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
293            self.bump();
294        }
295
296        let (pat, colon) =
297            self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
298
299        let (err, ty, colon_sp) = if colon {
300            // Save the state of the parser before parsing type normally, in case there is a `:`
301            // instead of an `=` typo.
302            let parser_snapshot_before_type = self.clone();
303            let colon_sp = self.prev_token.span;
304            match self.parse_ty() {
305                Ok(ty) => (None, Some(ty), Some(colon_sp)),
306                Err(mut err) => {
307                    err.span_label(
308                        colon_sp,
309                        format!(
310                            "while parsing the type for {}",
311                            pat.descr()
312                                .map_or_else(|| "the binding".to_string(), |n| format!("`{n}`"))
313                        ),
314                    );
315                    // we use noexpect here because we don't actually expect Eq to be here
316                    // but we are still checking for it in order to be able to handle it if
317                    // it is there
318                    let err = if self.check_noexpect(&token::Eq) {
319                        err.emit();
320                        None
321                    } else {
322                        // Rewind to before attempting to parse the type and continue parsing.
323                        let parser_snapshot_after_type =
324                            mem::replace(self, parser_snapshot_before_type);
325                        Some((parser_snapshot_after_type, colon_sp, err))
326                    };
327                    (err, None, Some(colon_sp))
328                }
329            }
330        } else {
331            (None, None, None)
332        };
333        let init = match (self.parse_initializer(err.is_some()), err) {
334            (Ok(init), None) => {
335                // init parsed, ty parsed
336                init
337            }
338            (Ok(init), Some((_, colon_sp, mut err))) => {
339                // init parsed, ty error
340                // Could parse the type as if it were the initializer, it is likely there was a
341                // typo in the code: `:` instead of `=`. Add suggestion and emit the error.
342                err.span_suggestion_short(
343                    colon_sp,
344                    "use `=` if you meant to assign",
345                    " =",
346                    Applicability::MachineApplicable,
347                );
348                err.emit();
349                // As this was parsed successfully, continue as if the code has been fixed for the
350                // rest of the file. It will still fail due to the emitted error, but we avoid
351                // extra noise.
352                init
353            }
354            (Err(init_err), Some((snapshot, _, ty_err))) => {
355                // init error, ty error
356                init_err.cancel();
357                // Couldn't parse the type nor the initializer, only raise the type error and
358                // return to the parser state before parsing the type as the initializer.
359                // let x: <parse_error>;
360                *self = snapshot;
361                return Err(ty_err);
362            }
363            (Err(err), None) => {
364                // init error, ty parsed
365                // Couldn't parse the initializer and we're not attempting to recover a failed
366                // parse of the type, return the error.
367                return Err(err);
368            }
369        };
370        let kind = match init {
371            None => LocalKind::Decl,
372            Some(init) => {
373                if self.eat_keyword(exp!(Else)) {
374                    if self.token.is_keyword(kw::If) {
375                        // `let...else if`. Emit the same error that `parse_block()` would,
376                        // but explicitly point out that this pattern is not allowed.
377                        let msg = "conditional `else if` is not supported for `let...else`";
378                        return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
379                    }
380                    let els = self.parse_block()?;
381                    self.check_let_else_init_bool_expr(&init);
382                    self.check_let_else_init_trailing_brace(&init);
383                    LocalKind::InitElse(init, els)
384                } else {
385                    LocalKind::Init(init)
386                }
387            }
388        };
389        let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
390        Ok(P(ast::Local {
391            ty,
392            pat,
393            kind,
394            id: DUMMY_NODE_ID,
395            span: lo.to(hi),
396            colon_sp,
397            attrs,
398            tokens: None,
399        }))
400    }
401
402    fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
403        if let ast::ExprKind::Binary(op, ..) = init.kind {
404            if op.node.is_lazy() {
405                self.dcx().emit_err(errors::InvalidExpressionInLetElse {
406                    span: init.span,
407                    operator: op.node.as_str(),
408                    sugg: errors::WrapInParentheses::Expression {
409                        left: init.span.shrink_to_lo(),
410                        right: init.span.shrink_to_hi(),
411                    },
412                });
413            }
414        }
415    }
416
417    fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
418        if let Some(trailing) = classify::expr_trailing_brace(init) {
419            let (span, sugg) = match trailing {
420                TrailingBrace::MacCall(mac) => (
421                    mac.span(),
422                    errors::WrapInParentheses::MacroArgs {
423                        left: mac.args.dspan.open,
424                        right: mac.args.dspan.close,
425                    },
426                ),
427                TrailingBrace::Expr(expr) => (
428                    expr.span,
429                    errors::WrapInParentheses::Expression {
430                        left: expr.span.shrink_to_lo(),
431                        right: expr.span.shrink_to_hi(),
432                    },
433                ),
434            };
435            self.dcx().emit_err(errors::InvalidCurlyInLetElse {
436                span: span.with_lo(span.hi() - BytePos(1)),
437                sugg,
438            });
439        }
440    }
441
442    /// Parses the RHS of a local variable declaration (e.g., `= 14;`).
443    fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> {
444        let eq_consumed = match self.token.kind {
445            token::BinOpEq(..) => {
446                // Recover `let x <op>= 1` as `let x = 1` We must not use `+ BytePos(1)` here
447                // because `<op>` can be a multi-byte lookalike that was recovered, e.g. `âž–=` (the
448                // `âž–` is a U+2796 Heavy Minus Sign Unicode Character) that was recovered as a
449                // `-=`.
450                let extra_op_span = self.psess.source_map().start_point(self.token.span);
451                self.dcx().emit_err(errors::CompoundAssignmentExpressionInLet {
452                    span: self.token.span,
453                    suggestion: extra_op_span,
454                });
455                self.bump();
456                true
457            }
458            _ => self.eat(exp!(Eq)),
459        };
460
461        Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None })
462    }
463
464    /// Parses a block. No inner attributes are allowed.
465    pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
466        let (attrs, block) = self.parse_inner_attrs_and_block()?;
467        if let [.., last] = &*attrs {
468            let suggest_to_outer = match &last.kind {
469                ast::AttrKind::Normal(attr) => attr.item.is_valid_for_outer_style(),
470                _ => false,
471            };
472            self.error_on_forbidden_inner_attr(
473                last.span,
474                super::attr::InnerAttrPolicy::Forbidden(Some(
475                    InnerAttrForbiddenReason::InCodeBlock,
476                )),
477                suggest_to_outer,
478            );
479        }
480        Ok(block)
481    }
482
483    fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
484        let prev = self.prev_token.span;
485        let sp = self.token.span;
486        let mut e = self.dcx().struct_span_err(sp, msg);
487        let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
488
489        // Check to see if the user has written something like
490        //
491        //    if (cond)
492        //      bar;
493        //
494        // which is valid in other languages, but not Rust.
495        match self.parse_stmt_without_recovery(false, ForceCollect::No) {
496            // If the next token is an open brace, e.g., we have:
497            //
498            //     if expr other_expr {
499            //        ^    ^          ^- lookahead(1) is a brace
500            //        |    |- current token is not "else"
501            //        |- (statement we just parsed)
502            //
503            // the place-inside-a-block suggestion would be more likely wrong than right.
504            //
505            // FIXME(compiler-errors): this should probably parse an arbitrary expr and not
506            // just lookahead one token, so we can see if there's a brace after _that_,
507            // since we want to protect against:
508            //     `if 1 1 + 1 {` being suggested as  `if { 1 } 1 + 1 {`
509            //                                            +   +
510            Ok(Some(_))
511                if (!self.token.is_keyword(kw::Else)
512                    && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)))
513                    || do_not_suggest_help => {}
514            // Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
515            Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
516            Ok(Some(stmt)) => {
517                let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
518                let stmt_span = if stmt_own_line && self.eat(exp!(Semi)) {
519                    // Expand the span to include the semicolon.
520                    stmt.span.with_hi(self.prev_token.span.hi())
521                } else {
522                    stmt.span
523                };
524                self.suggest_fixes_misparsed_for_loop_head(
525                    &mut e,
526                    prev.between(sp),
527                    stmt_span,
528                    &stmt.kind,
529                );
530            }
531            Err(e) => {
532                self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
533                e.cancel();
534            }
535            _ => {}
536        }
537        e.span_label(sp, "expected `{`");
538        e
539    }
540
541    fn suggest_fixes_misparsed_for_loop_head(
542        &self,
543        e: &mut Diag<'_>,
544        between: Span,
545        stmt_span: Span,
546        stmt_kind: &StmtKind,
547    ) {
548        match (&self.token.kind, &stmt_kind) {
549            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
550                if let ExprKind::Call(..) = expr.kind =>
551            {
552                // for _ in x y() {}
553                e.span_suggestion_verbose(
554                    between,
555                    "you might have meant to write a method call",
556                    ".".to_string(),
557                    Applicability::MaybeIncorrect,
558                );
559            }
560            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
561                if let ExprKind::Field(..) = expr.kind =>
562            {
563                // for _ in x y.z {}
564                e.span_suggestion_verbose(
565                    between,
566                    "you might have meant to write a field access",
567                    ".".to_string(),
568                    Applicability::MaybeIncorrect,
569                );
570            }
571            (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr))
572                if let ExprKind::Struct(expr) = &expr.kind
573                    && let None = expr.qself
574                    && expr.path.segments.len() == 1 =>
575            {
576                // This is specific to "mistyped `if` condition followed by empty body"
577                //
578                // for _ in x y {}
579                e.span_suggestion_verbose(
580                    between,
581                    "you might have meant to write a field access",
582                    ".".to_string(),
583                    Applicability::MaybeIncorrect,
584                );
585            }
586            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
587                if let ExprKind::Lit(lit) = expr.kind
588                    && let None = lit.suffix
589                    && let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
590            {
591                // for _ in x 0 {}
592                // for _ in x 0.0 {}
593                e.span_suggestion_verbose(
594                    between,
595                    format!("you might have meant to write a field access"),
596                    ".".to_string(),
597                    Applicability::MaybeIncorrect,
598                );
599            }
600            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
601                if let ExprKind::Loop(..)
602                | ExprKind::If(..)
603                | ExprKind::While(..)
604                | ExprKind::Match(..)
605                | ExprKind::ForLoop { .. }
606                | ExprKind::TryBlock(..)
607                | ExprKind::Ret(..)
608                | ExprKind::Closure(..)
609                | ExprKind::Struct(..)
610                | ExprKind::Try(..) = expr.kind =>
611            {
612                // These are more likely to have been meant as a block body.
613                e.multipart_suggestion(
614                    "you might have meant to write this as part of a block",
615                    vec![
616                        (stmt_span.shrink_to_lo(), "{ ".to_string()),
617                        (stmt_span.shrink_to_hi(), " }".to_string()),
618                    ],
619                    // Speculative; has been misleading in the past (#46836).
620                    Applicability::MaybeIncorrect,
621                );
622            }
623            (token::OpenDelim(Delimiter::Brace), _) => {}
624            (_, _) => {
625                e.multipart_suggestion(
626                    "you might have meant to write this as part of a block",
627                    vec![
628                        (stmt_span.shrink_to_lo(), "{ ".to_string()),
629                        (stmt_span.shrink_to_hi(), " }".to_string()),
630                    ],
631                    // Speculative; has been misleading in the past (#46836).
632                    Applicability::MaybeIncorrect,
633                );
634            }
635        }
636    }
637
638    fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
639        let tok = super::token_descr(&self.token);
640        let msg = format!("expected `{{`, found {tok}");
641        Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
642    }
643
644    /// Parses a block. Inner attributes are allowed.
645    pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
646        self.parse_block_common(self.token.span, BlockCheckMode::Default, true)
647    }
648
649    /// Parses a block. Inner attributes are allowed.
650    pub(super) fn parse_block_common(
651        &mut self,
652        lo: Span,
653        blk_mode: BlockCheckMode,
654        can_be_struct_literal: bool,
655    ) -> PResult<'a, (AttrVec, P<Block>)> {
656        maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block));
657
658        let maybe_ident = self.prev_token.clone();
659        self.maybe_recover_unexpected_block_label();
660        if !self.eat(exp!(OpenBrace)) {
661            return self.error_block_no_opening_brace();
662        }
663
664        let attrs = self.parse_inner_attributes()?;
665        let tail = match self.maybe_suggest_struct_literal(
666            lo,
667            blk_mode,
668            maybe_ident,
669            can_be_struct_literal,
670        ) {
671            Some(tail) => tail?,
672            None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
673        };
674        Ok((attrs, tail))
675    }
676
677    /// Parses the rest of a block expression or function body.
678    /// Precondition: already parsed the '{'.
679    pub(crate) fn parse_block_tail(
680        &mut self,
681        lo: Span,
682        s: BlockCheckMode,
683        recover: AttemptLocalParseRecovery,
684    ) -> PResult<'a, P<Block>> {
685        let mut stmts = ThinVec::new();
686        let mut snapshot = None;
687        while !self.eat(exp!(CloseBrace)) {
688            if self.token == token::Eof {
689                break;
690            }
691            if self.is_vcs_conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
692                // Account for `<<<<<<<` diff markers. We can't proactively error here because
693                // that can be a valid path start, so we snapshot and reparse only we've
694                // encountered another parse error.
695                snapshot = Some(self.create_snapshot_for_diagnostic());
696            }
697            let stmt = match self.parse_full_stmt(recover) {
698                Err(mut err) if recover.yes() => {
699                    if let Some(ref mut snapshot) = snapshot {
700                        snapshot.recover_vcs_conflict_marker();
701                    }
702                    if self.token == token::Colon {
703                        // if a previous and next token of the current one is
704                        // integer literal (e.g. `1:42`), it's likely a range
705                        // expression for Pythonistas and we can suggest so.
706                        if self.prev_token.is_integer_lit()
707                            && self.may_recover()
708                            && self.look_ahead(1, |token| token.is_integer_lit())
709                        {
710                            // FIXME(hkmatsumoto): Might be better to trigger
711                            // this only when parsing an index expression.
712                            err.span_suggestion_verbose(
713                                self.token.span,
714                                "you might have meant a range expression",
715                                "..",
716                                Applicability::MaybeIncorrect,
717                            );
718                        } else {
719                            // if next token is following a colon, it's likely a path
720                            // and we can suggest a path separator
721                            self.bump();
722                            if self.token.span.lo() == self.prev_token.span.hi() {
723                                err.span_suggestion_verbose(
724                                    self.prev_token.span,
725                                    "maybe write a path separator here",
726                                    "::",
727                                    Applicability::MaybeIncorrect,
728                                );
729                            }
730                            if self.psess.unstable_features.is_nightly_build() {
731                                // FIXME(Nilstrieb): Remove this again after a few months.
732                                err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
733                            }
734                        }
735                    }
736
737                    let guar = err.emit();
738                    self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
739                    Some(self.mk_stmt_err(self.token.span, guar))
740                }
741                Ok(stmt) => stmt,
742                Err(err) => return Err(err),
743            };
744            if let Some(stmt) = stmt {
745                stmts.push(stmt);
746            } else {
747                // Found only `;` or `}`.
748                continue;
749            };
750        }
751        Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
752    }
753
754    fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
755        let Some((ident, _)) = self.token.ident() else {
756            return;
757        };
758        if let Some(c) = ident.name.as_str().chars().next()
759            && c.is_uppercase()
760        {
761            return;
762        }
763        if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
764            return;
765        }
766        if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
767            // Likely `foo.await bar`
768        } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
769            // Likely `foo bar`
770        } else if self.prev_token.kind == token::Question {
771            // `foo? bar`
772        } else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
773            // `foo() bar`
774        } else {
775            return;
776        }
777        if self.token.span == self.prev_token.span {
778            // Account for syntax errors in proc-macros.
779            return;
780        }
781        if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
782            err.span_suggestion_verbose(
783                self.prev_token.span.between(self.token.span),
784                "you might have meant to write a field access",
785                ".".to_string(),
786                Applicability::MaybeIncorrect,
787            );
788        }
789        if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
790            err.span_suggestion_verbose(
791                self.prev_token.span.between(self.token.span),
792                "you might have meant to write a method call",
793                ".".to_string(),
794                Applicability::MaybeIncorrect,
795            );
796        }
797    }
798
799    /// Parses a statement, including the trailing semicolon.
800    pub fn parse_full_stmt(
801        &mut self,
802        recover: AttemptLocalParseRecovery,
803    ) -> PResult<'a, Option<Stmt>> {
804        // Skip looking for a trailing semicolon when we have an interpolated statement.
805        maybe_whole!(self, NtStmt, |stmt| Some(stmt.into_inner()));
806
807        let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else {
808            return Ok(None);
809        };
810
811        let mut eat_semi = true;
812        let mut add_semi_to_stmt = false;
813
814        match &mut stmt.kind {
815            // Expression without semicolon.
816            StmtKind::Expr(expr)
817                if classify::expr_requires_semi_to_be_stmt(expr)
818                    && !expr.attrs.is_empty()
819                    && ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)]
820                        .contains(&self.token.kind) =>
821            {
822                // The user has written `#[attr] expr` which is unsupported. (#106020)
823                let guar = self.attr_on_non_tail_expr(&expr);
824                // We already emitted an error, so don't emit another type error
825                let sp = expr.span.to(self.prev_token.span);
826                *expr = self.mk_expr_err(sp, guar);
827            }
828
829            // Expression without semicolon.
830            StmtKind::Expr(expr)
831                if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
832            {
833                // Just check for errors and recover; do not eat semicolon yet.
834
835                let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]);
836
837                // Try to both emit a better diagnostic, and avoid further errors by replacing
838                // the `expr` with `ExprKind::Err`.
839                let replace_with_err = 'break_recover: {
840                    match expect_result {
841                        Ok(Recovered::No) => None,
842                        Ok(Recovered::Yes(guar)) => {
843                            // Skip type error to avoid extra errors.
844                            Some(guar)
845                        }
846                        Err(e) => {
847                            if self.recover_colon_as_semi() {
848                                // recover_colon_as_semi has already emitted a nicer error.
849                                e.delay_as_bug();
850                                add_semi_to_stmt = true;
851                                eat_semi = false;
852
853                                break 'break_recover None;
854                            }
855
856                            match &expr.kind {
857                                ExprKind::Path(None, ast::Path { segments, .. })
858                                    if let [segment] = segments.as_slice() =>
859                                {
860                                    if self.token == token::Colon
861                                        && self.look_ahead(1, |token| {
862                                            token.is_whole_block()
863                                                || matches!(
864                                                    token.kind,
865                                                    token::Ident(
866                                                        kw::For | kw::Loop | kw::While,
867                                                        token::IdentIsRaw::No
868                                                    ) | token::OpenDelim(Delimiter::Brace)
869                                                )
870                                        })
871                                    {
872                                        let snapshot = self.create_snapshot_for_diagnostic();
873                                        let label = Label {
874                                            ident: Ident::from_str_and_span(
875                                                &format!("'{}", segment.ident),
876                                                segment.ident.span,
877                                            ),
878                                        };
879                                        match self.parse_expr_labeled(label, false) {
880                                            Ok(labeled_expr) => {
881                                                e.cancel();
882                                                self.dcx().emit_err(MalformedLoopLabel {
883                                                    span: label.ident.span,
884                                                    suggestion: label.ident.span.shrink_to_lo(),
885                                                });
886                                                *expr = labeled_expr;
887                                                break 'break_recover None;
888                                            }
889                                            Err(err) => {
890                                                err.cancel();
891                                                self.restore_snapshot(snapshot);
892                                            }
893                                        }
894                                    }
895                                }
896                                _ => {}
897                            }
898
899                            let res =
900                                self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
901
902                            Some(if recover.no() {
903                                res?
904                            } else {
905                                res.unwrap_or_else(|mut e| {
906                                    self.recover_missing_dot(&mut e);
907                                    let guar = e.emit();
908                                    self.recover_stmt();
909                                    guar
910                                })
911                            })
912                        }
913                    }
914                };
915
916                if let Some(guar) = replace_with_err {
917                    // We already emitted an error, so don't emit another type error
918                    let sp = expr.span.to(self.prev_token.span);
919                    *expr = self.mk_expr_err(sp, guar);
920                }
921            }
922            StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
923            StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
924                // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
925                match &mut local.kind {
926                    LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
927                        self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
928                            |mut e| {
929                                self.recover_missing_dot(&mut e);
930                                e
931                            },
932                        )?;
933                        // We found `foo<bar, baz>`, have we fully recovered?
934                        self.expect_semi()?;
935                    }
936                    LocalKind::Decl => {
937                        if let Some(colon_sp) = local.colon_sp {
938                            e.span_label(
939                                colon_sp,
940                                format!(
941                                    "while parsing the type for {}",
942                                    local.pat.descr().map_or_else(
943                                        || "the binding".to_string(),
944                                        |n| format!("`{n}`")
945                                    )
946                                ),
947                            );
948                            let suggest_eq = if self.token == token::Dot
949                                && let _ = self.bump()
950                                && let mut snapshot = self.create_snapshot_for_diagnostic()
951                                && let Ok(_) = snapshot
952                                    .parse_dot_suffix_expr(
953                                        colon_sp,
954                                        self.mk_expr_err(
955                                            colon_sp,
956                                            self.dcx()
957                                                .delayed_bug("error during `:` -> `=` recovery"),
958                                        ),
959                                    )
960                                    .map_err(Diag::cancel)
961                            {
962                                true
963                            } else if let Some(op) = self.check_assoc_op()
964                                && op.node.can_continue_expr_unambiguously()
965                            {
966                                true
967                            } else {
968                                false
969                            };
970                            if suggest_eq {
971                                e.span_suggestion_short(
972                                    colon_sp,
973                                    "use `=` if you meant to assign",
974                                    "=",
975                                    Applicability::MaybeIncorrect,
976                                );
977                            }
978                        }
979                        return Err(e);
980                    }
981                }
982                eat_semi = false;
983            }
984            StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
985                eat_semi = false
986            }
987        }
988
989        if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) {
990            stmt = stmt.add_trailing_semicolon();
991        }
992
993        stmt.span = stmt.span.to(self.prev_token.span);
994        Ok(Some(stmt))
995    }
996
997    pub(super) fn mk_block(
998        &self,
999        stmts: ThinVec<Stmt>,
1000        rules: BlockCheckMode,
1001        span: Span,
1002    ) -> P<Block> {
1003        P(Block {
1004            stmts,
1005            id: DUMMY_NODE_ID,
1006            rules,
1007            span,
1008            tokens: None,
1009            could_be_bare_literal: false,
1010        })
1011    }
1012
1013    pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1014        Stmt { id: DUMMY_NODE_ID, kind, span }
1015    }
1016
1017    pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1018        self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1019    }
1020
1021    pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> {
1022        self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
1023    }
1024}