1use std::borrow::Cow;
2use std::mem;
3use std::ops::Bound;
4
5use ast::Label;
6use rustc_ast as ast;
7use rustc_ast::ptr::P;
8use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
9use rustc_ast::util::classify::{self, TrailingBrace};
10use rustc_ast::{
11 AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, HasAttrs, Local,
12 LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
13};
14use rustc_errors::{Applicability, Diag, PResult};
15use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
16use thin_vec::{ThinVec, thin_vec};
17
18use super::attr::InnerAttrForbiddenReason;
19use super::diagnostics::AttemptLocalParseRecovery;
20use super::pat::{PatternLocation, RecoverComma};
21use super::path::PathStyle;
22use super::{
23 AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
24 Trailing, UsePreAttrPos,
25};
26use crate::errors::MalformedLoopLabel;
27use crate::{errors, exp, maybe_whole};
28
29impl<'a> Parser<'a> {
30 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::Let) {
77 self.collect_tokens(None, attrs, force_collect, |this, attrs| {
78 this.expect_keyword(exp!(Let))?;
79 let local = this.parse_local(attrs)?;
80 let trailing = Trailing::from(capture_semi && this.token == token::Semi);
81 Ok((
82 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
83 trailing,
84 UsePreAttrPos::No,
85 ))
86 })?
87 } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
88 self.recover_stmt_local_after_let(
89 lo,
90 attrs,
91 errors::InvalidVariableDeclarationSub::MissingLet,
92 force_collect,
93 )?
94 } else if self.is_kw_followed_by_ident(kw::Auto) && self.may_recover() {
95 self.bump(); self.recover_stmt_local_after_let(
97 lo,
98 attrs,
99 errors::InvalidVariableDeclarationSub::UseLetNotAuto,
100 force_collect,
101 )?
102 } else if self.is_kw_followed_by_ident(sym::var) && self.may_recover() {
103 self.bump(); self.recover_stmt_local_after_let(
105 lo,
106 attrs,
107 errors::InvalidVariableDeclarationSub::UseLetNotVar,
108 force_collect,
109 )?
110 } else if self.check_path()
111 && !self.token.is_qpath_start()
112 && !self.is_path_start_item()
113 && !self.is_builtin()
114 {
115 let stmt = self.collect_tokens(
125 Some(pre_attr_pos),
126 AttrWrapper::empty(),
127 force_collect,
128 |this, _empty_attrs| {
129 Ok((this.parse_stmt_path_start(lo, attrs)?, Trailing::No, UsePreAttrPos::Yes))
130 },
131 );
132 match stmt {
133 Ok(stmt) => stmt,
134 Err(mut err) => {
135 self.suggest_add_missing_let_for_stmt(&mut err);
136 return Err(err);
137 }
138 }
139 } else if let Some(item) = self.parse_item_common(
140 attrs.clone(), false,
142 true,
143 FnParseMode { req_name: |_| true, req_body: true },
144 force_collect,
145 )? {
146 self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
147 } else if self.eat(exp!(Semi)) {
148 self.error_outer_attrs(attrs);
150 self.mk_stmt(lo, StmtKind::Empty)
151 } else if self.token != token::CloseDelim(Delimiter::Brace) {
152 let restrictions =
155 if force_full_expr { Restrictions::empty() } else { Restrictions::STMT_EXPR };
156 let e = self.collect_tokens(
157 Some(pre_attr_pos),
158 AttrWrapper::empty(),
159 force_collect,
160 |this, _empty_attrs| {
161 let (expr, _) = this.parse_expr_res(restrictions, attrs)?;
162 Ok((expr, Trailing::No, UsePreAttrPos::Yes))
163 },
164 )?;
165 if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(exp!(Else)) {
166 let bl = self.parse_block()?;
167 self.dcx().emit_err(errors::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
170 }
171 self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
172 } else {
173 self.error_outer_attrs(attrs);
174 return Ok(None);
175 };
176
177 self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt);
178 Ok(Some(stmt))
179 }
180
181 fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
182 let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
183 let path = this.parse_path(PathStyle::Expr)?;
184
185 if this.eat(exp!(Bang)) {
186 let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
187 return Ok((
188 stmt_mac,
189 Trailing::from(this.token == token::Semi),
190 UsePreAttrPos::No,
191 ));
192 }
193
194 let expr = if this.eat(exp!(OpenBrace)) {
195 this.parse_expr_struct(None, path, true)?
196 } else {
197 let hi = this.prev_token.span;
198 this.mk_expr(lo.to(hi), ExprKind::Path(None, path))
199 };
200
201 let expr = this.with_res(Restrictions::STMT_EXPR, |this| {
202 this.parse_expr_dot_or_call_with(attrs, expr, lo)
203 })?;
204 Ok((
206 this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)),
207 Trailing::No,
208 UsePreAttrPos::No,
209 ))
210 })?;
211
212 if let StmtKind::Expr(expr) = stmt.kind {
213 let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
216 this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr)
217 })?;
218 Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
219 } else {
220 Ok(stmt)
221 }
222 }
223
224 fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
227 let args = self.parse_delim_args()?;
228 let hi = self.prev_token.span;
229
230 let style = match args.delim {
231 Delimiter::Brace => MacStmtStyle::Braces,
232 _ => MacStmtStyle::NoBraces,
233 };
234
235 let mac = P(MacCall { path, args });
236
237 let kind = if (style == MacStmtStyle::Braces
238 && !matches!(self.token.kind, token::Dot | token::Question))
239 || matches!(
240 self.token.kind,
241 token::Semi
242 | token::Eof
243 | token::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
244 MetaVarKind::Stmt
245 )))
246 ) {
247 StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
248 } else {
249 let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
251 let e = self.maybe_recover_from_bad_qpath(e)?;
252 let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
253 let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?;
254 StmtKind::Expr(e)
255 };
256 Ok(self.mk_stmt(lo.to(hi), kind))
257 }
258
259 fn error_outer_attrs(&self, attrs: AttrWrapper) {
262 if !attrs.is_empty()
263 && let attrs @ [.., last] = &*attrs.take_for_recovery(self.psess)
264 {
265 if last.is_doc_comment() {
266 self.dcx().emit_err(errors::DocCommentDoesNotDocumentAnything {
267 span: last.span,
268 missing_comma: None,
269 });
270 } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
271 self.dcx().emit_err(errors::ExpectedStatementAfterOuterAttr { span: last.span });
272 }
273 }
274 }
275
276 fn recover_stmt_local_after_let(
277 &mut self,
278 lo: Span,
279 attrs: AttrWrapper,
280 subdiagnostic: fn(Span) -> errors::InvalidVariableDeclarationSub,
281 force_collect: ForceCollect,
282 ) -> PResult<'a, Stmt> {
283 let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
284 let local = this.parse_local(attrs)?;
285 Ok((
287 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
288 Trailing::No,
289 UsePreAttrPos::No,
290 ))
291 })?;
292 self.dcx()
293 .emit_err(errors::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
294 Ok(stmt)
295 }
296
297 fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
299 let lo = self.prev_token.span;
300
301 if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
302 self.dcx().emit_err(errors::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
303 self.bump();
304 }
305
306 let (pat, colon) =
307 self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
308
309 let (err, ty, colon_sp) = if colon {
310 let parser_snapshot_before_type = self.clone();
313 let colon_sp = self.prev_token.span;
314 match self.parse_ty() {
315 Ok(ty) => (None, Some(ty), Some(colon_sp)),
316 Err(mut err) => {
317 err.span_label(
318 colon_sp,
319 format!(
320 "while parsing the type for {}",
321 pat.descr()
322 .map_or_else(|| "the binding".to_string(), |n| format!("`{n}`"))
323 ),
324 );
325 let err = if self.check_noexpect(&token::Eq) {
329 err.emit();
330 None
331 } else {
332 let parser_snapshot_after_type =
334 mem::replace(self, parser_snapshot_before_type);
335 Some((parser_snapshot_after_type, colon_sp, err))
336 };
337 (err, None, Some(colon_sp))
338 }
339 }
340 } else {
341 (None, None, None)
342 };
343 let init = match (self.parse_initializer(err.is_some()), err) {
344 (Ok(init), None) => {
345 init
347 }
348 (Ok(init), Some((_, colon_sp, mut err))) => {
349 err.span_suggestion_short(
353 colon_sp,
354 "use `=` if you meant to assign",
355 " =",
356 Applicability::MachineApplicable,
357 );
358 err.emit();
359 init
363 }
364 (Err(init_err), Some((snapshot, _, ty_err))) => {
365 init_err.cancel();
367 *self = snapshot;
371 return Err(ty_err);
372 }
373 (Err(err), None) => {
374 return Err(err);
378 }
379 };
380 let kind = match init {
381 None => LocalKind::Decl,
382 Some(init) => {
383 if self.eat_keyword(exp!(Else)) {
384 if self.token.is_keyword(kw::If) {
385 let msg = "conditional `else if` is not supported for `let...else`";
388 return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
389 }
390 let els = self.parse_block()?;
391 self.check_let_else_init_bool_expr(&init);
392 self.check_let_else_init_trailing_brace(&init);
393 LocalKind::InitElse(init, els)
394 } else {
395 LocalKind::Init(init)
396 }
397 }
398 };
399 let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
400 Ok(P(ast::Local {
401 ty,
402 pat,
403 kind,
404 id: DUMMY_NODE_ID,
405 span: lo.to(hi),
406 colon_sp,
407 attrs,
408 tokens: None,
409 }))
410 }
411
412 fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
413 if let ast::ExprKind::Binary(op, ..) = init.kind {
414 if op.node.is_lazy() {
415 self.dcx().emit_err(errors::InvalidExpressionInLetElse {
416 span: init.span,
417 operator: op.node.as_str(),
418 sugg: errors::WrapInParentheses::Expression {
419 left: init.span.shrink_to_lo(),
420 right: init.span.shrink_to_hi(),
421 },
422 });
423 }
424 }
425 }
426
427 fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
428 if let Some(trailing) = classify::expr_trailing_brace(init) {
429 let (span, sugg) = match trailing {
430 TrailingBrace::MacCall(mac) => (
431 mac.span(),
432 errors::WrapInParentheses::MacroArgs {
433 left: mac.args.dspan.open,
434 right: mac.args.dspan.close,
435 },
436 ),
437 TrailingBrace::Expr(expr) => (
438 expr.span,
439 errors::WrapInParentheses::Expression {
440 left: expr.span.shrink_to_lo(),
441 right: expr.span.shrink_to_hi(),
442 },
443 ),
444 };
445 self.dcx().emit_err(errors::InvalidCurlyInLetElse {
446 span: span.with_lo(span.hi() - BytePos(1)),
447 sugg,
448 });
449 }
450 }
451
452 fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> {
454 let eq_consumed = match self.token.kind {
455 token::PlusEq
456 | token::MinusEq
457 | token::StarEq
458 | token::SlashEq
459 | token::PercentEq
460 | token::CaretEq
461 | token::AndEq
462 | token::OrEq
463 | token::ShlEq
464 | token::ShrEq => {
465 let extra_op_span = self.psess.source_map().start_point(self.token.span);
470 self.dcx().emit_err(errors::CompoundAssignmentExpressionInLet {
471 span: self.token.span,
472 suggestion: extra_op_span,
473 });
474 self.bump();
475 true
476 }
477 _ => self.eat(exp!(Eq)),
478 };
479
480 Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None })
481 }
482
483 pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
485 let (attrs, block) = self.parse_inner_attrs_and_block(None)?;
486 if let [.., last] = &*attrs {
487 let suggest_to_outer = match &last.kind {
488 ast::AttrKind::Normal(attr) => attr.item.is_valid_for_outer_style(),
489 _ => false,
490 };
491 self.error_on_forbidden_inner_attr(
492 last.span,
493 super::attr::InnerAttrPolicy::Forbidden(Some(
494 InnerAttrForbiddenReason::InCodeBlock,
495 )),
496 suggest_to_outer,
497 );
498 }
499 Ok(block)
500 }
501
502 fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
503 let prev = self.prev_token.span;
504 let sp = self.token.span;
505 let mut e = self.dcx().struct_span_err(sp, msg);
506 let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
507
508 match self.parse_stmt_without_recovery(false, ForceCollect::No, false) {
515 Ok(Some(_))
530 if (!self.token.is_keyword(kw::Else)
531 && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)))
532 || do_not_suggest_help => {}
533 Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
535 Ok(Some(stmt)) => {
536 let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
537 let stmt_span = if stmt_own_line && self.eat(exp!(Semi)) {
538 stmt.span.with_hi(self.prev_token.span.hi())
540 } else {
541 stmt.span
542 };
543 self.suggest_fixes_misparsed_for_loop_head(
544 &mut e,
545 prev.between(sp),
546 stmt_span,
547 &stmt.kind,
548 );
549 }
550 Err(e) => {
551 self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
552 e.cancel();
553 }
554 _ => {}
555 }
556 e.span_label(sp, "expected `{`");
557 e
558 }
559
560 fn suggest_fixes_misparsed_for_loop_head(
561 &self,
562 e: &mut Diag<'_>,
563 between: Span,
564 stmt_span: Span,
565 stmt_kind: &StmtKind,
566 ) {
567 match (&self.token.kind, &stmt_kind) {
568 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
569 if let ExprKind::Call(..) = expr.kind =>
570 {
571 e.span_suggestion_verbose(
573 between,
574 "you might have meant to write a method call",
575 ".".to_string(),
576 Applicability::MaybeIncorrect,
577 );
578 }
579 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
580 if let ExprKind::Field(..) = expr.kind =>
581 {
582 e.span_suggestion_verbose(
584 between,
585 "you might have meant to write a field access",
586 ".".to_string(),
587 Applicability::MaybeIncorrect,
588 );
589 }
590 (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr))
591 if let ExprKind::Struct(expr) = &expr.kind
592 && let None = expr.qself
593 && expr.path.segments.len() == 1 =>
594 {
595 e.span_suggestion_verbose(
599 between,
600 "you might have meant to write a field access",
601 ".".to_string(),
602 Applicability::MaybeIncorrect,
603 );
604 }
605 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
606 if let ExprKind::Lit(lit) = expr.kind
607 && let None = lit.suffix
608 && let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
609 {
610 e.span_suggestion_verbose(
613 between,
614 format!("you might have meant to write a field access"),
615 ".".to_string(),
616 Applicability::MaybeIncorrect,
617 );
618 }
619 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
620 if let ExprKind::Loop(..)
621 | ExprKind::If(..)
622 | ExprKind::While(..)
623 | ExprKind::Match(..)
624 | ExprKind::ForLoop { .. }
625 | ExprKind::TryBlock(..)
626 | ExprKind::Ret(..)
627 | ExprKind::Closure(..)
628 | ExprKind::Struct(..)
629 | ExprKind::Try(..) = expr.kind =>
630 {
631 e.multipart_suggestion(
633 "you might have meant to write this as part of a block",
634 vec![
635 (stmt_span.shrink_to_lo(), "{ ".to_string()),
636 (stmt_span.shrink_to_hi(), " }".to_string()),
637 ],
638 Applicability::MaybeIncorrect,
640 );
641 }
642 (token::OpenDelim(Delimiter::Brace), _) => {}
643 (_, _) => {
644 e.multipart_suggestion(
645 "you might have meant to write this as part of a block",
646 vec![
647 (stmt_span.shrink_to_lo(), "{ ".to_string()),
648 (stmt_span.shrink_to_hi(), " }".to_string()),
649 ],
650 Applicability::MaybeIncorrect,
652 );
653 }
654 }
655 }
656
657 fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
658 let tok = super::token_descr(&self.token);
659 let msg = format!("expected `{{`, found {tok}");
660 Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
661 }
662
663 pub(super) fn parse_inner_attrs_and_block(
668 &mut self,
669 loop_header: Option<Span>,
670 ) -> PResult<'a, (AttrVec, P<Block>)> {
671 self.parse_block_common(self.token.span, BlockCheckMode::Default, true, loop_header)
672 }
673
674 pub(super) fn parse_block_common(
679 &mut self,
680 lo: Span,
681 blk_mode: BlockCheckMode,
682 can_be_struct_literal: bool,
683 loop_header: Option<Span>,
684 ) -> PResult<'a, (AttrVec, P<Block>)> {
685 maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block));
686
687 let maybe_ident = self.prev_token.clone();
688 self.maybe_recover_unexpected_block_label(loop_header);
689 if !self.eat(exp!(OpenBrace)) {
690 return self.error_block_no_opening_brace();
691 }
692
693 let attrs = self.parse_inner_attributes()?;
694 let tail = match self.maybe_suggest_struct_literal(
695 lo,
696 blk_mode,
697 maybe_ident,
698 can_be_struct_literal,
699 ) {
700 Some(tail) => tail?,
701 None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
702 };
703 Ok((attrs, tail))
704 }
705
706 pub(crate) fn parse_block_tail(
709 &mut self,
710 lo: Span,
711 s: BlockCheckMode,
712 recover: AttemptLocalParseRecovery,
713 ) -> PResult<'a, P<Block>> {
714 let mut stmts = ThinVec::new();
715 let mut snapshot = None;
716 while !self.eat(exp!(CloseBrace)) {
717 if self.token == token::Eof {
718 break;
719 }
720 if self.is_vcs_conflict_marker(&TokenKind::Shl, &TokenKind::Lt) {
721 snapshot = Some(self.create_snapshot_for_diagnostic());
725 }
726 let stmt = match self.parse_full_stmt(recover) {
727 Err(mut err) if recover.yes() => {
728 if let Some(ref mut snapshot) = snapshot {
729 snapshot.recover_vcs_conflict_marker();
730 }
731 if self.token == token::Colon {
732 if self.prev_token.is_integer_lit()
736 && self.may_recover()
737 && self.look_ahead(1, |token| token.is_integer_lit())
738 {
739 err.span_suggestion_verbose(
742 self.token.span,
743 "you might have meant a range expression",
744 "..",
745 Applicability::MaybeIncorrect,
746 );
747 } else {
748 self.bump();
751 if self.token.span.lo() == self.prev_token.span.hi() {
752 err.span_suggestion_verbose(
753 self.prev_token.span,
754 "maybe write a path separator here",
755 "::",
756 Applicability::MaybeIncorrect,
757 );
758 }
759 if self.psess.unstable_features.is_nightly_build() {
760 err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
762 }
763 }
764 }
765
766 let guar = err.emit();
767 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
768 Some(self.mk_stmt_err(self.token.span, guar))
769 }
770 Ok(stmt) => stmt,
771 Err(err) => return Err(err),
772 };
773 if let Some(stmt) = stmt {
774 stmts.push(stmt);
775 } else {
776 continue;
778 };
779 }
780 Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
781 }
782
783 fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
784 let Some((ident, _)) = self.token.ident() else {
785 return;
786 };
787 if let Some(c) = ident.name.as_str().chars().next()
788 && c.is_uppercase()
789 {
790 return;
791 }
792 if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
793 return;
794 }
795 if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
796 } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
798 } else if self.prev_token.kind == token::Question {
800 } else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
802 } else {
804 return;
805 }
806 if self.token.span == self.prev_token.span {
807 return;
809 }
810 if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
811 err.span_suggestion_verbose(
812 self.prev_token.span.between(self.token.span),
813 "you might have meant to write a field access",
814 ".".to_string(),
815 Applicability::MaybeIncorrect,
816 );
817 }
818 if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
819 err.span_suggestion_verbose(
820 self.prev_token.span.between(self.token.span),
821 "you might have meant to write a method call",
822 ".".to_string(),
823 Applicability::MaybeIncorrect,
824 );
825 }
826 }
827
828 pub fn parse_full_stmt(
830 &mut self,
831 recover: AttemptLocalParseRecovery,
832 ) -> PResult<'a, Option<Stmt>> {
833 if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
835 this.parse_stmt_without_recovery(false, ForceCollect::No, true)
844 }) {
845 let stmt = stmt.expect("an actual statement");
846 return Ok(Some(stmt));
847 }
848
849 let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?
850 else {
851 return Ok(None);
852 };
853
854 let mut eat_semi = true;
855 let mut add_semi_to_stmt = false;
856
857 match &mut stmt.kind {
858 StmtKind::Expr(expr)
860 if classify::expr_requires_semi_to_be_stmt(expr)
861 && !expr.attrs.is_empty()
862 && ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)]
863 .contains(&self.token.kind) =>
864 {
865 let guar = self.attr_on_non_tail_expr(&expr);
867 let sp = expr.span.to(self.prev_token.span);
869 *expr = self.mk_expr_err(sp, guar);
870 }
871
872 StmtKind::Expr(expr)
874 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
875 {
876 let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]);
879
880 let replace_with_err = 'break_recover: {
883 match expect_result {
884 Ok(Recovered::No) => None,
885 Ok(Recovered::Yes(guar)) => {
886 Some(guar)
888 }
889 Err(e) => {
890 if self.recover_colon_as_semi() {
891 e.delay_as_bug();
893 add_semi_to_stmt = true;
894 eat_semi = false;
895
896 break 'break_recover None;
897 }
898
899 match &expr.kind {
900 ExprKind::Path(None, ast::Path { segments, .. })
901 if let [segment] = segments.as_slice() =>
902 {
903 if self.token == token::Colon
904 && self.look_ahead(1, |token| {
905 token.is_whole_block()
906 || matches!(
907 token.kind,
908 token::Ident(
909 kw::For | kw::Loop | kw::While,
910 token::IdentIsRaw::No
911 ) | token::OpenDelim(Delimiter::Brace)
912 )
913 })
914 {
915 let snapshot = self.create_snapshot_for_diagnostic();
916 let label = Label {
917 ident: Ident::from_str_and_span(
918 &format!("'{}", segment.ident),
919 segment.ident.span,
920 ),
921 };
922 match self.parse_expr_labeled(label, false) {
923 Ok(labeled_expr) => {
924 e.cancel();
925 self.dcx().emit_err(MalformedLoopLabel {
926 span: label.ident.span,
927 suggestion: label.ident.span.shrink_to_lo(),
928 });
929 *expr = labeled_expr;
930 break 'break_recover None;
931 }
932 Err(err) => {
933 err.cancel();
934 self.restore_snapshot(snapshot);
935 }
936 }
937 }
938 }
939 _ => {}
940 }
941
942 let res =
943 self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
944
945 Some(if recover.no() {
946 res?
947 } else {
948 res.unwrap_or_else(|mut e| {
949 self.recover_missing_dot(&mut e);
950 let guar = e.emit();
951 self.recover_stmt();
952 guar
953 })
954 })
955 }
956 }
957 };
958
959 if let Some(guar) = replace_with_err {
960 let sp = expr.span.to(self.prev_token.span);
962 *expr = self.mk_expr_err(sp, guar);
963 }
964 }
965 StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
966 StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
967 match &mut local.kind {
969 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
970 self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
971 |mut e| {
972 self.recover_missing_dot(&mut e);
973 e
974 },
975 )?;
976 self.expect_semi()?;
978 }
979 LocalKind::Decl => {
980 if let Some(colon_sp) = local.colon_sp {
981 e.span_label(
982 colon_sp,
983 format!(
984 "while parsing the type for {}",
985 local.pat.descr().map_or_else(
986 || "the binding".to_string(),
987 |n| format!("`{n}`")
988 )
989 ),
990 );
991 let suggest_eq = if self.token == token::Dot
992 && let _ = self.bump()
993 && let mut snapshot = self.create_snapshot_for_diagnostic()
994 && let Ok(_) = snapshot
995 .parse_dot_suffix_expr(
996 colon_sp,
997 self.mk_expr_err(
998 colon_sp,
999 self.dcx()
1000 .delayed_bug("error during `:` -> `=` recovery"),
1001 ),
1002 )
1003 .map_err(Diag::cancel)
1004 {
1005 true
1006 } else if let Some(op) = self.check_assoc_op()
1007 && op.node.can_continue_expr_unambiguously()
1008 {
1009 true
1010 } else {
1011 false
1012 };
1013 if suggest_eq {
1014 e.span_suggestion_short(
1015 colon_sp,
1016 "use `=` if you meant to assign",
1017 "=",
1018 Applicability::MaybeIncorrect,
1019 );
1020 }
1021 }
1022 return Err(e);
1023 }
1024 }
1025 eat_semi = false;
1026 }
1027 StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
1028 eat_semi = false
1029 }
1030 }
1031
1032 if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) {
1033 stmt = stmt.add_trailing_semicolon();
1034 }
1035
1036 stmt.span = stmt.span.to(self.prev_token.span);
1037 Ok(Some(stmt))
1038 }
1039
1040 pub(super) fn mk_block(
1041 &self,
1042 stmts: ThinVec<Stmt>,
1043 rules: BlockCheckMode,
1044 span: Span,
1045 ) -> P<Block> {
1046 P(Block {
1047 stmts,
1048 id: DUMMY_NODE_ID,
1049 rules,
1050 span,
1051 tokens: None,
1052 could_be_bare_literal: false,
1053 })
1054 }
1055
1056 pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1057 Stmt { id: DUMMY_NODE_ID, kind, span }
1058 }
1059
1060 pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1061 self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1062 }
1063
1064 pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> {
1065 self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
1066 }
1067}