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