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

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy