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