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 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 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(); 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(); 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 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(), 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 self.error_outer_attrs(attrs);
164 self.mk_stmt(lo, StmtKind::Empty)
165 } else if self.token != token::CloseBrace {
166 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 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 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 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 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 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 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 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 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 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 let err = if self.check_noexpect(&token::Eq) {
341 err.emit();
342 None
343 } else {
344 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
359 }
360 (Ok(init), Some((_, colon_sp, mut err))) => {
361 err.span_suggestion_short(
365 colon_sp,
366 "use `=` if you meant to assign",
367 " =",
368 Applicability::MachineApplicable,
369 );
370 err.emit();
371 init
375 }
376 (Err(init_err), Some((snapshot, _, ty_err))) => {
377 init_err.cancel();
379 *self = snapshot;
383 return Err(ty_err);
384 }
385 (Err(err), None) => {
386 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 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 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 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 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 match self.parse_stmt_without_recovery(false, ForceCollect::No, false) {
532 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 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 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 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 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 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 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 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 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 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 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 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 pub(crate) 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 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 self.prev_token.is_integer_lit()
744 && self.may_recover()
745 && self.look_ahead(1, |token| token.is_integer_lit())
746 {
747 err.span_suggestion_verbose(
750 self.token.span,
751 "you might have meant a range expression",
752 "..",
753 Applicability::MaybeIncorrect,
754 );
755 } else {
756 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 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 } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
802 } else if self.prev_token.kind == token::Question {
804 } else if self.prev_token.kind == token::CloseParen {
806 } else {
808 return;
809 }
810 if self.token.span == self.prev_token.span {
811 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 pub fn parse_full_stmt(
834 &mut self,
835 recover: AttemptLocalParseRecovery,
836 ) -> PResult<'a, Option<Stmt>> {
837 if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
839 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 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 let guar = self.attr_on_non_tail_expr(&expr);
870 let sp = expr.span.to(self.prev_token.span);
872 *expr = self.mk_expr_err(sp, guar);
873 }
874
875 StmtKind::Expr(expr)
877 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
878 {
879 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 let replace_with_err = 'break_recover: {
891 match expect_result {
892 Ok(Recovered::No) => None,
893 Ok(Recovered::Yes(guar)) => {
894 Some(guar)
896 }
897 Err(e) => {
898 if self.recover_colon_as_semi() {
899 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 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 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 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}