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, loop_header)
672 }
673
674 pub(super) fn parse_block_common(
679 &mut self,
680 lo: Span,
681 blk_mode: BlockCheckMode,
682 loop_header: Option<Span>,
683 ) -> PResult<'a, (AttrVec, P<Block>)> {
684 maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block));
685
686 let maybe_ident = self.prev_token.clone();
687 self.maybe_recover_unexpected_block_label(loop_header);
688 if !self.eat(exp!(OpenBrace)) {
689 return self.error_block_no_opening_brace();
690 }
691
692 let attrs = self.parse_inner_attributes()?;
693 let tail = match self.maybe_suggest_struct_literal(lo, blk_mode, maybe_ident) {
694 Some(tail) => tail?,
695 None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
696 };
697 Ok((attrs, tail))
698 }
699
700 pub(crate) fn parse_block_tail(
703 &mut self,
704 lo: Span,
705 s: BlockCheckMode,
706 recover: AttemptLocalParseRecovery,
707 ) -> PResult<'a, P<Block>> {
708 let mut stmts = ThinVec::new();
709 let mut snapshot = None;
710 while !self.eat(exp!(CloseBrace)) {
711 if self.token == token::Eof {
712 break;
713 }
714 if self.is_vcs_conflict_marker(&TokenKind::Shl, &TokenKind::Lt) {
715 snapshot = Some(self.create_snapshot_for_diagnostic());
719 }
720 let stmt = match self.parse_full_stmt(recover) {
721 Err(mut err) if recover.yes() => {
722 if let Some(ref mut snapshot) = snapshot {
723 snapshot.recover_vcs_conflict_marker();
724 }
725 if self.token == token::Colon {
726 if self.prev_token.is_integer_lit()
730 && self.may_recover()
731 && self.look_ahead(1, |token| token.is_integer_lit())
732 {
733 err.span_suggestion_verbose(
736 self.token.span,
737 "you might have meant a range expression",
738 "..",
739 Applicability::MaybeIncorrect,
740 );
741 } else {
742 self.bump();
745 if self.token.span.lo() == self.prev_token.span.hi() {
746 err.span_suggestion_verbose(
747 self.prev_token.span,
748 "maybe write a path separator here",
749 "::",
750 Applicability::MaybeIncorrect,
751 );
752 }
753 if self.psess.unstable_features.is_nightly_build() {
754 err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
756 }
757 }
758 }
759
760 let guar = err.emit();
761 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
762 Some(self.mk_stmt_err(self.token.span, guar))
763 }
764 Ok(stmt) => stmt,
765 Err(err) => return Err(err),
766 };
767 if let Some(stmt) = stmt {
768 stmts.push(stmt);
769 } else {
770 continue;
772 };
773 }
774 Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
775 }
776
777 fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
778 let Some((ident, _)) = self.token.ident() else {
779 return;
780 };
781 if let Some(c) = ident.name.as_str().chars().next()
782 && c.is_uppercase()
783 {
784 return;
785 }
786 if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
787 return;
788 }
789 if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
790 } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
792 } else if self.prev_token.kind == token::Question {
794 } else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
796 } else {
798 return;
799 }
800 if self.token.span == self.prev_token.span {
801 return;
803 }
804 if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
805 err.span_suggestion_verbose(
806 self.prev_token.span.between(self.token.span),
807 "you might have meant to write a field access",
808 ".".to_string(),
809 Applicability::MaybeIncorrect,
810 );
811 }
812 if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
813 err.span_suggestion_verbose(
814 self.prev_token.span.between(self.token.span),
815 "you might have meant to write a method call",
816 ".".to_string(),
817 Applicability::MaybeIncorrect,
818 );
819 }
820 }
821
822 pub fn parse_full_stmt(
824 &mut self,
825 recover: AttemptLocalParseRecovery,
826 ) -> PResult<'a, Option<Stmt>> {
827 if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
829 this.parse_stmt_without_recovery(false, ForceCollect::No, true)
838 }) {
839 let stmt = stmt.expect("an actual statement");
840 return Ok(Some(stmt));
841 }
842
843 let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?
844 else {
845 return Ok(None);
846 };
847
848 let mut eat_semi = true;
849 let mut add_semi_to_stmt = false;
850
851 match &mut stmt.kind {
852 StmtKind::Expr(expr)
854 if classify::expr_requires_semi_to_be_stmt(expr)
855 && !expr.attrs.is_empty()
856 && ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)]
857 .contains(&self.token.kind) =>
858 {
859 let guar = self.attr_on_non_tail_expr(&expr);
861 let sp = expr.span.to(self.prev_token.span);
863 *expr = self.mk_expr_err(sp, guar);
864 }
865
866 StmtKind::Expr(expr)
868 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
869 {
870 let expect_result = self.expect_one_of(&[], &[exp!(Semi), exp!(CloseBrace)]);
873
874 let replace_with_err = 'break_recover: {
877 match expect_result {
878 Ok(Recovered::No) => None,
879 Ok(Recovered::Yes(guar)) => {
880 Some(guar)
882 }
883 Err(e) => {
884 if self.recover_colon_as_semi() {
885 e.delay_as_bug();
887 add_semi_to_stmt = true;
888 eat_semi = false;
889
890 break 'break_recover None;
891 }
892
893 match &expr.kind {
894 ExprKind::Path(None, ast::Path { segments, .. })
895 if let [segment] = segments.as_slice() =>
896 {
897 if self.token == token::Colon
898 && self.look_ahead(1, |token| {
899 token.is_whole_block()
900 || matches!(
901 token.kind,
902 token::Ident(
903 kw::For | kw::Loop | kw::While,
904 token::IdentIsRaw::No
905 ) | token::OpenDelim(Delimiter::Brace)
906 )
907 })
908 {
909 let snapshot = self.create_snapshot_for_diagnostic();
910 let label = Label {
911 ident: Ident::from_str_and_span(
912 &format!("'{}", segment.ident),
913 segment.ident.span,
914 ),
915 };
916 match self.parse_expr_labeled(label, false) {
917 Ok(labeled_expr) => {
918 e.cancel();
919 self.dcx().emit_err(MalformedLoopLabel {
920 span: label.ident.span,
921 suggestion: label.ident.span.shrink_to_lo(),
922 });
923 *expr = labeled_expr;
924 break 'break_recover None;
925 }
926 Err(err) => {
927 err.cancel();
928 self.restore_snapshot(snapshot);
929 }
930 }
931 }
932 }
933 _ => {}
934 }
935
936 let res =
937 self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
938
939 Some(if recover.no() {
940 res?
941 } else {
942 res.unwrap_or_else(|mut e| {
943 self.recover_missing_dot(&mut e);
944 let guar = e.emit();
945 self.recover_stmt();
946 guar
947 })
948 })
949 }
950 }
951 };
952
953 if let Some(guar) = replace_with_err {
954 let sp = expr.span.to(self.prev_token.span);
956 *expr = self.mk_expr_err(sp, guar);
957 }
958 }
959 StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
960 StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
961 match &mut local.kind {
963 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
964 self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
965 |mut e| {
966 self.recover_missing_dot(&mut e);
967 e
968 },
969 )?;
970 self.expect_semi()?;
972 }
973 LocalKind::Decl => {
974 if let Some(colon_sp) = local.colon_sp {
975 e.span_label(
976 colon_sp,
977 format!(
978 "while parsing the type for {}",
979 local.pat.descr().map_or_else(
980 || "the binding".to_string(),
981 |n| format!("`{n}`")
982 )
983 ),
984 );
985 let suggest_eq = if self.token == token::Dot
986 && let _ = self.bump()
987 && let mut snapshot = self.create_snapshot_for_diagnostic()
988 && let Ok(_) = snapshot
989 .parse_dot_suffix_expr(
990 colon_sp,
991 self.mk_expr_err(
992 colon_sp,
993 self.dcx()
994 .delayed_bug("error during `:` -> `=` recovery"),
995 ),
996 )
997 .map_err(Diag::cancel)
998 {
999 true
1000 } else if let Some(op) = self.check_assoc_op()
1001 && op.node.can_continue_expr_unambiguously()
1002 {
1003 true
1004 } else {
1005 false
1006 };
1007 if suggest_eq {
1008 e.span_suggestion_short(
1009 colon_sp,
1010 "use `=` if you meant to assign",
1011 "=",
1012 Applicability::MaybeIncorrect,
1013 );
1014 }
1015 }
1016 return Err(e);
1017 }
1018 }
1019 eat_semi = false;
1020 }
1021 StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
1022 eat_semi = false
1023 }
1024 }
1025
1026 if add_semi_to_stmt || (eat_semi && self.eat(exp!(Semi))) {
1027 stmt = stmt.add_trailing_semicolon();
1028 }
1029
1030 stmt.span = stmt.span.to(self.prev_token.span);
1031 Ok(Some(stmt))
1032 }
1033
1034 pub(super) fn mk_block(
1035 &self,
1036 stmts: ThinVec<Stmt>,
1037 rules: BlockCheckMode,
1038 span: Span,
1039 ) -> P<Block> {
1040 P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None })
1041 }
1042
1043 pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1044 Stmt { id: DUMMY_NODE_ID, kind, span }
1045 }
1046
1047 pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1048 self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1049 }
1050
1051 pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> {
1052 self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
1053 }
1054}