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 {
163 req_name: |_, _| true,
164 context: FnContext::Free,
165 req_body: true,
166 allow_const_params: true,
167 },
168 force_collect,
169 AllowConstBlockItems::No,
170 )? {
171 self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item)))
172 } else if self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Semi,
token_type: crate::parser::token_type::TokenType::Semi,
}exp!(Semi)) {
173 self.error_outer_attrs(attrs)?;
175 self.mk_stmt(lo, StmtKind::Empty)
176 } else {
177 if self.token == token::CloseBrace {
178 self.error_outer_attrs(attrs.clone())?;
179 }
180
181 let restrictions =
184 if force_full_expr { Restrictions::empty() } else { Restrictions::STMT_EXPR };
185 let e = self.collect_tokens(
186 Some(pre_attr_pos),
187 AttrWrapper::empty(),
188 force_collect,
189 |this, _empty_attrs| {
190 let (expr, _) = this.parse_expr_res_after_attrs(restrictions, attrs)?;
191 Ok((expr, Trailing::No, UsePreAttrPos::Yes))
192 },
193 )?;
194 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)) {
195 let bl = self.parse_block()?;
196 self.dcx()
199 .emit_err(diagnostics::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
200 }
201 self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
202 };
203
204 self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt);
205 Ok(stmt)
206 }
207
208 fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
209 let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
210 let path = this.parse_path(PathStyle::Expr)?;
211
212 if this.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Bang,
token_type: crate::parser::token_type::TokenType::Bang,
}exp!(Bang)) {
213 let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
214 return Ok((
215 stmt_mac,
216 Trailing::from(this.token == token::Semi),
217 UsePreAttrPos::No,
218 ));
219 }
220
221 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)) {
222 this.parse_expr_struct(None, path, true)?
223 } else {
224 let hi = this.prev_token.span;
225 this.mk_expr(lo.to(hi), ExprKind::Path(None, path))
226 };
227
228 let expr = this.with_res(Restrictions::STMT_EXPR, |this| {
229 this.parse_expr_dot_or_call_with(attrs, expr, lo)
230 })?;
231 Ok((
233 this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)),
234 Trailing::No,
235 UsePreAttrPos::No,
236 ))
237 })?;
238
239 if let StmtKind::Expr(expr) = stmt.kind {
240 let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
243 this.parse_expr_assoc_rest(Bound::Unbounded, true, expr)
244 })?;
245 Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
246 } else {
247 Ok(stmt)
248 }
249 }
250
251 fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
254 let args = self.parse_delim_args()?;
255 let hi = self.prev_token.span;
256
257 let style = match args.delim {
258 Delimiter::Brace => MacStmtStyle::Braces,
259 _ => MacStmtStyle::NoBraces,
260 };
261
262 let mac = Box::new(MacCall { path, args });
263
264 let kind = if (style == MacStmtStyle::Braces
265 && !#[allow(non_exhaustive_omitted_patterns)] match self.token.kind {
token::Dot | token::Question => true,
_ => false,
}matches!(self.token.kind, token::Dot | token::Question))
266 || #[allow(non_exhaustive_omitted_patterns)] match self.token.kind {
token::Semi | token::Eof |
token::CloseInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Stmt)) =>
true,
_ => false,
}matches!(
267 self.token.kind,
268 token::Semi
269 | token::Eof
270 | token::CloseInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Stmt))
271 ) {
272 StmtKind::MacCall(Box::new(MacCallStmt { mac, style, attrs, tokens: None }))
273 } else {
274 let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
276 let e = self.maybe_recover_from_bad_qpath(e)?;
277 let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
278 let (e, _) = self.parse_expr_assoc_rest(Bound::Unbounded, false, e)?;
279 StmtKind::Expr(e)
280 };
281 Ok(self.mk_stmt(lo.to(hi), kind))
282 }
283
284 fn error_outer_attrs(&self, attrs: AttrWrapper) -> PResult<'a, ()> {
287 if attrs.is_empty() {
288 return Ok(());
289 }
290 let attrs = attrs.take_for_recovery(self.psess);
291 let last = attrs.last().unwrap();
292 Err(if last.is_doc_comment() {
293 self.dcx().create_err(diagnostics::DocCommentDoesNotDocumentAnything {
294 span: last.span,
295 missing_comma: None,
296 })
297 } else {
298 {
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);
299 self.dcx().create_err(diagnostics::ExpectedStatementAfterOuterAttr { span: last.span })
300 })
301 }
302
303 fn recover_stmt_local_after_let(
304 &mut self,
305 lo: Span,
306 attrs: AttrWrapper,
307 subdiagnostic: fn(Span) -> diagnostics::InvalidVariableDeclarationSub,
308 force_collect: ForceCollect,
309 ) -> PResult<'a, Stmt> {
310 let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
311 let local = this.parse_local(None, attrs)?;
312 Ok((
314 this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
315 Trailing::No,
316 UsePreAttrPos::No,
317 ))
318 })?;
319 self.dcx()
320 .emit_err(diagnostics::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
321 Ok(stmt)
322 }
323
324 fn parse_local(&mut self, super_: Option<Span>, attrs: AttrVec) -> PResult<'a, Box<Local>> {
326 let lo = super_.unwrap_or(self.prev_token.span);
327
328 if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
329 self.dcx()
330 .emit_err(diagnostics::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
331 self.bump();
332 }
333
334 let (pat, colon) =
335 self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
336
337 let (err, ty, colon_sp) = if colon {
338 let parser_snapshot_before_type = self.clone();
341 let colon_sp = self.prev_token.span;
342 match self.parse_ty() {
343 Ok(ty) => (None, Some(ty), Some(colon_sp)),
344 Err(mut err) => {
345 err.span_label(
346 colon_sp,
347 ::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!(
348 "while parsing the type for {}",
349 pat.descr()
350 .map_or_else(|| "the binding".to_string(), |n| format!("`{n}`"))
351 ),
352 );
353 let err = if self.check_noexpect(&token::Eq) {
357 err.emit();
358 None
359 } else {
360 let parser_snapshot_after_type =
362 mem::replace(self, parser_snapshot_before_type);
363 Some((parser_snapshot_after_type, colon_sp, err))
364 };
365 (err, None, Some(colon_sp))
366 }
367 }
368 } else {
369 (None, None, None)
370 };
371
372 let init_wrapped = self
373 .tree_look_ahead(2, |tree| match tree {
374 TokenTree::Token(tok, _) => tok.is_keyword(kw::Else),
375 TokenTree::Delimited(..) => false,
376 })
377 .unwrap_or(false);
378
379 let init = match (self.parse_initializer(err.is_some()), err) {
380 (Ok(init), None) => {
381 init
383 }
384 (Ok(init), Some((_, colon_sp, mut err))) => {
385 err.span_suggestion_verbose(
389 colon_sp,
390 "use `=` if you meant to assign",
391 " =",
392 Applicability::MachineApplicable,
393 );
394 err.emit();
395 init
399 }
400 (Err(init_err), Some((snapshot, _, ty_err))) => {
401 init_err.cancel();
403 *self = snapshot;
407 return Err(ty_err);
408 }
409 (Err(err), None) => {
410 return Err(err);
414 }
415 };
416 let trailing_token = self.prev_token;
417 let kind = match init {
418 None => LocalKind::Decl,
419 Some(init) => {
420 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)) {
421 if self.token.is_keyword(kw::If) {
422 let msg = "conditional `else if` is not supported for `let...else`";
425 return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
426 }
427 let els = self.parse_block()?;
428 if !init_wrapped {
430 self.check_let_else_init_bool_expr(&init);
431 }
432 if #[allow(non_exhaustive_omitted_patterns)] match trailing_token.kind {
TokenKind::CloseBrace => true,
_ => false,
}matches!(trailing_token.kind, TokenKind::CloseBrace) {
433 self.check_let_else_init_trailing_brace(&init);
434 }
435
436 LocalKind::InitElse(init, els)
437 } else {
438 LocalKind::Init(init)
439 }
440 }
441 };
442 let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
443 Ok(Box::new(ast::Local {
444 super_,
445 ty,
446 pat,
447 kind,
448 id: DUMMY_NODE_ID,
449 span: lo.to(hi),
450 colon_sp,
451 attrs,
452 tokens: None,
453 }))
454 }
455
456 fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
457 if let ast::ExprKind::Binary(op, ..) = init.kind {
458 if op.node.is_lazy() {
459 self.dcx().emit_err(diagnostics::InvalidExpressionInLetElse {
460 span: init.span,
461 operator: op.node.as_str(),
462 sugg: diagnostics::WrapInParentheses::Expression {
463 left: init.span.shrink_to_lo(),
464 right: init.span.shrink_to_hi(),
465 },
466 });
467 }
468 }
469 }
470
471 fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
472 if let Some(trailing) = classify::expr_trailing_brace(init) {
473 let (span, sugg) = match trailing {
474 TrailingBrace::MacCall(mac) => (
475 mac.span(),
476 diagnostics::WrapInParentheses::MacroArgs {
477 left: mac.args.dspan.open,
478 right: mac.args.dspan.close,
479 },
480 ),
481 TrailingBrace::Expr(expr) => (
482 expr.span,
483 diagnostics::WrapInParentheses::Expression {
484 left: expr.span.shrink_to_lo(),
485 right: expr.span.shrink_to_hi(),
486 },
487 ),
488 };
489 self.dcx().emit_err(diagnostics::InvalidCurlyInLetElse {
490 span: self.psess.source_map().end_point(span),
491 sugg,
492 });
493 }
494 }
495
496 fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<Box<Expr>>> {
498 let eq_consumed = match self.token.kind {
499 token::PlusEq
500 | token::MinusEq
501 | token::StarEq
502 | token::SlashEq
503 | token::PercentEq
504 | token::CaretEq
505 | token::AndEq
506 | token::OrEq
507 | token::ShlEq
508 | token::ShrEq => {
509 let extra_op_span = self.psess.source_map().start_point(self.token.span);
514 self.dcx().emit_err(diagnostics::CompoundAssignmentExpressionInLet {
515 span: self.token.span,
516 suggestion: extra_op_span,
517 });
518 self.bump();
519 true
520 }
521 _ => self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Eq,
token_type: crate::parser::token_type::TokenType::Eq,
}exp!(Eq)),
522 };
523
524 Ok(if eq_consumed || eq_optional { Some(self.parse_expr_in_let()?) } else { None })
525 }
526
527 pub fn parse_block(&mut self) -> PResult<'a, Box<Block>> {
529 let (attrs, block) = self.parse_inner_attrs_and_block(None)?;
530 if let [.., last] = &*attrs {
531 let suggest_to_outer = match &last.kind {
532 ast::AttrKind::Normal(attr) => attr.item.is_valid_for_outer_style(),
533 _ => false,
534 };
535 self.error_on_forbidden_inner_attr(
536 last.span,
537 super::attr::InnerAttrPolicy::Forbidden(Some(
538 InnerAttrForbiddenReason::InCodeBlock,
539 )),
540 suggest_to_outer,
541 );
542 }
543 Ok(block)
544 }
545
546 fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
547 let prev = self.prev_token.span;
548 let sp = self.token.span;
549 let mut err = self.dcx().struct_span_err(sp, msg);
550 self.label_expected_raw_ref(&mut err);
551 err.span_label(sp, "expected `{`");
552 if self.token == token::CloseBrace {
553 return err;
554 }
555
556 let do_not_suggest_help = self.token.is_keyword(kw::In)
557 || self.token == token::Colon
558 || self.prev_token.is_keyword(kw::Raw);
559
560 match self.parse_stmt_without_recovery(false, ForceCollect::No, false) {
567 Ok(_)
582 if (!self.token.is_keyword(kw::Else)
583 && self.look_ahead(1, |t| t == &token::OpenBrace))
584 || do_not_suggest_help => {}
585 Ok(Stmt { kind: StmtKind::Empty, .. }) => {}
587 Ok(stmt) => {
588 let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
589 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)) {
590 stmt.span.with_hi(self.prev_token.span.hi())
592 } else {
593 stmt.span
594 };
595 self.suggest_fixes_misparsed_for_loop_head(
596 &mut err,
597 prev.between(sp),
598 stmt_span,
599 &stmt.kind,
600 );
601 }
602 Err(e) => {
603 e.delay_as_bug();
604 }
605 }
606 err
607 }
608
609 fn suggest_fixes_misparsed_for_loop_head(
610 &self,
611 e: &mut Diag<'_>,
612 between: Span,
613 stmt_span: Span,
614 stmt_kind: &StmtKind,
615 ) {
616 match (&self.token.kind, &stmt_kind) {
617 (token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Call(..) = expr.kind => {
618 e.span_suggestion_verbose(
620 between,
621 "you might have meant to write a method call",
622 ".".to_string(),
623 Applicability::MaybeIncorrect,
624 );
625 }
626 (token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Field(..) = expr.kind => {
627 e.span_suggestion_verbose(
629 between,
630 "you might have meant to write a field access",
631 ".".to_string(),
632 Applicability::MaybeIncorrect,
633 );
634 }
635 (token::CloseBrace, StmtKind::Expr(expr))
636 if let ExprKind::Struct(expr) = &expr.kind
637 && let None = expr.qself
638 && expr.path.segments.len() == 1 =>
639 {
640 e.span_suggestion_verbose(
644 between,
645 "you might have meant to write a field access",
646 ".".to_string(),
647 Applicability::MaybeIncorrect,
648 );
649 }
650 (token::OpenBrace, StmtKind::Expr(expr))
651 if let ExprKind::Lit(lit) = expr.kind
652 && let None = lit.suffix
653 && let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
654 {
655 e.span_suggestion_verbose(
658 between,
659 ::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"),
660 ".".to_string(),
661 Applicability::MaybeIncorrect,
662 );
663 }
664 (token::OpenBrace, StmtKind::Expr(expr))
665 if let ExprKind::Loop(..)
666 | ExprKind::If(..)
667 | ExprKind::While(..)
668 | ExprKind::Match(..)
669 | ExprKind::ForLoop { .. }
670 | ExprKind::TryBlock(..)
671 | ExprKind::Ret(..)
672 | ExprKind::Closure(..)
673 | ExprKind::Struct(..)
674 | ExprKind::Try(..) = expr.kind =>
675 {
676 e.multipart_suggestion(
678 "you might have meant to write this as part of a block",
679 ::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![
680 (stmt_span.shrink_to_lo(), "{ ".to_string()),
681 (stmt_span.shrink_to_hi(), " }".to_string()),
682 ],
683 Applicability::MaybeIncorrect,
685 );
686 }
687 (token::OpenBrace, _) => {}
688 (_, _) => {
689 e.multipart_suggestion(
690 "you might have meant to write this as part of a block",
691 ::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![
692 (stmt_span.shrink_to_lo(), "{ ".to_string()),
693 (stmt_span.shrink_to_hi(), " }".to_string()),
694 ],
695 Applicability::MaybeIncorrect,
697 );
698 }
699 }
700 }
701
702 fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
703 let tok = super::token_descr(&self.token);
704 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected `{{`, found {0}", tok))
})format!("expected `{{`, found {tok}");
705 Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
706 }
707
708 pub(super) fn parse_inner_attrs_and_block(
713 &mut self,
714 loop_header: Option<Span>,
715 ) -> PResult<'a, (AttrVec, Box<Block>)> {
716 self.parse_block_common(self.token.span, BlockCheckMode::Default, loop_header)
717 }
718
719 pub(super) fn parse_block_common(
724 &mut self,
725 lo: Span,
726 blk_mode: BlockCheckMode,
727 loop_header: Option<Span>,
728 ) -> PResult<'a, (AttrVec, Box<Block>)> {
729 if let Some(block) = self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block()) {
730 return Ok((AttrVec::new(), block));
731 }
732
733 let maybe_ident = self.prev_token;
734 self.maybe_recover_unexpected_block_label(loop_header);
735 if !self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::OpenBrace,
token_type: crate::parser::token_type::TokenType::OpenBrace,
}exp!(OpenBrace)) {
736 return self.error_block_no_opening_brace();
737 }
738
739 let attrs = self.parse_inner_attributes()?;
740 let tail = match self.maybe_suggest_struct_literal(lo, blk_mode, maybe_ident) {
741 Some(tail) => tail?,
742 None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
743 };
744 Ok((attrs, tail))
745 }
746
747 pub fn parse_block_tail(
750 &mut self,
751 lo: Span,
752 s: BlockCheckMode,
753 recover: AttemptLocalParseRecovery,
754 ) -> PResult<'a, Box<Block>> {
755 let mut stmts = ThinVec::new();
756 let mut snapshot = None;
757 while !self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::CloseBrace,
token_type: crate::parser::token_type::TokenType::CloseBrace,
}exp!(CloseBrace)) {
758 if self.token == token::Eof {
759 break;
760 }
761 if self.is_vcs_conflict_marker(&TokenKind::Shl, &TokenKind::Lt) {
762 snapshot = Some(self.create_snapshot_for_diagnostic());
766 }
767 let stmt = match self.parse_full_stmt(recover) {
768 Err(mut err) if recover.yes(self) => {
769 if let Some(ref mut snapshot) = snapshot {
770 snapshot.recover_vcs_conflict_marker();
771 }
772 if self.token == token::Colon {
773 if self.prev_token.is_integer_lit()
777 && self.may_recover()
778 && self.look_ahead(1, |token| token.is_integer_lit())
779 {
780 err.span_suggestion_verbose(
783 self.token.span,
784 "you might have meant a range expression",
785 "..",
786 Applicability::MaybeIncorrect,
787 );
788 } else {
789 self.bump();
792 if self.token.span.lo() == self.prev_token.span.hi() {
793 err.span_suggestion_verbose(
794 self.prev_token.span,
795 "maybe write a path separator here",
796 "::",
797 Applicability::MaybeIncorrect,
798 );
799 }
800 }
801 }
802
803 let guar = err.emit_err();
804 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
805 self.mk_stmt_err(self.token.span, guar)
806 }
807 Ok(stmt) => stmt,
808 Err(err) => return Err(err),
809 };
810 stmts.push(stmt);
811 }
812 Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
813 }
814
815 fn recover_missing_let_else(&mut self, err: &mut Diag<'_>, pat: &ast::Pat, stmt_span: Span) {
816 if self.token.kind != token::OpenBrace {
817 return;
818 }
819 match pat.kind {
820 ast::PatKind::Ident(..) | ast::PatKind::Missing | ast::PatKind::Wild => {
821 return;
823 }
824 _ => {}
825 }
826 let snapshot = self.create_snapshot_for_diagnostic();
827 let block_span = self.token.span;
828 let (if_let, let_else) = match self.parse_block() {
829 Ok(block) => {
830 let mut idents = ::alloc::vec::Vec::new()vec![];
831 pat.walk(&mut |pat: &ast::Pat| {
832 if let ast::PatKind::Ident(_, ident, _) = pat.kind {
833 idents.push(ident);
834 }
835 true
836 });
837
838 struct IdentFinder {
839 idents: Vec<Ident>,
840 references_ident: bool = false,
845 has_return: bool = false,
848 }
849
850 impl<'a> Visitor<'a> for IdentFinder {
851 fn visit_ident(&mut self, ident: &Ident) {
852 for i in &self.idents {
853 if ident.name == i.name {
854 self.references_ident = true;
855 }
856 }
857 }
858 fn visit_expr(&mut self, node: &'a Expr) {
859 if let ExprKind::Ret(..) = node.kind {
860 self.has_return = true;
861 }
862 walk_expr(self, node);
863 }
864 }
865
866 let mut visitor = IdentFinder { idents, .. };
870 visitor.visit_block(&block);
871
872 (visitor.references_ident, visitor.has_return)
873 }
874 Err(e) => {
875 e.cancel();
876 self.restore_snapshot(snapshot);
877 (false, false)
878 }
879 };
880
881 let mut alternatively = "";
882 if if_let || !let_else {
883 alternatively = "alternatively, ";
884 err.span_suggestion_verbose(
885 stmt_span.shrink_to_lo(),
886 "you might have meant to use `if let`",
887 "if ".to_string(),
888 if if_let {
889 Applicability::MachineApplicable
890 } else {
891 Applicability::MaybeIncorrect
892 },
893 );
894 }
895 if let_else || !if_let {
896 err.span_suggestion_verbose(
897 block_span.shrink_to_lo(),
898 ::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`"),
899 "else ".to_string(),
900 if let_else {
901 Applicability::MachineApplicable
902 } else {
903 Applicability::MaybeIncorrect
904 },
905 );
906 }
907 }
908
909 fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
910 let Some((ident, _)) = self.token.ident() else {
911 return;
912 };
913 if let Some(c) = ident.name.as_str().chars().next()
914 && c.is_uppercase()
915 {
916 return;
917 }
918 if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
919 return;
920 }
921 if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
922 } else if self.prev_token.is_non_reserved_ident() {
924 } else if self.prev_token.kind == token::Question {
926 } else if self.prev_token.kind == token::CloseParen {
928 } else {
930 return;
931 }
932 if self.token.span == self.prev_token.span {
933 return;
935 }
936 if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
937 err.span_suggestion_verbose(
938 self.prev_token.span.between(self.token.span),
939 "you might have meant to write a field access",
940 ".".to_string(),
941 Applicability::MaybeIncorrect,
942 );
943 }
944 if self.look_ahead(1, |t| t.kind == token::OpenParen) {
945 err.span_suggestion_verbose(
946 self.prev_token.span.between(self.token.span),
947 "you might have meant to write a method call",
948 ".".to_string(),
949 Applicability::MaybeIncorrect,
950 );
951 }
952 }
953
954 fn try_recover_let_missing_semi(&mut self, local: &mut Local) -> Option<ErrorGuaranteed> {
955 let expr = match &mut local.kind {
956 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => expr,
957 LocalKind::Decl => return None,
958 };
959 if let Some((span, guar)) =
960 self.missing_semi_from_binop("`let` binding", expr, Some(local.span.shrink_to_lo()))
961 {
962 self.fn_body_missing_semi_guar = Some(guar);
963 *expr = self.mk_expr(span, ExprKind::Err(guar));
964 return Some(guar);
965 }
966 None
967 }
968
969 pub fn parse_full_stmt(&mut self, recover: AttemptLocalParseRecovery) -> PResult<'a, Stmt> {
971 if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
973 this.parse_stmt_without_recovery(false, ForceCollect::No, true)
982 }) {
983 return Ok(stmt);
984 }
985
986 let mut stmt = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?;
987
988 let mut eat_semi = true;
989 let mut add_semi_to_stmt = false;
990
991 match &mut stmt.kind {
992 StmtKind::Expr(expr)
994 if classify::expr_requires_semi_to_be_stmt(expr)
995 && !expr.attrs.is_empty()
996 && !#[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) =>
997 {
998 let guar = self.attr_on_non_tail_expr(&expr);
1000 let sp = expr.span.to(self.prev_token.span);
1002 *expr = self.mk_expr_err(sp, guar);
1003 }
1004
1005 StmtKind::Expr(expr)
1007 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
1008 {
1009 let expect_result =
1012 if let Err(e) = self.maybe_recover_from_ternary_operator(Some(expr.span)) {
1013 Err(e)
1014 } else {
1015 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)])
1016 };
1017
1018 let replace_with_err = 'break_recover: {
1021 match expect_result {
1022 Ok(Recovered::No) => None,
1023 Ok(Recovered::Yes(guar)) => {
1024 Some(guar)
1026 }
1027 Err(e) => {
1028 if self.recover_colon_as_semi() {
1029 e.delay_as_bug();
1031 add_semi_to_stmt = true;
1032 eat_semi = false;
1033
1034 break 'break_recover None;
1035 }
1036
1037 if self.token == token::Colon
1038 && let ExprKind::Path(None, ast::Path { segments, .. }) = &expr.kind
1039 && let [segment] = segments.as_slice()
1040 && self.look_ahead(1, |t| {
1041 t.is_metavar_block()
1042 || t.kind == token::OpenBrace
1043 || t.non_raw_ident().is_some_and(|ident| {
1044 #[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)
1045 })
1046 })
1047 {
1048 let snapshot = self.create_snapshot_for_diagnostic();
1049 let label = Label {
1050 ident: Ident::from_str_and_span(
1051 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\'{0}", segment.ident))
})format!("'{}", segment.ident),
1052 segment.ident.span,
1053 ),
1054 };
1055 match self.parse_expr_labeled(label, false) {
1056 Ok(labeled_expr) => {
1057 e.cancel();
1058 self.dcx().emit_err(MalformedLoopLabel {
1059 span: label.ident.span,
1060 suggestion: label.ident.span.shrink_to_lo(),
1061 });
1062 *expr = labeled_expr;
1063 break 'break_recover None;
1064 }
1065 Err(err) => {
1066 err.cancel();
1067 self.restore_snapshot(snapshot);
1068 }
1069 }
1070 }
1071
1072 let res =
1073 self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
1074
1075 Some(if recover.no(self) {
1076 res?
1077 } else {
1078 res.unwrap_or_else(|mut e| {
1079 self.recover_missing_dot(&mut e);
1080 let guar = e.emit_err();
1081 self.recover_stmt();
1082 guar
1083 })
1084 })
1085 }
1086 }
1087 };
1088
1089 if let Some(guar) = replace_with_err {
1090 let sp = expr.span.to(self.prev_token.span);
1092 *expr = self.mk_expr_err(sp, guar);
1093 }
1094 }
1095 StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
1096 StmtKind::Let(local) => {
1097 if self.try_recover_let_missing_semi(local).is_some() {
1098 return Ok(stmt);
1099 }
1100 if let Err(mut e) = self.expect_semi() {
1101 match &mut local.kind {
1103 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
1104 self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
1105 .map_err(|mut e| {
1106 self.recover_missing_dot(&mut e);
1107 self.recover_missing_let_else(&mut e, &local.pat, stmt.span);
1108 e
1109 })?;
1110 self.expect_semi()?;
1112 }
1113 LocalKind::Decl => {
1114 if let Some(colon_sp) = local.colon_sp {
1115 e.span_label(
1116 colon_sp,
1117 ::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!(
1118 "while parsing the type for {}",
1119 local.pat.descr().map_or_else(
1120 || "the binding".to_string(),
1121 |n| format!("`{n}`")
1122 )
1123 ),
1124 );
1125 let suggest_eq = if self.token == token::Dot
1126 && let _ = self.bump()
1127 && let mut snapshot = self.create_snapshot_for_diagnostic()
1128 && let Ok(_) = snapshot
1129 .parse_dot_suffix_expr(
1130 colon_sp,
1131 self.mk_expr_err(
1132 colon_sp,
1133 self.dcx().delayed_bug(
1134 "error during `:` -> `=` recovery",
1135 ),
1136 ),
1137 )
1138 .map_err(Diag::cancel)
1139 {
1140 true
1141 } else if let Some(op) = self.check_assoc_op()
1142 && super::expr::diagnostics::op_can_continue_stmt_expr_unambiguously(op.node)
1143 {
1144 true
1145 } else {
1146 false
1147 };
1148 if suggest_eq && let Some(ty) = &local.ty {
1149 e.span_suggestion_verbose(
1150 local.pat.span.between(ty.span),
1151 "use `=` if you meant to assign",
1152 " = ",
1153 Applicability::MaybeIncorrect,
1154 );
1155 }
1156 }
1157 return Err(e);
1158 }
1159 }
1160 }
1161 eat_semi = false;
1162 }
1163 StmtKind::Empty | StmtKind::Item(_) | StmtKind::Semi(_) => eat_semi = false,
1164 }
1165
1166 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))) {
1167 stmt = stmt.add_trailing_semicolon();
1168 }
1169
1170 stmt.span = stmt.span.to(self.prev_token.span);
1171 Ok(stmt)
1172 }
1173
1174 pub(super) fn mk_block(
1175 &self,
1176 stmts: ThinVec<Stmt>,
1177 rules: BlockCheckMode,
1178 span: Span,
1179 ) -> Box<Block> {
1180 Box::new(Block { stmts, id: DUMMY_NODE_ID, rules, span })
1181 }
1182
1183 pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1184 Stmt { id: DUMMY_NODE_ID, kind, span }
1185 }
1186
1187 pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1188 self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1189 }
1190
1191 pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> Box<Block> {
1192 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)
1193 }
1194}