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