1use std::borrow::Cow;
2use std::mem;
3use std::ops::Bound;
4
5use ast::Label;
6use rustc_ast as ast;
7use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind};
8use rustc_ast::tokenstream::TokenTree;
9use rustc_ast::util::classify::{self, TrailingBrace};
10use rustc_ast::visit::{Visitor, walk_expr};
11use rustc_ast::{
12 AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, HasAttrs, Local,
13 LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
14};
15use rustc_errors::{Applicability, Diag, PResult};
16use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
17use thin_vec::{ThinVec, thin_vec};
18
19use super::attr::InnerAttrForbiddenReason;
20use super::diagnostics::AttemptLocalParseRecovery;
21use super::pat::{PatternLocation, RecoverComma};
22use super::path::PathStyle;
23use super::{
24 AllowConstBlockItems, AttrWrapper, BlockMode, FnContext, FnParseMode, ForceCollect, Parser,
25 Restrictions, SemiColonMode, Trailing, UsePreAttrPos,
26};
27use crate::diagnostics::{self, MalformedLoopLabel};
28use crate::exp;
29
30impl<'a> Parser<'a> {
31 pub fn parse_stmt_nonterminal(&mut self, force_collect: ForceCollect) -> Option<Stmt> {
39 match self.parse_stmt_without_recovery(false, force_collect, false) {
40 Ok(stmt) => Some(stmt),
41 Err(e) => {
42 e.emit();
43 self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
44 None
45 }
46 }
47 }
48
49 pub fn parse_stmt_without_recovery(
53 &mut self,
54 capture_semi: bool,
55 force_collect: ForceCollect,
56 force_full_expr: bool,
57 ) -> PResult<'a, Stmt> {
58 self.current_closure.take();
59
60 let pre_attr_pos = self.collect_pos();
61 let attrs = self.parse_outer_attributes()?;
62 let lo = self.token.span;
63
64 if let Some(mut stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
65 this.parse_stmt_without_recovery(false, ForceCollect::Yes, false)
66 }) {
67 stmt.visit_attrs(|stmt_attrs| {
68 attrs.prepend_to_nt_inner(stmt_attrs);
69 });
70 return Ok(stmt);
71 }
72
73 if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
74 self.bump();
75 let mut_let_span = lo.to(self.token.span);
76 self.dcx().emit_err(diagnostics::InvalidVariableDeclaration {
77 span: mut_let_span,
78 sub: diagnostics::InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
79 });
80 }
81
82 let stmt = if self.token.is_keyword(kw::Super) && self.is_keyword_ahead(1, &[kw::Let]) {
83 self.collect_tokens(None, attrs, force_collect, |this, attrs| {
84 let super_span = this.token.span;
85 this.expect_keyword(crate::parser::token_type::ExpKeywordPair {
kw: rustc_span::symbol::kw::Super,
token_type: crate::parser::token_type::TokenType::KwSuper,
}exp!(Super))?;
86 this.expect_keyword(crate::parser::token_type::ExpKeywordPair {
kw: rustc_span::symbol::kw::Let,
token_type: crate::parser::token_type::TokenType::KwLet,
}exp!(Let))?;
87 this.psess.gated_spans.gate(sym::super_let, super_span);
88 let local = this.parse_local(Some(super_span), attrs)?;
89 let trailing = Trailing::from(capture_semi && this.token == token::Semi);
90 Ok((
91 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
92 trailing,
93 UsePreAttrPos::No,
94 ))
95 })?
96 } else if self.token.is_keyword(kw::Let) {
97 self.collect_tokens(None, attrs, force_collect, |this, attrs| {
98 this.expect_keyword(crate::parser::token_type::ExpKeywordPair {
kw: rustc_span::symbol::kw::Let,
token_type: crate::parser::token_type::TokenType::KwLet,
}exp!(Let))?;
99 let local = this.parse_local(None, attrs)?;
100 let trailing = Trailing::from(capture_semi && this.token == token::Semi);
101 Ok((
102 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
103 trailing,
104 UsePreAttrPos::No,
105 ))
106 })?
107 } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
108 self.recover_stmt_local_after_let(
109 lo,
110 attrs,
111 diagnostics::InvalidVariableDeclarationSub::MissingLet,
112 force_collect,
113 )?
114 } else if self.is_kw_followed_by_ident(kw::Auto) && self.may_recover() {
115 self.bump(); self.recover_stmt_local_after_let(
117 lo,
118 attrs,
119 diagnostics::InvalidVariableDeclarationSub::UseLetNotAuto,
120 force_collect,
121 )?
122 } else if self.is_kw_followed_by_ident(sym::var) && self.may_recover() {
123 self.bump(); self.recover_stmt_local_after_let(
125 lo,
126 attrs,
127 diagnostics::InvalidVariableDeclarationSub::UseLetNotVar,
128 force_collect,
129 )?
130 } else if self.check_path()
131 && !self.token.is_qpath_start()
132 && !self.is_path_start_item()
133 && !self.is_builtin()
134 {
135 let stmt = self.collect_tokens(
145 Some(pre_attr_pos),
146 AttrWrapper::empty(),
147 force_collect,
148 |this, _empty_attrs| {
149 Ok((this.parse_stmt_path_start(lo, attrs)?, Trailing::No, UsePreAttrPos::Yes))
150 },
151 );
152 match stmt {
153 Ok(stmt) => stmt,
154 Err(mut err) => {
155 self.suggest_add_missing_let_for_stmt(&mut err);
156 return Err(err);
157 }
158 }
159 } else if let Some(item) = self.parse_item_common(
160 attrs.clone(), false,
162 true,
163 FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
164 force_collect,
165 AllowConstBlockItems::No,
166 )? {
167 self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item)))
168 } else if self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Semi,
token_type: crate::parser::token_type::TokenType::Semi,
}exp!(Semi)) {
169 self.error_outer_attrs(attrs)?;
171 self.mk_stmt(lo, StmtKind::Empty)
172 } else {
173 if self.token == token::CloseBrace {
174 self.error_outer_attrs(attrs.clone())?;
175 }
176
177 let restrictions =
180 if force_full_expr { Restrictions::empty() } else { Restrictions::STMT_EXPR };
181 let e = self.collect_tokens(
182 Some(pre_attr_pos),
183 AttrWrapper::empty(),
184 force_collect,
185 |this, _empty_attrs| {
186 let (expr, _) = this.parse_expr_res_after_attrs(restrictions, attrs)?;
187 Ok((expr, Trailing::No, UsePreAttrPos::Yes))
188 },
189 )?;
190 if #[allow(non_exhaustive_omitted_patterns)] match e.kind {
ExprKind::Assign(..) => true,
_ => false,
}matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(crate::parser::token_type::ExpKeywordPair {
kw: rustc_span::symbol::kw::Else,
token_type: crate::parser::token_type::TokenType::KwElse,
}exp!(Else)) {
191 let bl = self.parse_block()?;
192 self.dcx()
195 .emit_err(diagnostics::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
196 }
197 self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
198 };
199
200 self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt);
201 Ok(stmt)
202 }
203
204 fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
205 let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
206 let path = this.parse_path(PathStyle::Expr)?;
207
208 if this.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Bang,
token_type: crate::parser::token_type::TokenType::Bang,
}exp!(Bang)) {
209 let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
210 return Ok((
211 stmt_mac,
212 Trailing::from(this.token == token::Semi),
213 UsePreAttrPos::No,
214 ));
215 }
216
217 let expr = if this.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::OpenBrace,
token_type: crate::parser::token_type::TokenType::OpenBrace,
}exp!(OpenBrace)) {
218 this.parse_expr_struct(None, path, true)?
219 } else {
220 let hi = this.prev_token.span;
221 this.mk_expr(lo.to(hi), ExprKind::Path(None, path))
222 };
223
224 let expr = this.with_res(Restrictions::STMT_EXPR, |this| {
225 this.parse_expr_dot_or_call_with(attrs, expr, lo)
226 })?;
227 Ok((
229 this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)),
230 Trailing::No,
231 UsePreAttrPos::No,
232 ))
233 })?;
234
235 if let StmtKind::Expr(expr) = stmt.kind {
236 let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
239 this.parse_expr_assoc_rest(Bound::Unbounded, true, expr)
240 })?;
241 Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
242 } else {
243 Ok(stmt)
244 }
245 }
246
247 fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
250 let args = self.parse_delim_args()?;
251 let hi = self.prev_token.span;
252
253 let style = match args.delim {
254 Delimiter::Brace => MacStmtStyle::Braces,
255 _ => MacStmtStyle::NoBraces,
256 };
257
258 let mac = Box::new(MacCall { path, args });
259
260 let kind = if (style == MacStmtStyle::Braces
261 && !#[allow(non_exhaustive_omitted_patterns)] match self.token.kind {
token::Dot | token::Question => true,
_ => false,
}matches!(self.token.kind, token::Dot | token::Question))
262 || #[allow(non_exhaustive_omitted_patterns)] match self.token.kind {
token::Semi | token::Eof |
token::CloseInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Stmt)) =>
true,
_ => false,
}matches!(
263 self.token.kind,
264 token::Semi
265 | token::Eof
266 | token::CloseInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Stmt))
267 ) {
268 StmtKind::MacCall(Box::new(MacCallStmt { mac, style, attrs, tokens: None }))
269 } else {
270 let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
272 let e = self.maybe_recover_from_bad_qpath(e)?;
273 let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
274 let (e, _) = self.parse_expr_assoc_rest(Bound::Unbounded, false, e)?;
275 StmtKind::Expr(e)
276 };
277 Ok(self.mk_stmt(lo.to(hi), kind))
278 }
279
280 fn error_outer_attrs(&self, attrs: AttrWrapper) -> PResult<'a, ()> {
283 if attrs.is_empty() {
284 return Ok(());
285 }
286 let attrs = attrs.take_for_recovery(self.psess);
287 let last = attrs.last().unwrap();
288 Err(if last.is_doc_comment() {
289 self.dcx().create_err(diagnostics::DocCommentDoesNotDocumentAnything {
290 span: last.span,
291 missing_comma: None,
292 })
293 } else {
294 {
match (&last.style, &AttrStyle::Outer) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(last.style, AttrStyle::Outer);
295 self.dcx().create_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span })
296 })
297 }
298
299 fn recover_stmt_local_after_let(
300 &mut self,
301 lo: Span,
302 attrs: AttrWrapper,
303 subdiagnostic: fn(Span) -> diagnostics::InvalidVariableDeclarationSub,
304 force_collect: ForceCollect,
305 ) -> PResult<'a, Stmt> {
306 let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
307 let local = this.parse_local(None, attrs)?;
308 Ok((
310 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
311 Trailing::No,
312 UsePreAttrPos::No,
313 ))
314 })?;
315 self.dcx()
316 .emit_err(diagnostics::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
317 Ok(stmt)
318 }
319
320 fn parse_local(&mut self, super_: Option<Span>, attrs: AttrVec) -> PResult<'a, Box<Local>> {
322 let lo = super_.unwrap_or(self.prev_token.span);
323
324 if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
325 self.dcx()
326 .emit_err(diagnostics::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
327 self.bump();
328 }
329
330 let (pat, colon) =
331 self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
332
333 let (err, ty, colon_sp) = if colon {
334 let parser_snapshot_before_type = self.clone();
337 let colon_sp = self.prev_token.span;
338 match self.parse_ty() {
339 Ok(ty) => (None, Some(ty), Some(colon_sp)),
340 Err(mut err) => {
341 err.span_label(
342 colon_sp,
343 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("while parsing the type for {0}",
pat.descr().map_or_else(|| "the binding".to_string(),
|n|
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", n))
}))))
})format!(
344 "while parsing the type for {}",
345 pat.descr()
346 .map_or_else(|| "the binding".to_string(), |n| format!("`{n}`"))
347 ),
348 );
349 let err = if self.check_noexpect(&token::Eq) {
353 err.emit();
354 None
355 } else {
356 let parser_snapshot_after_type =
358 mem::replace(self, parser_snapshot_before_type);
359 Some((parser_snapshot_after_type, colon_sp, err))
360 };
361 (err, None, Some(colon_sp))
362 }
363 }
364 } else {
365 (None, None, None)
366 };
367
368 let init_wrapped = self
369 .tree_look_ahead(2, |tree| match tree {
370 TokenTree::Token(tok, _) => tok.is_keyword(kw::Else),
371 TokenTree::Delimited(..) => false,
372 })
373 .unwrap_or(false);
374
375 let init = match (self.parse_initializer(err.is_some()), err) {
376 (Ok(init), None) => {
377 init
379 }
380 (Ok(init), Some((_, colon_sp, mut err))) => {
381 err.span_suggestion_verbose(
385 colon_sp,
386 "use `=` if you meant to assign",
387 " =",
388 Applicability::MachineApplicable,
389 );
390 err.emit();
391 init
395 }
396 (Err(init_err), Some((snapshot, _, ty_err))) => {
397 init_err.cancel();
399 *self = snapshot;
403 return Err(ty_err);
404 }
405 (Err(err), None) => {
406 return Err(err);
410 }
411 };
412 let trailing_token = self.prev_token;
413 let kind = match init {
414 None => LocalKind::Decl,
415 Some(init) => {
416 if self.eat_keyword(crate::parser::token_type::ExpKeywordPair {
kw: rustc_span::symbol::kw::Else,
token_type: crate::parser::token_type::TokenType::KwElse,
}exp!(Else)) {
417 if self.token.is_keyword(kw::If) {
418 let msg = "conditional `else if` is not supported for `let...else`";
421 return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
422 }
423 let els = self.parse_block()?;
424 if !init_wrapped {
426 self.check_let_else_init_bool_expr(&init);
427 }
428 if #[allow(non_exhaustive_omitted_patterns)] match trailing_token.kind {
TokenKind::CloseBrace => true,
_ => false,
}matches!(trailing_token.kind, TokenKind::CloseBrace) {
429 self.check_let_else_init_trailing_brace(&init);
430 }
431
432 LocalKind::InitElse(init, els)
433 } else {
434 LocalKind::Init(init)
435 }
436 }
437 };
438 let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
439 Ok(Box::new(ast::Local {
440 super_,
441 ty,
442 pat,
443 kind,
444 id: DUMMY_NODE_ID,
445 span: lo.to(hi),
446 colon_sp,
447 attrs,
448 tokens: None,
449 }))
450 }
451
452 fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
453 if let ast::ExprKind::Binary(op, ..) = init.kind {
454 if op.node.is_lazy() {
455 self.dcx().emit_err(diagnostics::InvalidExpressionInLetElse {
456 span: init.span,
457 operator: op.node.as_str(),
458 sugg: diagnostics::WrapInParentheses::Expression {
459 left: init.span.shrink_to_lo(),
460 right: init.span.shrink_to_hi(),
461 },
462 });
463 }
464 }
465 }
466
467 fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
468 if let Some(trailing) = classify::expr_trailing_brace(init) {
469 let (span, sugg) = match trailing {
470 TrailingBrace::MacCall(mac) => (
471 mac.span(),
472 diagnostics::WrapInParentheses::MacroArgs {
473 left: mac.args.dspan.open,
474 right: mac.args.dspan.close,
475 },
476 ),
477 TrailingBrace::Expr(expr) => (
478 expr.span,
479 diagnostics::WrapInParentheses::Expression {
480 left: expr.span.shrink_to_lo(),
481 right: expr.span.shrink_to_hi(),
482 },
483 ),
484 };
485 self.dcx().emit_err(diagnostics::InvalidCurlyInLetElse {
486 span: self.psess.source_map().end_point(span),
487 sugg,
488 });
489 }
490 }
491
492 fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<Box<Expr>>> {
494 let eq_consumed = match self.token.kind {
495 token::PlusEq
496 | token::MinusEq
497 | token::StarEq
498 | token::SlashEq
499 | token::PercentEq
500 | token::CaretEq
501 | token::AndEq
502 | token::OrEq
503 | token::ShlEq
504 | token::ShrEq => {
505 let extra_op_span = self.psess.source_map().start_point(self.token.span);
510 self.dcx().emit_err(diagnostics::CompoundAssignmentExpressionInLet {
511 span: self.token.span,
512 suggestion: extra_op_span,
513 });
514 self.bump();
515 true
516 }
517 _ => self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Eq,
token_type: crate::parser::token_type::TokenType::Eq,
}exp!(Eq)),
518 };
519
520 Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None })
521 }
522
523 pub fn parse_block(&mut self) -> PResult<'a, Box<Block>> {
525 let (attrs, block) = self.parse_inner_attrs_and_block(None)?;
526 if let [.., last] = &*attrs {
527 let suggest_to_outer = match &last.kind {
528 ast::AttrKind::Normal(attr) => attr.item.is_valid_for_outer_style(),
529 _ => false,
530 };
531 self.error_on_forbidden_inner_attr(
532 last.span,
533 super::attr::InnerAttrPolicy::Forbidden(Some(
534 InnerAttrForbiddenReason::InCodeBlock,
535 )),
536 suggest_to_outer,
537 );
538 }
539 Ok(block)
540 }
541
542 fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
543 let prev = self.prev_token.span;
544 let sp = self.token.span;
545 let mut err = self.dcx().struct_span_err(sp, msg);
546 self.label_expected_raw_ref(&mut err);
547 err.span_label(sp, "expected `{`");
548 if self.token == token::CloseBrace {
549 return err;
550 }
551
552 let do_not_suggest_help = self.token.is_keyword(kw::In)
553 || self.token == token::Colon
554 || self.prev_token.is_keyword(kw::Raw);
555
556 match self.parse_stmt_without_recovery(false, ForceCollect::No, false) {
563 Ok(_)
578 if (!self.token.is_keyword(kw::Else)
579 && self.look_ahead(1, |t| t == &token::OpenBrace))
580 || do_not_suggest_help => {}
581 Ok(Stmt { kind: StmtKind::Empty, .. }) => {}
583 Ok(stmt) => {
584 let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
585 let stmt_span = if stmt_own_line && self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Semi,
token_type: crate::parser::token_type::TokenType::Semi,
}exp!(Semi)) {
586 stmt.span.with_hi(self.prev_token.span.hi())
588 } else {
589 stmt.span
590 };
591 self.suggest_fixes_misparsed_for_loop_head(
592 &mut err,
593 prev.between(sp),
594 stmt_span,
595 &stmt.kind,
596 );
597 }
598 Err(e) => {
599 e.delay_as_bug();
600 }
601 }
602 err
603 }
604
605 fn suggest_fixes_misparsed_for_loop_head(
606 &self,
607 e: &mut Diag<'_>,
608 between: Span,
609 stmt_span: Span,
610 stmt_kind: &StmtKind,
611 ) {
612 match (&self.token.kind, &stmt_kind) {
613 (token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Call(..) = expr.kind => {
614 e.span_suggestion_verbose(
616 between,
617 "you might have meant to write a method call",
618 ".".to_string(),
619 Applicability::MaybeIncorrect,
620 );
621 }
622 (token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Field(..) = expr.kind => {
623 e.span_suggestion_verbose(
625 between,
626 "you might have meant to write a field access",
627 ".".to_string(),
628 Applicability::MaybeIncorrect,
629 );
630 }
631 (token::CloseBrace, StmtKind::Expr(expr))
632 if let ExprKind::Struct(expr) = &expr.kind
633 && let None = expr.qself
634 && expr.path.segments.len() == 1 =>
635 {
636 e.span_suggestion_verbose(
640 between,
641 "you might have meant to write a field access",
642 ".".to_string(),
643 Applicability::MaybeIncorrect,
644 );
645 }
646 (token::OpenBrace, StmtKind::Expr(expr))
647 if let ExprKind::Lit(lit) = expr.kind
648 && let None = lit.suffix
649 && let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
650 {
651 e.span_suggestion_verbose(
654 between,
655 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you might have meant to write a field access"))
})format!("you might have meant to write a field access"),
656 ".".to_string(),
657 Applicability::MaybeIncorrect,
658 );
659 }
660 (token::OpenBrace, StmtKind::Expr(expr))
661 if let ExprKind::Loop(..)
662 | ExprKind::If(..)
663 | ExprKind::While(..)
664 | ExprKind::Match(..)
665 | ExprKind::ForLoop { .. }
666 | ExprKind::TryBlock(..)
667 | ExprKind::Ret(..)
668 | ExprKind::Closure(..)
669 | ExprKind::Struct(..)
670 | ExprKind::Try(..) = expr.kind =>
671 {
672 e.multipart_suggestion(
674 "you might have meant to write this as part of a block",
675 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(stmt_span.shrink_to_lo(), "{ ".to_string()),
(stmt_span.shrink_to_hi(), " }".to_string())]))vec![
676 (stmt_span.shrink_to_lo(), "{ ".to_string()),
677 (stmt_span.shrink_to_hi(), " }".to_string()),
678 ],
679 Applicability::MaybeIncorrect,
681 );
682 }
683 (token::OpenBrace, _) => {}
684 (_, _) => {
685 e.multipart_suggestion(
686 "you might have meant to write this as part of a block",
687 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(stmt_span.shrink_to_lo(), "{ ".to_string()),
(stmt_span.shrink_to_hi(), " }".to_string())]))vec![
688 (stmt_span.shrink_to_lo(), "{ ".to_string()),
689 (stmt_span.shrink_to_hi(), " }".to_string()),
690 ],
691 Applicability::MaybeIncorrect,
693 );
694 }
695 }
696 }
697
698 fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
699 let tok = super::token_descr(&self.token);
700 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected `{{`, found {0}", tok))
})format!("expected `{{`, found {tok}");
701 Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
702 }
703
704 pub(super) fn parse_inner_attrs_and_block(
709 &mut self,
710 loop_header: Option<Span>,
711 ) -> PResult<'a, (AttrVec, Box<Block>)> {
712 self.parse_block_common(self.token.span, BlockCheckMode::Default, loop_header)
713 }
714
715 pub(super) fn parse_block_common(
720 &mut self,
721 lo: Span,
722 blk_mode: BlockCheckMode,
723 loop_header: Option<Span>,
724 ) -> PResult<'a, (AttrVec, Box<Block>)> {
725 if let Some(block) = self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block()) {
726 return Ok((AttrVec::new(), block));
727 }
728
729 let maybe_ident = self.prev_token;
730 self.maybe_recover_unexpected_block_label(loop_header);
731 if !self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::OpenBrace,
token_type: crate::parser::token_type::TokenType::OpenBrace,
}exp!(OpenBrace)) {
732 return self.error_block_no_opening_brace();
733 }
734
735 let attrs = self.parse_inner_attributes()?;
736 let tail = match self.maybe_suggest_struct_literal(lo, blk_mode, maybe_ident) {
737 Some(tail) => tail?,
738 None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
739 };
740 Ok((attrs, tail))
741 }
742
743 pub fn parse_block_tail(
746 &mut self,
747 lo: Span,
748 s: BlockCheckMode,
749 recover: AttemptLocalParseRecovery,
750 ) -> PResult<'a, Box<Block>> {
751 let mut stmts = ThinVec::new();
752 let mut snapshot = None;
753 while !self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::CloseBrace,
token_type: crate::parser::token_type::TokenType::CloseBrace,
}exp!(CloseBrace)) {
754 if self.token == token::Eof {
755 break;
756 }
757 if self.is_vcs_conflict_marker(&TokenKind::Shl, &TokenKind::Lt) {
758 snapshot = Some(self.create_snapshot_for_diagnostic());
762 }
763 let stmt = match self.parse_full_stmt(recover) {
764 Err(mut err) if recover.yes() => {
765 if let Some(ref mut snapshot) = snapshot {
766 snapshot.recover_vcs_conflict_marker();
767 }
768 if self.token == token::Colon {
769 if self.prev_token.is_integer_lit()
773 && self.may_recover()
774 && self.look_ahead(1, |token| token.is_integer_lit())
775 {
776 err.span_suggestion_verbose(
779 self.token.span,
780 "you might have meant a range expression",
781 "..",
782 Applicability::MaybeIncorrect,
783 );
784 } else {
785 self.bump();
788 if self.token.span.lo() == self.prev_token.span.hi() {
789 err.span_suggestion_verbose(
790 self.prev_token.span,
791 "maybe write a path separator here",
792 "::",
793 Applicability::MaybeIncorrect,
794 );
795 }
796 }
797 }
798
799 let guar = err.emit_err();
800 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
801 self.mk_stmt_err(self.token.span, guar)
802 }
803 Ok(stmt) => stmt,
804 Err(err) => return Err(err),
805 };
806 stmts.push(stmt);
807 }
808 Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
809 }
810
811 fn recover_missing_let_else(&mut self, err: &mut Diag<'_>, pat: &ast::Pat, stmt_span: Span) {
812 if self.token.kind != token::OpenBrace {
813 return;
814 }
815 match pat.kind {
816 ast::PatKind::Ident(..) | ast::PatKind::Missing | ast::PatKind::Wild => {
817 return;
819 }
820 _ => {}
821 }
822 let snapshot = self.create_snapshot_for_diagnostic();
823 let block_span = self.token.span;
824 let (if_let, let_else) = match self.parse_block() {
825 Ok(block) => {
826 let mut idents = ::alloc::vec::Vec::new()vec![];
827 pat.walk(&mut |pat: &ast::Pat| {
828 if let ast::PatKind::Ident(_, ident, _) = pat.kind {
829 idents.push(ident);
830 }
831 true
832 });
833
834 struct IdentFinder {
835 idents: Vec<Ident>,
836 references_ident: bool = false,
841 has_return: bool = false,
844 }
845
846 impl<'a> Visitor<'a> for IdentFinder {
847 fn visit_ident(&mut self, ident: &Ident) {
848 for i in &self.idents {
849 if ident.name == i.name {
850 self.references_ident = true;
851 }
852 }
853 }
854 fn visit_expr(&mut self, node: &'a Expr) {
855 if let ExprKind::Ret(..) = node.kind {
856 self.has_return = true;
857 }
858 walk_expr(self, node);
859 }
860 }
861
862 let mut visitor = IdentFinder { idents, .. };
866 visitor.visit_block(&block);
867
868 (visitor.references_ident, visitor.has_return)
869 }
870 Err(e) => {
871 e.cancel();
872 self.restore_snapshot(snapshot);
873 (false, false)
874 }
875 };
876
877 let mut alternatively = "";
878 if if_let || !let_else {
879 alternatively = "alternatively, ";
880 err.span_suggestion_verbose(
881 stmt_span.shrink_to_lo(),
882 "you might have meant to use `if let`",
883 "if ".to_string(),
884 if if_let {
885 Applicability::MachineApplicable
886 } else {
887 Applicability::MaybeIncorrect
888 },
889 );
890 }
891 if let_else || !if_let {
892 err.span_suggestion_verbose(
893 block_span.shrink_to_lo(),
894 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}you might have meant to use `let...else`",
alternatively))
})format!("{alternatively}you might have meant to use `let...else`"),
895 "else ".to_string(),
896 if let_else {
897 Applicability::MachineApplicable
898 } else {
899 Applicability::MaybeIncorrect
900 },
901 );
902 }
903 }
904
905 fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
906 let Some((ident, _)) = self.token.ident() else {
907 return;
908 };
909 if let Some(c) = ident.name.as_str().chars().next()
910 && c.is_uppercase()
911 {
912 return;
913 }
914 if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
915 return;
916 }
917 if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
918 } else if self.prev_token.is_non_reserved_ident() {
920 } else if self.prev_token.kind == token::Question {
922 } else if self.prev_token.kind == token::CloseParen {
924 } else {
926 return;
927 }
928 if self.token.span == self.prev_token.span {
929 return;
931 }
932 if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
933 err.span_suggestion_verbose(
934 self.prev_token.span.between(self.token.span),
935 "you might have meant to write a field access",
936 ".".to_string(),
937 Applicability::MaybeIncorrect,
938 );
939 }
940 if self.look_ahead(1, |t| t.kind == token::OpenParen) {
941 err.span_suggestion_verbose(
942 self.prev_token.span.between(self.token.span),
943 "you might have meant to write a method call",
944 ".".to_string(),
945 Applicability::MaybeIncorrect,
946 );
947 }
948 }
949
950 fn try_recover_let_missing_semi(&mut self, local: &mut Local) -> Option<ErrorGuaranteed> {
951 let expr = match &mut local.kind {
952 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => expr,
953 LocalKind::Decl => return None,
954 };
955 if let Some((span, guar)) =
956 self.missing_semi_from_binop("`let` binding", expr, Some(local.span.shrink_to_lo()))
957 {
958 self.fn_body_missing_semi_guar = Some(guar);
959 *expr = self.mk_expr(span, ExprKind::Err(guar));
960 return Some(guar);
961 }
962 None
963 }
964
965 pub fn parse_full_stmt(&mut self, recover: AttemptLocalParseRecovery) -> PResult<'a, Stmt> {
967 if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
969 this.parse_stmt_without_recovery(false, ForceCollect::No, true)
978 }) {
979 return Ok(stmt);
980 }
981
982 let mut stmt = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?;
983
984 let mut eat_semi = true;
985 let mut add_semi_to_stmt = false;
986
987 match &mut stmt.kind {
988 StmtKind::Expr(expr)
990 if classify::expr_requires_semi_to_be_stmt(expr)
991 && !expr.attrs.is_empty()
992 && !#[allow(non_exhaustive_omitted_patterns)] match self.token.kind {
token::Eof | token::Semi | token::CloseBrace => true,
_ => false,
}matches!(self.token.kind, token::Eof | token::Semi | token::CloseBrace) =>
993 {
994 let guar = self.attr_on_non_tail_expr(&expr);
996 let sp = expr.span.to(self.prev_token.span);
998 *expr = self.mk_expr_err(sp, guar);
999 }
1000
1001 StmtKind::Expr(expr)
1003 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
1004 {
1005 let expect_result =
1008 if let Err(e) = self.maybe_recover_from_ternary_operator(Some(expr.span)) {
1009 Err(e)
1010 } else {
1011 self.expect_one_of(&[], &[crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Semi,
token_type: crate::parser::token_type::TokenType::Semi,
}exp!(Semi), crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::CloseBrace,
token_type: crate::parser::token_type::TokenType::CloseBrace,
}exp!(CloseBrace)])
1012 };
1013
1014 let replace_with_err = 'break_recover: {
1017 match expect_result {
1018 Ok(Recovered::No) => None,
1019 Ok(Recovered::Yes(guar)) => {
1020 Some(guar)
1022 }
1023 Err(e) => {
1024 if self.recover_colon_as_semi() {
1025 e.delay_as_bug();
1027 add_semi_to_stmt = true;
1028 eat_semi = false;
1029
1030 break 'break_recover None;
1031 }
1032
1033 if self.token == token::Colon
1034 && let ExprKind::Path(None, ast::Path { segments, .. }) = &expr.kind
1035 && let [segment] = segments.as_slice()
1036 && self.look_ahead(1, |t| {
1037 t.is_metavar_block()
1038 || t.kind == token::OpenBrace
1039 || t.non_raw_ident().is_some_and(|ident| {
1040 #[allow(non_exhaustive_omitted_patterns)] match ident.name {
kw::For | kw::Loop | kw::While => true,
_ => false,
}matches!(ident.name, kw::For | kw::Loop | kw::While)
1041 })
1042 })
1043 {
1044 let snapshot = self.create_snapshot_for_diagnostic();
1045 let label = Label {
1046 ident: Ident::from_str_and_span(
1047 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\'{0}", segment.ident))
})format!("'{}", segment.ident),
1048 segment.ident.span,
1049 ),
1050 };
1051 match self.parse_expr_labeled(label, false) {
1052 Ok(labeled_expr) => {
1053 e.cancel();
1054 self.dcx().emit_err(MalformedLoopLabel {
1055 span: label.ident.span,
1056 suggestion: label.ident.span.shrink_to_lo(),
1057 });
1058 *expr = labeled_expr;
1059 break 'break_recover None;
1060 }
1061 Err(err) => {
1062 err.cancel();
1063 self.restore_snapshot(snapshot);
1064 }
1065 }
1066 }
1067
1068 let res =
1069 self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
1070
1071 Some(if recover.no() {
1072 res?
1073 } else {
1074 res.unwrap_or_else(|mut e| {
1075 self.recover_missing_dot(&mut e);
1076 let guar = e.emit_err();
1077 self.recover_stmt();
1078 guar
1079 })
1080 })
1081 }
1082 }
1083 };
1084
1085 if let Some(guar) = replace_with_err {
1086 let sp = expr.span.to(self.prev_token.span);
1088 *expr = self.mk_expr_err(sp, guar);
1089 }
1090 }
1091 StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
1092 StmtKind::Let(local) => {
1093 if self.try_recover_let_missing_semi(local).is_some() {
1094 return Ok(stmt);
1095 }
1096 if let Err(mut e) = self.expect_semi() {
1097 match &mut local.kind {
1099 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
1100 self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
1101 .map_err(|mut e| {
1102 self.recover_missing_dot(&mut e);
1103 self.recover_missing_let_else(&mut e, &local.pat, stmt.span);
1104 e
1105 })?;
1106 self.expect_semi()?;
1108 }
1109 LocalKind::Decl => {
1110 if let Some(colon_sp) = local.colon_sp {
1111 e.span_label(
1112 colon_sp,
1113 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("while parsing the type for {0}",
local.pat.descr().map_or_else(|| "the binding".to_string(),
|n|
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`", n))
}))))
})format!(
1114 "while parsing the type for {}",
1115 local.pat.descr().map_or_else(
1116 || "the binding".to_string(),
1117 |n| format!("`{n}`")
1118 )
1119 ),
1120 );
1121 let suggest_eq = if self.token == token::Dot
1122 && let _ = self.bump()
1123 && let mut snapshot = self.create_snapshot_for_diagnostic()
1124 && let Ok(_) = snapshot
1125 .parse_dot_suffix_expr(
1126 colon_sp,
1127 self.mk_expr_err(
1128 colon_sp,
1129 self.dcx().delayed_bug(
1130 "error during `:` -> `=` recovery",
1131 ),
1132 ),
1133 )
1134 .map_err(Diag::cancel)
1135 {
1136 true
1137 } else if let Some(op) = self.check_assoc_op()
1138 && op.node.can_continue_expr_unambiguously()
1139 {
1140 true
1141 } else {
1142 false
1143 };
1144 if suggest_eq && let Some(ty) = &local.ty {
1145 e.span_suggestion_verbose(
1146 local.pat.span.between(ty.span),
1147 "use `=` if you meant to assign",
1148 " = ",
1149 Applicability::MaybeIncorrect,
1150 );
1151 }
1152 }
1153 return Err(e);
1154 }
1155 }
1156 }
1157 eat_semi = false;
1158 }
1159 StmtKind::Empty | StmtKind::Item(_) | StmtKind::Semi(_) => eat_semi = false,
1160 }
1161
1162 if add_semi_to_stmt || (eat_semi && self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Semi,
token_type: crate::parser::token_type::TokenType::Semi,
}exp!(Semi))) {
1163 stmt = stmt.add_trailing_semicolon();
1164 }
1165
1166 stmt.span = stmt.span.to(self.prev_token.span);
1167 Ok(stmt)
1168 }
1169
1170 pub(super) fn mk_block(
1171 &self,
1172 stmts: ThinVec<Stmt>,
1173 rules: BlockCheckMode,
1174 span: Span,
1175 ) -> Box<Block> {
1176 Box::new(Block { stmts, id: DUMMY_NODE_ID, rules, span })
1177 }
1178
1179 pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1180 Stmt { id: DUMMY_NODE_ID, kind, span }
1181 }
1182
1183 pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1184 self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1185 }
1186
1187 pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> Box<Block> {
1188 self.mk_block({
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(self.mk_stmt_err(span, guar));
vec
}thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
1189 }
1190}