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, 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(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
37 Ok(self.parse_stmt_without_recovery(false, force_collect).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 ) -> PResult<'a, Option<Stmt>> {
52 let pre_attr_pos = self.collect_pos();
53 let attrs = self.parse_outer_attributes()?;
54 let lo = self.token.span;
55
56 maybe_whole!(self, NtStmt, |stmt| {
57 stmt.visit_attrs(|stmt_attrs| {
58 attrs.prepend_to_nt_inner(stmt_attrs);
59 });
60 Some(stmt.into_inner())
61 });
62
63 if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
64 self.bump();
65 let mut_let_span = lo.to(self.token.span);
66 self.dcx().emit_err(errors::InvalidVariableDeclaration {
67 span: mut_let_span,
68 sub: errors::InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
69 });
70 }
71
72 let stmt = if self.token.is_keyword(kw::Let) {
73 self.collect_tokens(None, attrs, force_collect, |this, attrs| {
74 this.expect_keyword(exp!(Let))?;
75 let local = this.parse_local(attrs)?;
76 let trailing = Trailing::from(capture_semi && this.token == token::Semi);
77 Ok((
78 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
79 trailing,
80 UsePreAttrPos::No,
81 ))
82 })?
83 } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
84 self.recover_stmt_local_after_let(
85 lo,
86 attrs,
87 errors::InvalidVariableDeclarationSub::MissingLet,
88 force_collect,
89 )?
90 } else if self.is_kw_followed_by_ident(kw::Auto) && self.may_recover() {
91 self.bump(); self.recover_stmt_local_after_let(
93 lo,
94 attrs,
95 errors::InvalidVariableDeclarationSub::UseLetNotAuto,
96 force_collect,
97 )?
98 } else if self.is_kw_followed_by_ident(sym::var) && self.may_recover() {
99 self.bump(); self.recover_stmt_local_after_let(
101 lo,
102 attrs,
103 errors::InvalidVariableDeclarationSub::UseLetNotVar,
104 force_collect,
105 )?
106 } else if self.check_path()
107 && !self.token.is_qpath_start()
108 && !self.is_path_start_item()
109 && !self.is_builtin()
110 {
111 let stmt = self.collect_tokens(
121 Some(pre_attr_pos),
122 AttrWrapper::empty(),
123 force_collect,
124 |this, _empty_attrs| {
125 Ok((this.parse_stmt_path_start(lo, attrs)?, Trailing::No, UsePreAttrPos::Yes))
126 },
127 );
128 match stmt {
129 Ok(stmt) => stmt,
130 Err(mut err) => {
131 self.suggest_add_missing_let_for_stmt(&mut err);
132 return Err(err);
133 }
134 }
135 } else if let Some(item) = self.parse_item_common(
136 attrs.clone(), false,
138 true,
139 FnParseMode { req_name: |_| true, req_body: true },
140 force_collect,
141 )? {
142 self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
143 } else if self.eat(exp!(Semi)) {
144 self.error_outer_attrs(attrs);
146 self.mk_stmt(lo, StmtKind::Empty)
147 } else if self.token != token::CloseDelim(Delimiter::Brace) {
148 let e = self.collect_tokens(
151 Some(pre_attr_pos),
152 AttrWrapper::empty(),
153 force_collect,
154 |this, _empty_attrs| {
155 let (expr, _) = this.parse_expr_res(Restrictions::STMT_EXPR, attrs)?;
156 Ok((expr, Trailing::No, UsePreAttrPos::Yes))
157 },
158 )?;
159 if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(exp!(Else)) {
160 let bl = self.parse_block()?;
161 self.dcx().emit_err(errors::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
164 }
165 self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
166 } else {
167 self.error_outer_attrs(attrs);
168 return Ok(None);
169 };
170
171 self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt);
172 Ok(Some(stmt))
173 }
174
175 fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
176 let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
177 let path = this.parse_path(PathStyle::Expr)?;
178
179 if this.eat(exp!(Not)) {
180 let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
181 return Ok((
182 stmt_mac,
183 Trailing::from(this.token == token::Semi),
184 UsePreAttrPos::No,
185 ));
186 }
187
188 let expr = if this.eat(exp!(OpenBrace)) {
189 this.parse_expr_struct(None, path, true)?
190 } else {
191 let hi = this.prev_token.span;
192 this.mk_expr(lo.to(hi), ExprKind::Path(None, path))
193 };
194
195 let expr = this.with_res(Restrictions::STMT_EXPR, |this| {
196 this.parse_expr_dot_or_call_with(attrs, expr, lo)
197 })?;
198 Ok((
200 this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)),
201 Trailing::No,
202 UsePreAttrPos::No,
203 ))
204 })?;
205
206 if let StmtKind::Expr(expr) = stmt.kind {
207 let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
210 this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr)
211 })?;
212 Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
213 } else {
214 Ok(stmt)
215 }
216 }
217
218 fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
221 let args = self.parse_delim_args()?;
222 let hi = self.prev_token.span;
223
224 let style = match args.delim {
225 Delimiter::Brace => MacStmtStyle::Braces,
226 _ => MacStmtStyle::NoBraces,
227 };
228
229 let mac = P(MacCall { path, args });
230
231 let kind = if (style == MacStmtStyle::Braces
232 && self.token != token::Dot
233 && self.token != token::Question)
234 || self.token == token::Semi
235 || self.token == token::Eof
236 {
237 StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
238 } else {
239 let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
241 let e = self.maybe_recover_from_bad_qpath(e)?;
242 let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
243 let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?;
244 StmtKind::Expr(e)
245 };
246 Ok(self.mk_stmt(lo.to(hi), kind))
247 }
248
249 fn error_outer_attrs(&self, attrs: AttrWrapper) {
252 if !attrs.is_empty()
253 && let attrs @ [.., last] = &*attrs.take_for_recovery(self.psess)
254 {
255 if last.is_doc_comment() {
256 self.dcx().emit_err(errors::DocCommentDoesNotDocumentAnything {
257 span: last.span,
258 missing_comma: None,
259 });
260 } else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
261 self.dcx().emit_err(errors::ExpectedStatementAfterOuterAttr { span: last.span });
262 }
263 }
264 }
265
266 fn recover_stmt_local_after_let(
267 &mut self,
268 lo: Span,
269 attrs: AttrWrapper,
270 subdiagnostic: fn(Span) -> errors::InvalidVariableDeclarationSub,
271 force_collect: ForceCollect,
272 ) -> PResult<'a, Stmt> {
273 let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
274 let local = this.parse_local(attrs)?;
275 Ok((
277 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
278 Trailing::No,
279 UsePreAttrPos::No,
280 ))
281 })?;
282 self.dcx()
283 .emit_err(errors::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
284 Ok(stmt)
285 }
286
287 fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
289 let lo = self.prev_token.span;
290
291 if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
292 self.dcx().emit_err(errors::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
293 self.bump();
294 }
295
296 let (pat, colon) =
297 self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
298
299 let (err, ty, colon_sp) = if colon {
300 let parser_snapshot_before_type = self.clone();
303 let colon_sp = self.prev_token.span;
304 match self.parse_ty() {
305 Ok(ty) => (None, Some(ty), Some(colon_sp)),
306 Err(mut err) => {
307 err.span_label(
308 colon_sp,
309 format!(
310 "while parsing the type for {}",
311 pat.descr()
312 .map_or_else(|| "the binding".to_string(), |n| format!("`{n}`"))
313 ),
314 );
315 let err = if self.check_noexpect(&token::Eq) {
319 err.emit();
320 None
321 } else {
322 let parser_snapshot_after_type =
324 mem::replace(self, parser_snapshot_before_type);
325 Some((parser_snapshot_after_type, colon_sp, err))
326 };
327 (err, None, Some(colon_sp))
328 }
329 }
330 } else {
331 (None, None, None)
332 };
333 let init = match (self.parse_initializer(err.is_some()), err) {
334 (Ok(init), None) => {
335 init
337 }
338 (Ok(init), Some((_, colon_sp, mut err))) => {
339 err.span_suggestion_short(
343 colon_sp,
344 "use `=` if you meant to assign",
345 " =",
346 Applicability::MachineApplicable,
347 );
348 err.emit();
349 init
353 }
354 (Err(init_err), Some((snapshot, _, ty_err))) => {
355 init_err.cancel();
357 *self = snapshot;
361 return Err(ty_err);
362 }
363 (Err(err), None) => {
364 return Err(err);
368 }
369 };
370 let kind = match init {
371 None => LocalKind::Decl,
372 Some(init) => {
373 if self.eat_keyword(exp!(Else)) {
374 if self.token.is_keyword(kw::If) {
375 let msg = "conditional `else if` is not supported for `let...else`";
378 return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
379 }
380 let els = self.parse_block()?;
381 self.check_let_else_init_bool_expr(&init);
382 self.check_let_else_init_trailing_brace(&init);
383 LocalKind::InitElse(init, els)
384 } else {
385 LocalKind::Init(init)
386 }
387 }
388 };
389 let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
390 Ok(P(ast::Local {
391 ty,
392 pat,
393 kind,
394 id: DUMMY_NODE_ID,
395 span: lo.to(hi),
396 colon_sp,
397 attrs,
398 tokens: None,
399 }))
400 }
401
402 fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
403 if let ast::ExprKind::Binary(op, ..) = init.kind {
404 if op.node.is_lazy() {
405 self.dcx().emit_err(errors::InvalidExpressionInLetElse {
406 span: init.span,
407 operator: op.node.as_str(),
408 sugg: errors::WrapInParentheses::Expression {
409 left: init.span.shrink_to_lo(),
410 right: init.span.shrink_to_hi(),
411 },
412 });
413 }
414 }
415 }
416
417 fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
418 if let Some(trailing) = classify::expr_trailing_brace(init) {
419 let (span, sugg) = match trailing {
420 TrailingBrace::MacCall(mac) => (
421 mac.span(),
422 errors::WrapInParentheses::MacroArgs {
423 left: mac.args.dspan.open,
424 right: mac.args.dspan.close,
425 },
426 ),
427 TrailingBrace::Expr(expr) => (
428 expr.span,
429 errors::WrapInParentheses::Expression {
430 left: expr.span.shrink_to_lo(),
431 right: expr.span.shrink_to_hi(),
432 },
433 ),
434 };
435 self.dcx().emit_err(errors::InvalidCurlyInLetElse {
436 span: span.with_lo(span.hi() - BytePos(1)),
437 sugg,
438 });
439 }
440 }
441
442 fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> {
444 let eq_consumed = match self.token.kind {
445 token::BinOpEq(..) => {
446 let extra_op_span = self.psess.source_map().start_point(self.token.span);
451 self.dcx().emit_err(errors::CompoundAssignmentExpressionInLet {
452 span: self.token.span,
453 suggestion: extra_op_span,
454 });
455 self.bump();
456 true
457 }
458 _ => self.eat(exp!(Eq)),
459 };
460
461 Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None })
462 }
463
464 pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
466 let (attrs, block) = self.parse_inner_attrs_and_block()?;
467 if let [.., last] = &*attrs {
468 let suggest_to_outer = match &last.kind {
469 ast::AttrKind::Normal(attr) => attr.item.is_valid_for_outer_style(),
470 _ => false,
471 };
472 self.error_on_forbidden_inner_attr(
473 last.span,
474 super::attr::InnerAttrPolicy::Forbidden(Some(
475 InnerAttrForbiddenReason::InCodeBlock,
476 )),
477 suggest_to_outer,
478 );
479 }
480 Ok(block)
481 }
482
483 fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
484 let prev = self.prev_token.span;
485 let sp = self.token.span;
486 let mut e = self.dcx().struct_span_err(sp, msg);
487 let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
488
489 match self.parse_stmt_without_recovery(false, ForceCollect::No) {
496 Ok(Some(_))
511 if (!self.token.is_keyword(kw::Else)
512 && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)))
513 || do_not_suggest_help => {}
514 Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
516 Ok(Some(stmt)) => {
517 let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
518 let stmt_span = if stmt_own_line && self.eat(exp!(Semi)) {
519 stmt.span.with_hi(self.prev_token.span.hi())
521 } else {
522 stmt.span
523 };
524 self.suggest_fixes_misparsed_for_loop_head(
525 &mut e,
526 prev.between(sp),
527 stmt_span,
528 &stmt.kind,
529 );
530 }
531 Err(e) => {
532 self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
533 e.cancel();
534 }
535 _ => {}
536 }
537 e.span_label(sp, "expected `{`");
538 e
539 }
540
541 fn suggest_fixes_misparsed_for_loop_head(
542 &self,
543 e: &mut Diag<'_>,
544 between: Span,
545 stmt_span: Span,
546 stmt_kind: &StmtKind,
547 ) {
548 match (&self.token.kind, &stmt_kind) {
549 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
550 if let ExprKind::Call(..) = expr.kind =>
551 {
552 e.span_suggestion_verbose(
554 between,
555 "you might have meant to write a method call",
556 ".".to_string(),
557 Applicability::MaybeIncorrect,
558 );
559 }
560 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
561 if let ExprKind::Field(..) = expr.kind =>
562 {
563 e.span_suggestion_verbose(
565 between,
566 "you might have meant to write a field access",
567 ".".to_string(),
568 Applicability::MaybeIncorrect,
569 );
570 }
571 (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr))
572 if let ExprKind::Struct(expr) = &expr.kind
573 && let None = expr.qself
574 && expr.path.segments.len() == 1 =>
575 {
576 e.span_suggestion_verbose(
580 between,
581 "you might have meant to write a field access",
582 ".".to_string(),
583 Applicability::MaybeIncorrect,
584 );
585 }
586 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
587 if let ExprKind::Lit(lit) = expr.kind
588 && let None = lit.suffix
589 && let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
590 {
591 e.span_suggestion_verbose(
594 between,
595 format!("you might have meant to write a field access"),
596 ".".to_string(),
597 Applicability::MaybeIncorrect,
598 );
599 }
600 (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
601 if let ExprKind::Loop(..)
602 | ExprKind::If(..)
603 | ExprKind::While(..)
604 | ExprKind::Match(..)
605 | ExprKind::ForLoop { .. }
606 | ExprKind::TryBlock(..)
607 | ExprKind::Ret(..)
608 | ExprKind::Closure(..)
609 | ExprKind::Struct(..)
610 | ExprKind::Try(..) = expr.kind =>
611 {
612 e.multipart_suggestion(
614 "you might have meant to write this as part of a block",
615 vec![
616 (stmt_span.shrink_to_lo(), "{ ".to_string()),
617 (stmt_span.shrink_to_hi(), " }".to_string()),
618 ],
619 Applicability::MaybeIncorrect,
621 );
622 }
623 (token::OpenDelim(Delimiter::Brace), _) => {}
624 (_, _) => {
625 e.multipart_suggestion(
626 "you might have meant to write this as part of a block",
627 vec![
628 (stmt_span.shrink_to_lo(), "{ ".to_string()),
629 (stmt_span.shrink_to_hi(), " }".to_string()),
630 ],
631 Applicability::MaybeIncorrect,
633 );
634 }
635 }
636 }
637
638 fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
639 let tok = super::token_descr(&self.token);
640 let msg = format!("expected `{{`, found {tok}");
641 Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
642 }
643
644 pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
646 self.parse_block_common(self.token.span, BlockCheckMode::Default, true)
647 }
648
649 pub(super) fn parse_block_common(
651 &mut self,
652 lo: Span,
653 blk_mode: BlockCheckMode,
654 can_be_struct_literal: bool,
655 ) -> PResult<'a, (AttrVec, P<Block>)> {
656 maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block));
657
658 let maybe_ident = self.prev_token.clone();
659 self.maybe_recover_unexpected_block_label();
660 if !self.eat(exp!(OpenBrace)) {
661 return self.error_block_no_opening_brace();
662 }
663
664 let attrs = self.parse_inner_attributes()?;
665 let tail = match self.maybe_suggest_struct_literal(
666 lo,
667 blk_mode,
668 maybe_ident,
669 can_be_struct_literal,
670 ) {
671 Some(tail) => tail?,
672 None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
673 };
674 Ok((attrs, tail))
675 }
676
677 pub(crate) fn parse_block_tail(
680 &mut self,
681 lo: Span,
682 s: BlockCheckMode,
683 recover: AttemptLocalParseRecovery,
684 ) -> PResult<'a, P<Block>> {
685 let mut stmts = ThinVec::new();
686 let mut snapshot = None;
687 while !self.eat(exp!(CloseBrace)) {
688 if self.token == token::Eof {
689 break;
690 }
691 if self.is_vcs_conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
692 snapshot = Some(self.create_snapshot_for_diagnostic());
696 }
697 let stmt = match self.parse_full_stmt(recover) {
698 Err(mut err) if recover.yes() => {
699 if let Some(ref mut snapshot) = snapshot {
700 snapshot.recover_vcs_conflict_marker();
701 }
702 if self.token == token::Colon {
703 if self.prev_token.is_integer_lit()
707 && self.may_recover()
708 && self.look_ahead(1, |token| token.is_integer_lit())
709 {
710 err.span_suggestion_verbose(
713 self.token.span,
714 "you might have meant a range expression",
715 "..",
716 Applicability::MaybeIncorrect,
717 );
718 } else {
719 self.bump();
722 if self.token.span.lo() == self.prev_token.span.hi() {
723 err.span_suggestion_verbose(
724 self.prev_token.span,
725 "maybe write a path separator here",
726 "::",
727 Applicability::MaybeIncorrect,
728 );
729 }
730 if self.psess.unstable_features.is_nightly_build() {
731 err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
733 }
734 }
735 }
736
737 let guar = err.emit();
738 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
739 Some(self.mk_stmt_err(self.token.span, guar))
740 }
741 Ok(stmt) => stmt,
742 Err(err) => return Err(err),
743 };
744 if let Some(stmt) = stmt {
745 stmts.push(stmt);
746 } else {
747 continue;
749 };
750 }
751 Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
752 }
753
754 fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
755 let Some((ident, _)) = self.token.ident() else {
756 return;
757 };
758 if let Some(c) = ident.name.as_str().chars().next()
759 && c.is_uppercase()
760 {
761 return;
762 }
763 if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
764 return;
765 }
766 if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
767 } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
769 } else if self.prev_token.kind == token::Question {
771 } else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
773 } else {
775 return;
776 }
777 if self.token.span == self.prev_token.span {
778 return;
780 }
781 if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
782 err.span_suggestion_verbose(
783 self.prev_token.span.between(self.token.span),
784 "you might have meant to write a field access",
785 ".".to_string(),
786 Applicability::MaybeIncorrect,
787 );
788 }
789 if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
790 err.span_suggestion_verbose(
791 self.prev_token.span.between(self.token.span),
792 "you might have meant to write a method call",
793 ".".to_string(),
794 Applicability::MaybeIncorrect,
795 );
796 }
797 }
798
799 pub fn parse_full_stmt(
801 &mut self,
802 recover: AttemptLocalParseRecovery,
803 ) -> PResult<'a, Option<Stmt>> {
804 maybe_whole!(self, NtStmt, |stmt| Some(stmt.into_inner()));
806
807 let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else {
808 return Ok(None);
809 };
810
811 let mut eat_semi = true;
812 let mut add_semi_to_stmt = false;
813
814 match &mut stmt.kind {
815 StmtKind::Expr(expr)
817 if classify::expr_requires_semi_to_be_stmt(expr)
818 && !expr.attrs.is_empty()
819 && ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)]
820 .contains(&self.token.kind) =>
821 {
822 let guar = self.attr_on_non_tail_expr(&expr);
824 let sp = expr.span.to(self.prev_token.span);
826 *expr = self.mk_expr_err(sp, guar);
827 }
828
829 StmtKind::Expr(expr)
831 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
832 {
833 let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]);
836
837 let replace_with_err = 'break_recover: {
840 match expect_result {
841 Ok(Recovered::No) => None,
842 Ok(Recovered::Yes(guar)) => {
843 Some(guar)
845 }
846 Err(e) => {
847 if self.recover_colon_as_semi() {
848 e.delay_as_bug();
850 add_semi_to_stmt = true;
851 eat_semi = false;
852
853 break 'break_recover None;
854 }
855
856 match &expr.kind {
857 ExprKind::Path(None, ast::Path { segments, .. })
858 if let [segment] = segments.as_slice() =>
859 {
860 if self.token == token::Colon
861 && self.look_ahead(1, |token| {
862 token.is_whole_block()
863 || matches!(
864 token.kind,
865 token::Ident(
866 kw::For | kw::Loop | kw::While,
867 token::IdentIsRaw::No
868 ) | token::OpenDelim(Delimiter::Brace)
869 )
870 })
871 {
872 let snapshot = self.create_snapshot_for_diagnostic();
873 let label = Label {
874 ident: Ident::from_str_and_span(
875 &format!("'{}", segment.ident),
876 segment.ident.span,
877 ),
878 };
879 match self.parse_expr_labeled(label, false) {
880 Ok(labeled_expr) => {
881 e.cancel();
882 self.dcx().emit_err(MalformedLoopLabel {
883 span: label.ident.span,
884 suggestion: label.ident.span.shrink_to_lo(),
885 });
886 *expr = labeled_expr;
887 break 'break_recover None;
888 }
889 Err(err) => {
890 err.cancel();
891 self.restore_snapshot(snapshot);
892 }
893 }
894 }
895 }
896 _ => {}
897 }
898
899 let res =
900 self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
901
902 Some(if recover.no() {
903 res?
904 } else {
905 res.unwrap_or_else(|mut e| {
906 self.recover_missing_dot(&mut e);
907 let guar = e.emit();
908 self.recover_stmt();
909 guar
910 })
911 })
912 }
913 }
914 };
915
916 if let Some(guar) = replace_with_err {
917 let sp = expr.span.to(self.prev_token.span);
919 *expr = self.mk_expr_err(sp, guar);
920 }
921 }
922 StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
923 StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
924 match &mut local.kind {
926 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
927 self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
928 |mut e| {
929 self.recover_missing_dot(&mut e);
930 e
931 },
932 )?;
933 self.expect_semi()?;
935 }
936 LocalKind::Decl => {
937 if let Some(colon_sp) = local.colon_sp {
938 e.span_label(
939 colon_sp,
940 format!(
941 "while parsing the type for {}",
942 local.pat.descr().map_or_else(
943 || "the binding".to_string(),
944 |n| format!("`{n}`")
945 )
946 ),
947 );
948 let suggest_eq = if self.token == token::Dot
949 && let _ = self.bump()
950 && let mut snapshot = self.create_snapshot_for_diagnostic()
951 && let Ok(_) = snapshot
952 .parse_dot_suffix_expr(
953 colon_sp,
954 self.mk_expr_err(
955 colon_sp,
956 self.dcx()
957 .delayed_bug("error during `:` -> `=` recovery"),
958 ),
959 )
960 .map_err(Diag::cancel)
961 {
962 true
963 } else if let Some(op) = self.check_assoc_op()
964 && op.node.can_continue_expr_unambiguously()
965 {
966 true
967 } else {
968 false
969 };
970 if suggest_eq {
971 e.span_suggestion_short(
972 colon_sp,
973 "use `=` if you meant to assign",
974 "=",
975 Applicability::MaybeIncorrect,
976 );
977 }
978 }
979 return Err(e);
980 }
981 }
982 eat_semi = false;
983 }
984 StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
985 eat_semi = false
986 }
987 }
988
989 if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) {
990 stmt = stmt.add_trailing_semicolon();
991 }
992
993 stmt.span = stmt.span.to(self.prev_token.span);
994 Ok(Some(stmt))
995 }
996
997 pub(super) fn mk_block(
998 &self,
999 stmts: ThinVec<Stmt>,
1000 rules: BlockCheckMode,
1001 span: Span,
1002 ) -> P<Block> {
1003 P(Block {
1004 stmts,
1005 id: DUMMY_NODE_ID,
1006 rules,
1007 span,
1008 tokens: None,
1009 could_be_bare_literal: false,
1010 })
1011 }
1012
1013 pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1014 Stmt { id: DUMMY_NODE_ID, kind, span }
1015 }
1016
1017 pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1018 self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1019 }
1020
1021 pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> {
1022 self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
1023 }
1024}