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, true, 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        can_be_struct_literal: bool,
683        loop_header: Option<Span>,
684    ) -> PResult<'a, (AttrVec, P<Block>)> {
685        maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block));
686
687        let maybe_ident = self.prev_token.clone();
688        self.maybe_recover_unexpected_block_label(loop_header);
689        if !self.eat(exp!(OpenBrace)) {
690            return self.error_block_no_opening_brace();
691        }
692
693        let attrs = self.parse_inner_attributes()?;
694        let tail = match self.maybe_suggest_struct_literal(
695            lo,
696            blk_mode,
697            maybe_ident,
698            can_be_struct_literal,
699        ) {
700            Some(tail) => tail?,
701            None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
702        };
703        Ok((attrs, tail))
704    }
705
706    /// Parses the rest of a block expression or function body.
707    /// Precondition: already parsed the '{'.
708    pub(crate) fn parse_block_tail(
709        &mut self,
710        lo: Span,
711        s: BlockCheckMode,
712        recover: AttemptLocalParseRecovery,
713    ) -> PResult<'a, P<Block>> {
714        let mut stmts = ThinVec::new();
715        let mut snapshot = None;
716        while !self.eat(exp!(CloseBrace)) {
717            if self.token == token::Eof {
718                break;
719            }
720            if self.is_vcs_conflict_marker(&TokenKind::Shl, &TokenKind::Lt) {
721                // Account for `<<<<<<<` diff markers. We can't proactively error here because
722                // that can be a valid path start, so we snapshot and reparse only we've
723                // encountered another parse error.
724                snapshot = Some(self.create_snapshot_for_diagnostic());
725            }
726            let stmt = match self.parse_full_stmt(recover) {
727                Err(mut err) if recover.yes() => {
728                    if let Some(ref mut snapshot) = snapshot {
729                        snapshot.recover_vcs_conflict_marker();
730                    }
731                    if self.token == token::Colon {
732                        // if a previous and next token of the current one is
733                        // integer literal (e.g. `1:42`), it's likely a range
734                        // expression for Pythonistas and we can suggest so.
735                        if self.prev_token.is_integer_lit()
736                            && self.may_recover()
737                            && self.look_ahead(1, |token| token.is_integer_lit())
738                        {
739                            // FIXME(hkmatsumoto): Might be better to trigger
740                            // this only when parsing an index expression.
741                            err.span_suggestion_verbose(
742                                self.token.span,
743                                "you might have meant a range expression",
744                                "..",
745                                Applicability::MaybeIncorrect,
746                            );
747                        } else {
748                            // if next token is following a colon, it's likely a path
749                            // and we can suggest a path separator
750                            self.bump();
751                            if self.token.span.lo() == self.prev_token.span.hi() {
752                                err.span_suggestion_verbose(
753                                    self.prev_token.span,
754                                    "maybe write a path separator here",
755                                    "::",
756                                    Applicability::MaybeIncorrect,
757                                );
758                            }
759                            if self.psess.unstable_features.is_nightly_build() {
760                                // FIXME(Nilstrieb): Remove this again after a few months.
761                                err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
762                            }
763                        }
764                    }
765
766                    let guar = err.emit();
767                    self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
768                    Some(self.mk_stmt_err(self.token.span, guar))
769                }
770                Ok(stmt) => stmt,
771                Err(err) => return Err(err),
772            };
773            if let Some(stmt) = stmt {
774                stmts.push(stmt);
775            } else {
776                // Found only `;` or `}`.
777                continue;
778            };
779        }
780        Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
781    }
782
783    fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
784        let Some((ident, _)) = self.token.ident() else {
785            return;
786        };
787        if let Some(c) = ident.name.as_str().chars().next()
788            && c.is_uppercase()
789        {
790            return;
791        }
792        if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
793            return;
794        }
795        if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
796            // Likely `foo.await bar`
797        } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
798            // Likely `foo bar`
799        } else if self.prev_token.kind == token::Question {
800            // `foo? bar`
801        } else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
802            // `foo() bar`
803        } else {
804            return;
805        }
806        if self.token.span == self.prev_token.span {
807            // Account for syntax errors in proc-macros.
808            return;
809        }
810        if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
811            err.span_suggestion_verbose(
812                self.prev_token.span.between(self.token.span),
813                "you might have meant to write a field access",
814                ".".to_string(),
815                Applicability::MaybeIncorrect,
816            );
817        }
818        if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
819            err.span_suggestion_verbose(
820                self.prev_token.span.between(self.token.span),
821                "you might have meant to write a method call",
822                ".".to_string(),
823                Applicability::MaybeIncorrect,
824            );
825        }
826    }
827
828    /// Parses a statement, including the trailing semicolon.
829    pub fn parse_full_stmt(
830        &mut self,
831        recover: AttemptLocalParseRecovery,
832    ) -> PResult<'a, Option<Stmt>> {
833        // Skip looking for a trailing semicolon when we have a metavar seq.
834        if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
835            // Why pass `true` for `force_full_expr`? Statement expressions are less expressive
836            // than "full" expressions, due to the `STMT_EXPR` restriction, and sometimes need
837            // parentheses. E.g. the "full" expression `match paren_around_match {} | true` when
838            // used in statement context must be written `(match paren_around_match {} | true)`.
839            // However, if the expression we are parsing in this statement context was pasted by a
840            // declarative macro, it may have come from a "full" expression context, and lack
841            // these parentheses. So we lift the `STMT_EXPR` restriction to ensure the statement
842            // will reparse successfully.
843            this.parse_stmt_without_recovery(false, ForceCollect::No, true)
844        }) {
845            let stmt = stmt.expect("an actual statement");
846            return Ok(Some(stmt));
847        }
848
849        let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?
850        else {
851            return Ok(None);
852        };
853
854        let mut eat_semi = true;
855        let mut add_semi_to_stmt = false;
856
857        match &mut stmt.kind {
858            // Expression without semicolon.
859            StmtKind::Expr(expr)
860                if classify::expr_requires_semi_to_be_stmt(expr)
861                    && !expr.attrs.is_empty()
862                    && ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)]
863                        .contains(&self.token.kind) =>
864            {
865                // The user has written `#[attr] expr` which is unsupported. (#106020)
866                let guar = self.attr_on_non_tail_expr(&expr);
867                // We already emitted an error, so don't emit another type error
868                let sp = expr.span.to(self.prev_token.span);
869                *expr = self.mk_expr_err(sp, guar);
870            }
871
872            // Expression without semicolon.
873            StmtKind::Expr(expr)
874                if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
875            {
876                // Just check for errors and recover; do not eat semicolon yet.
877
878                let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]);
879
880                // Try to both emit a better diagnostic, and avoid further errors by replacing
881                // the `expr` with `ExprKind::Err`.
882                let replace_with_err = 'break_recover: {
883                    match expect_result {
884                        Ok(Recovered::No) => None,
885                        Ok(Recovered::Yes(guar)) => {
886                            // Skip type error to avoid extra errors.
887                            Some(guar)
888                        }
889                        Err(e) => {
890                            if self.recover_colon_as_semi() {
891                                // recover_colon_as_semi has already emitted a nicer error.
892                                e.delay_as_bug();
893                                add_semi_to_stmt = true;
894                                eat_semi = false;
895
896                                break 'break_recover None;
897                            }
898
899                            match &expr.kind {
900                                ExprKind::Path(None, ast::Path { segments, .. })
901                                    if let [segment] = segments.as_slice() =>
902                                {
903                                    if self.token == token::Colon
904                                        && self.look_ahead(1, |token| {
905                                            token.is_whole_block()
906                                                || matches!(
907                                                    token.kind,
908                                                    token::Ident(
909                                                        kw::For | kw::Loop | kw::While,
910                                                        token::IdentIsRaw::No
911                                                    ) | token::OpenDelim(Delimiter::Brace)
912                                                )
913                                        })
914                                    {
915                                        let snapshot = self.create_snapshot_for_diagnostic();
916                                        let label = Label {
917                                            ident: Ident::from_str_and_span(
918                                                &format!("'{}", segment.ident),
919                                                segment.ident.span,
920                                            ),
921                                        };
922                                        match self.parse_expr_labeled(label, false) {
923                                            Ok(labeled_expr) => {
924                                                e.cancel();
925                                                self.dcx().emit_err(MalformedLoopLabel {
926                                                    span: label.ident.span,
927                                                    suggestion: label.ident.span.shrink_to_lo(),
928                                                });
929                                                *expr = labeled_expr;
930                                                break 'break_recover None;
931                                            }
932                                            Err(err) => {
933                                                err.cancel();
934                                                self.restore_snapshot(snapshot);
935                                            }
936                                        }
937                                    }
938                                }
939                                _ => {}
940                            }
941
942                            let res =
943                                self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
944
945                            Some(if recover.no() {
946                                res?
947                            } else {
948                                res.unwrap_or_else(|mut e| {
949                                    self.recover_missing_dot(&mut e);
950                                    let guar = e.emit();
951                                    self.recover_stmt();
952                                    guar
953                                })
954                            })
955                        }
956                    }
957                };
958
959                if let Some(guar) = replace_with_err {
960                    // We already emitted an error, so don't emit another type error
961                    let sp = expr.span.to(self.prev_token.span);
962                    *expr = self.mk_expr_err(sp, guar);
963                }
964            }
965            StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
966            StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
967                // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
968                match &mut local.kind {
969                    LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
970                        self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
971                            |mut e| {
972                                self.recover_missing_dot(&mut e);
973                                e
974                            },
975                        )?;
976                        // We found `foo<bar, baz>`, have we fully recovered?
977                        self.expect_semi()?;
978                    }
979                    LocalKind::Decl => {
980                        if let Some(colon_sp) = local.colon_sp {
981                            e.span_label(
982                                colon_sp,
983                                format!(
984                                    "while parsing the type for {}",
985                                    local.pat.descr().map_or_else(
986                                        || "the binding".to_string(),
987                                        |n| format!("`{n}`")
988                                    )
989                                ),
990                            );
991                            let suggest_eq = if self.token == token::Dot
992                                && let _ = self.bump()
993                                && let mut snapshot = self.create_snapshot_for_diagnostic()
994                                && let Ok(_) = snapshot
995                                    .parse_dot_suffix_expr(
996                                        colon_sp,
997                                        self.mk_expr_err(
998                                            colon_sp,
999                                            self.dcx()
1000                                                .delayed_bug("error during `:` -> `=` recovery"),
1001                                        ),
1002                                    )
1003                                    .map_err(Diag::cancel)
1004                            {
1005                                true
1006                            } else if let Some(op) = self.check_assoc_op()
1007                                && op.node.can_continue_expr_unambiguously()
1008                            {
1009                                true
1010                            } else {
1011                                false
1012                            };
1013                            if suggest_eq {
1014                                e.span_suggestion_short(
1015                                    colon_sp,
1016                                    "use `=` if you meant to assign",
1017                                    "=",
1018                                    Applicability::MaybeIncorrect,
1019                                );
1020                            }
1021                        }
1022                        return Err(e);
1023                    }
1024                }
1025                eat_semi = false;
1026            }
1027            StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
1028                eat_semi = false
1029            }
1030        }
1031
1032        if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) {
1033            stmt = stmt.add_trailing_semicolon();
1034        }
1035
1036        stmt.span = stmt.span.to(self.prev_token.span);
1037        Ok(Some(stmt))
1038    }
1039
1040    pub(super) fn mk_block(
1041        &self,
1042        stmts: ThinVec<Stmt>,
1043        rules: BlockCheckMode,
1044        span: Span,
1045    ) -> P<Block> {
1046        P(Block {
1047            stmts,
1048            id: DUMMY_NODE_ID,
1049            rules,
1050            span,
1051            tokens: None,
1052            could_be_bare_literal: false,
1053        })
1054    }
1055
1056    pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1057        Stmt { id: DUMMY_NODE_ID, kind, span }
1058    }
1059
1060    pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1061        self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1062    }
1063
1064    pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> {
1065        self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
1066    }
1067}