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::util::classify::{self, TrailingBrace};
9use rustc_ast::visit::{Visitor, walk_expr};
10use rustc_ast::{
11 AttrStyle, AttrVec, Block, BlockCheckMode, DUMMY_NODE_ID, Expr, ExprKind, HasAttrs, Local,
12 LocalKind, MacCall, MacCallStmt, MacStmtStyle, Recovered, Stmt, StmtKind,
13};
14use rustc_errors::{Applicability, Diag, PResult};
15use rustc_span::{BytePos, ErrorGuaranteed, Ident, Span, kw, sym};
16use thin_vec::{ThinVec, thin_vec};
17
18use super::attr::InnerAttrForbiddenReason;
19use super::diagnostics::AttemptLocalParseRecovery;
20use super::pat::{PatternLocation, RecoverComma};
21use super::path::PathStyle;
22use super::{
23 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 let init = match (self.parse_initializer(err.is_some()), err) {
367 (Ok(init), None) => {
368 init
370 }
371 (Ok(init), Some((_, colon_sp, mut err))) => {
372 err.span_suggestion_verbose(
376 colon_sp,
377 "use `=` if you meant to assign",
378 " =",
379 Applicability::MachineApplicable,
380 );
381 err.emit();
382 init
386 }
387 (Err(init_err), Some((snapshot, _, ty_err))) => {
388 init_err.cancel();
390 *self = snapshot;
394 return Err(ty_err);
395 }
396 (Err(err), None) => {
397 return Err(err);
401 }
402 };
403 let kind = match init {
404 None => LocalKind::Decl,
405 Some(init) => {
406 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)) {
407 if self.token.is_keyword(kw::If) {
408 let msg = "conditional `else if` is not supported for `let...else`";
411 return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
412 }
413 let els = self.parse_block()?;
414 self.check_let_else_init_bool_expr(&init);
415 self.check_let_else_init_trailing_brace(&init);
416 LocalKind::InitElse(init, els)
417 } else {
418 LocalKind::Init(init)
419 }
420 }
421 };
422 let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
423 Ok(Box::new(ast::Local {
424 super_,
425 ty,
426 pat,
427 kind,
428 id: DUMMY_NODE_ID,
429 span: lo.to(hi),
430 colon_sp,
431 attrs,
432 tokens: None,
433 }))
434 }
435
436 fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
437 if let ast::ExprKind::Binary(op, ..) = init.kind {
438 if op.node.is_lazy() {
439 self.dcx().emit_err(diagnostics::InvalidExpressionInLetElse {
440 span: init.span,
441 operator: op.node.as_str(),
442 sugg: diagnostics::WrapInParentheses::Expression {
443 left: init.span.shrink_to_lo(),
444 right: init.span.shrink_to_hi(),
445 },
446 });
447 }
448 }
449 }
450
451 fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
452 if let Some(trailing) = classify::expr_trailing_brace(init) {
453 let (span, sugg) = match trailing {
454 TrailingBrace::MacCall(mac) => (
455 mac.span(),
456 diagnostics::WrapInParentheses::MacroArgs {
457 left: mac.args.dspan.open,
458 right: mac.args.dspan.close,
459 },
460 ),
461 TrailingBrace::Expr(expr) => (
462 expr.span,
463 diagnostics::WrapInParentheses::Expression {
464 left: expr.span.shrink_to_lo(),
465 right: expr.span.shrink_to_hi(),
466 },
467 ),
468 };
469 self.dcx().emit_err(diagnostics::InvalidCurlyInLetElse {
470 span: span.with_lo(span.hi() - BytePos(1)),
471 sugg,
472 });
473 }
474 }
475
476 fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<Box<Expr>>> {
478 let eq_consumed = match self.token.kind {
479 token::PlusEq
480 | token::MinusEq
481 | token::StarEq
482 | token::SlashEq
483 | token::PercentEq
484 | token::CaretEq
485 | token::AndEq
486 | token::OrEq
487 | token::ShlEq
488 | token::ShrEq => {
489 let extra_op_span = self.psess.source_map().start_point(self.token.span);
494 self.dcx().emit_err(diagnostics::CompoundAssignmentExpressionInLet {
495 span: self.token.span,
496 suggestion: extra_op_span,
497 });
498 self.bump();
499 true
500 }
501 _ => self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Eq,
token_type: crate::parser::token_type::TokenType::Eq,
}exp!(Eq)),
502 };
503
504 Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None })
505 }
506
507 pub fn parse_block(&mut self) -> PResult<'a, Box<Block>> {
509 let (attrs, block) = self.parse_inner_attrs_and_block(None)?;
510 if let [.., last] = &*attrs {
511 let suggest_to_outer = match &last.kind {
512 ast::AttrKind::Normal(attr) => attr.item.is_valid_for_outer_style(),
513 _ => false,
514 };
515 self.error_on_forbidden_inner_attr(
516 last.span,
517 super::attr::InnerAttrPolicy::Forbidden(Some(
518 InnerAttrForbiddenReason::InCodeBlock,
519 )),
520 suggest_to_outer,
521 );
522 }
523 Ok(block)
524 }
525
526 fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
527 let prev = self.prev_token.span;
528 let sp = self.token.span;
529 let mut err = self.dcx().struct_span_err(sp, msg);
530 self.label_expected_raw_ref(&mut err);
531 err.span_label(sp, "expected `{`");
532 if self.token == token::CloseBrace {
533 return err;
534 }
535
536 let do_not_suggest_help = self.token.is_keyword(kw::In)
537 || self.token == token::Colon
538 || self.prev_token.is_keyword(kw::Raw);
539
540 match self.parse_stmt_without_recovery(false, ForceCollect::No, false) {
547 Ok(_)
562 if (!self.token.is_keyword(kw::Else)
563 && self.look_ahead(1, |t| t == &token::OpenBrace))
564 || do_not_suggest_help => {}
565 Ok(Stmt { kind: StmtKind::Empty, .. }) => {}
567 Ok(stmt) => {
568 let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
569 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)) {
570 stmt.span.with_hi(self.prev_token.span.hi())
572 } else {
573 stmt.span
574 };
575 self.suggest_fixes_misparsed_for_loop_head(
576 &mut err,
577 prev.between(sp),
578 stmt_span,
579 &stmt.kind,
580 );
581 }
582 Err(e) => {
583 e.delay_as_bug();
584 }
585 }
586 err
587 }
588
589 fn suggest_fixes_misparsed_for_loop_head(
590 &self,
591 e: &mut Diag<'_>,
592 between: Span,
593 stmt_span: Span,
594 stmt_kind: &StmtKind,
595 ) {
596 match (&self.token.kind, &stmt_kind) {
597 (token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Call(..) = expr.kind => {
598 e.span_suggestion_verbose(
600 between,
601 "you might have meant to write a method call",
602 ".".to_string(),
603 Applicability::MaybeIncorrect,
604 );
605 }
606 (token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Field(..) = expr.kind => {
607 e.span_suggestion_verbose(
609 between,
610 "you might have meant to write a field access",
611 ".".to_string(),
612 Applicability::MaybeIncorrect,
613 );
614 }
615 (token::CloseBrace, StmtKind::Expr(expr))
616 if let ExprKind::Struct(expr) = &expr.kind
617 && let None = expr.qself
618 && expr.path.segments.len() == 1 =>
619 {
620 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::OpenBrace, StmtKind::Expr(expr))
631 if let ExprKind::Lit(lit) = expr.kind
632 && let None = lit.suffix
633 && let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
634 {
635 e.span_suggestion_verbose(
638 between,
639 ::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"),
640 ".".to_string(),
641 Applicability::MaybeIncorrect,
642 );
643 }
644 (token::OpenBrace, StmtKind::Expr(expr))
645 if let ExprKind::Loop(..)
646 | ExprKind::If(..)
647 | ExprKind::While(..)
648 | ExprKind::Match(..)
649 | ExprKind::ForLoop { .. }
650 | ExprKind::TryBlock(..)
651 | ExprKind::Ret(..)
652 | ExprKind::Closure(..)
653 | ExprKind::Struct(..)
654 | ExprKind::Try(..) = expr.kind =>
655 {
656 e.multipart_suggestion(
658 "you might have meant to write this as part of a block",
659 ::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![
660 (stmt_span.shrink_to_lo(), "{ ".to_string()),
661 (stmt_span.shrink_to_hi(), " }".to_string()),
662 ],
663 Applicability::MaybeIncorrect,
665 );
666 }
667 (token::OpenBrace, _) => {}
668 (_, _) => {
669 e.multipart_suggestion(
670 "you might have meant to write this as part of a block",
671 ::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![
672 (stmt_span.shrink_to_lo(), "{ ".to_string()),
673 (stmt_span.shrink_to_hi(), " }".to_string()),
674 ],
675 Applicability::MaybeIncorrect,
677 );
678 }
679 }
680 }
681
682 fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
683 let tok = super::token_descr(&self.token);
684 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected `{{`, found {0}", tok))
})format!("expected `{{`, found {tok}");
685 Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
686 }
687
688 pub(super) fn parse_inner_attrs_and_block(
693 &mut self,
694 loop_header: Option<Span>,
695 ) -> PResult<'a, (AttrVec, Box<Block>)> {
696 self.parse_block_common(self.token.span, BlockCheckMode::Default, loop_header)
697 }
698
699 pub(super) fn parse_block_common(
704 &mut self,
705 lo: Span,
706 blk_mode: BlockCheckMode,
707 loop_header: Option<Span>,
708 ) -> PResult<'a, (AttrVec, Box<Block>)> {
709 if let Some(block) = self.eat_metavar_seq(MetaVarKind::Block, |this| this.parse_block()) {
710 return Ok((AttrVec::new(), block));
711 }
712
713 let maybe_ident = self.prev_token;
714 self.maybe_recover_unexpected_block_label(loop_header);
715 if !self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::OpenBrace,
token_type: crate::parser::token_type::TokenType::OpenBrace,
}exp!(OpenBrace)) {
716 return self.error_block_no_opening_brace();
717 }
718
719 let attrs = self.parse_inner_attributes()?;
720 let tail = match self.maybe_suggest_struct_literal(lo, blk_mode, maybe_ident) {
721 Some(tail) => tail?,
722 None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
723 };
724 Ok((attrs, tail))
725 }
726
727 pub fn parse_block_tail(
730 &mut self,
731 lo: Span,
732 s: BlockCheckMode,
733 recover: AttemptLocalParseRecovery,
734 ) -> PResult<'a, Box<Block>> {
735 let mut stmts = ThinVec::new();
736 let mut snapshot = None;
737 while !self.eat(crate::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::CloseBrace,
token_type: crate::parser::token_type::TokenType::CloseBrace,
}exp!(CloseBrace)) {
738 if self.token == token::Eof {
739 break;
740 }
741 if self.is_vcs_conflict_marker(&TokenKind::Shl, &TokenKind::Lt) {
742 snapshot = Some(self.create_snapshot_for_diagnostic());
746 }
747 let stmt = match self.parse_full_stmt(recover) {
748 Err(mut err) if recover.yes() => {
749 if let Some(ref mut snapshot) = snapshot {
750 snapshot.recover_vcs_conflict_marker();
751 }
752 if self.token == token::Colon {
753 if self.prev_token.is_integer_lit()
757 && self.may_recover()
758 && self.look_ahead(1, |token| token.is_integer_lit())
759 {
760 err.span_suggestion_verbose(
763 self.token.span,
764 "you might have meant a range expression",
765 "..",
766 Applicability::MaybeIncorrect,
767 );
768 } else {
769 self.bump();
772 if self.token.span.lo() == self.prev_token.span.hi() {
773 err.span_suggestion_verbose(
774 self.prev_token.span,
775 "maybe write a path separator here",
776 "::",
777 Applicability::MaybeIncorrect,
778 );
779 }
780 }
781 }
782
783 let guar = err.emit();
784 self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
785 self.mk_stmt_err(self.token.span, guar)
786 }
787 Ok(stmt) => stmt,
788 Err(err) => return Err(err),
789 };
790 stmts.push(stmt);
791 }
792 Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
793 }
794
795 fn recover_missing_let_else(&mut self, err: &mut Diag<'_>, pat: &ast::Pat, stmt_span: Span) {
796 if self.token.kind != token::OpenBrace {
797 return;
798 }
799 match pat.kind {
800 ast::PatKind::Ident(..) | ast::PatKind::Missing | ast::PatKind::Wild => {
801 return;
803 }
804 _ => {}
805 }
806 let snapshot = self.create_snapshot_for_diagnostic();
807 let block_span = self.token.span;
808 let (if_let, let_else) = match self.parse_block() {
809 Ok(block) => {
810 let mut idents = ::alloc::vec::Vec::new()vec![];
811 pat.walk(&mut |pat: &ast::Pat| {
812 if let ast::PatKind::Ident(_, ident, _) = pat.kind {
813 idents.push(ident);
814 }
815 true
816 });
817
818 struct IdentFinder {
819 idents: Vec<Ident>,
820 references_ident: bool = false,
825 has_return: bool = false,
828 }
829
830 impl<'a> Visitor<'a> for IdentFinder {
831 fn visit_ident(&mut self, ident: &Ident) {
832 for i in &self.idents {
833 if ident.name == i.name {
834 self.references_ident = true;
835 }
836 }
837 }
838 fn visit_expr(&mut self, node: &'a Expr) {
839 if let ExprKind::Ret(..) = node.kind {
840 self.has_return = true;
841 }
842 walk_expr(self, node);
843 }
844 }
845
846 let mut visitor = IdentFinder { idents, .. };
850 visitor.visit_block(&block);
851
852 (visitor.references_ident, visitor.has_return)
853 }
854 Err(e) => {
855 e.cancel();
856 self.restore_snapshot(snapshot);
857 (false, false)
858 }
859 };
860
861 let mut alternatively = "";
862 if if_let || !let_else {
863 alternatively = "alternatively, ";
864 err.span_suggestion_verbose(
865 stmt_span.shrink_to_lo(),
866 "you might have meant to use `if let`",
867 "if ".to_string(),
868 if if_let {
869 Applicability::MachineApplicable
870 } else {
871 Applicability::MaybeIncorrect
872 },
873 );
874 }
875 if let_else || !if_let {
876 err.span_suggestion_verbose(
877 block_span.shrink_to_lo(),
878 ::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`"),
879 "else ".to_string(),
880 if let_else {
881 Applicability::MachineApplicable
882 } else {
883 Applicability::MaybeIncorrect
884 },
885 );
886 }
887 }
888
889 fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
890 let Some((ident, _)) = self.token.ident() else {
891 return;
892 };
893 if let Some(c) = ident.name.as_str().chars().next()
894 && c.is_uppercase()
895 {
896 return;
897 }
898 if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
899 return;
900 }
901 if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
902 } else if self.prev_token.is_non_reserved_ident() {
904 } else if self.prev_token.kind == token::Question {
906 } else if self.prev_token.kind == token::CloseParen {
908 } else {
910 return;
911 }
912 if self.token.span == self.prev_token.span {
913 return;
915 }
916 if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
917 err.span_suggestion_verbose(
918 self.prev_token.span.between(self.token.span),
919 "you might have meant to write a field access",
920 ".".to_string(),
921 Applicability::MaybeIncorrect,
922 );
923 }
924 if self.look_ahead(1, |t| t.kind == token::OpenParen) {
925 err.span_suggestion_verbose(
926 self.prev_token.span.between(self.token.span),
927 "you might have meant to write a method call",
928 ".".to_string(),
929 Applicability::MaybeIncorrect,
930 );
931 }
932 }
933
934 fn try_recover_let_missing_semi(&mut self, local: &mut Local) -> Option<ErrorGuaranteed> {
935 let expr = match &mut local.kind {
936 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => expr,
937 LocalKind::Decl => return None,
938 };
939 if let Some((span, guar)) =
940 self.missing_semi_from_binop("`let` binding", expr, Some(local.span.shrink_to_lo()))
941 {
942 self.fn_body_missing_semi_guar = Some(guar);
943 *expr = self.mk_expr(span, ExprKind::Err(guar));
944 return Some(guar);
945 }
946 None
947 }
948
949 pub fn parse_full_stmt(&mut self, recover: AttemptLocalParseRecovery) -> PResult<'a, Stmt> {
951 if let Some(stmt) = self.eat_metavar_seq(MetaVarKind::Stmt, |this| {
953 this.parse_stmt_without_recovery(false, ForceCollect::No, true)
962 }) {
963 return Ok(stmt);
964 }
965
966 let mut stmt = self.parse_stmt_without_recovery(true, ForceCollect::No, false)?;
967
968 let mut eat_semi = true;
969 let mut add_semi_to_stmt = false;
970
971 match &mut stmt.kind {
972 StmtKind::Expr(expr)
974 if classify::expr_requires_semi_to_be_stmt(expr)
975 && !expr.attrs.is_empty()
976 && !#[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) =>
977 {
978 let guar = self.attr_on_non_tail_expr(&expr);
980 let sp = expr.span.to(self.prev_token.span);
982 *expr = self.mk_expr_err(sp, guar);
983 }
984
985 StmtKind::Expr(expr)
987 if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
988 {
989 let expect_result =
992 if let Err(e) = self.maybe_recover_from_ternary_operator(Some(expr.span)) {
993 Err(e)
994 } else {
995 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)])
996 };
997
998 let replace_with_err = 'break_recover: {
1001 match expect_result {
1002 Ok(Recovered::No) => None,
1003 Ok(Recovered::Yes(guar)) => {
1004 Some(guar)
1006 }
1007 Err(e) => {
1008 if self.recover_colon_as_semi() {
1009 e.delay_as_bug();
1011 add_semi_to_stmt = true;
1012 eat_semi = false;
1013
1014 break 'break_recover None;
1015 }
1016
1017 match &expr.kind {
1018 ExprKind::Path(None, ast::Path { segments, .. })
1019 if let [segment] = segments.as_slice() =>
1020 {
1021 if self.token == token::Colon
1022 && self.look_ahead(1, |token| {
1023 token.is_metavar_block()
1024 || #[allow(non_exhaustive_omitted_patterns)] match token.kind {
token::Ident(kw::For | kw::Loop | kw::While, token::IdentIsRaw::No) |
token::OpenBrace => true,
_ => false,
}matches!(
1025 token.kind,
1026 token::Ident(
1027 kw::For | kw::Loop | kw::While,
1028 token::IdentIsRaw::No
1029 ) | token::OpenBrace
1030 )
1031 })
1032 {
1033 let snapshot = self.create_snapshot_for_diagnostic();
1034 let label = Label {
1035 ident: Ident::from_str_and_span(
1036 &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\'{0}", segment.ident))
})format!("'{}", segment.ident),
1037 segment.ident.span,
1038 ),
1039 };
1040 match self.parse_expr_labeled(label, false) {
1041 Ok(labeled_expr) => {
1042 e.cancel();
1043 self.dcx().emit_err(MalformedLoopLabel {
1044 span: label.ident.span,
1045 suggestion: label.ident.span.shrink_to_lo(),
1046 });
1047 *expr = labeled_expr;
1048 break 'break_recover None;
1049 }
1050 Err(err) => {
1051 err.cancel();
1052 self.restore_snapshot(snapshot);
1053 }
1054 }
1055 }
1056 }
1057 _ => {}
1058 }
1059
1060 let res =
1061 self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
1062
1063 Some(if recover.no() {
1064 res?
1065 } else {
1066 res.unwrap_or_else(|mut e| {
1067 self.recover_missing_dot(&mut e);
1068 let guar = e.emit();
1069 self.recover_stmt();
1070 guar
1071 })
1072 })
1073 }
1074 }
1075 };
1076
1077 if let Some(guar) = replace_with_err {
1078 let sp = expr.span.to(self.prev_token.span);
1080 *expr = self.mk_expr_err(sp, guar);
1081 }
1082 }
1083 StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
1084 StmtKind::Let(local) => {
1085 if self.try_recover_let_missing_semi(local).is_some() {
1086 return Ok(stmt);
1087 }
1088 if let Err(mut e) = self.expect_semi() {
1089 match &mut local.kind {
1091 LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
1092 self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
1093 .map_err(|mut e| {
1094 self.recover_missing_dot(&mut e);
1095 self.recover_missing_let_else(&mut e, &local.pat, stmt.span);
1096 e
1097 })?;
1098 self.expect_semi()?;
1100 }
1101 LocalKind::Decl => {
1102 if let Some(colon_sp) = local.colon_sp {
1103 e.span_label(
1104 colon_sp,
1105 ::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!(
1106 "while parsing the type for {}",
1107 local.pat.descr().map_or_else(
1108 || "the binding".to_string(),
1109 |n| format!("`{n}`")
1110 )
1111 ),
1112 );
1113 let suggest_eq = if self.token == token::Dot
1114 && let _ = self.bump()
1115 && let mut snapshot = self.create_snapshot_for_diagnostic()
1116 && let Ok(_) = snapshot
1117 .parse_dot_suffix_expr(
1118 colon_sp,
1119 self.mk_expr_err(
1120 colon_sp,
1121 self.dcx().delayed_bug(
1122 "error during `:` -> `=` recovery",
1123 ),
1124 ),
1125 )
1126 .map_err(Diag::cancel)
1127 {
1128 true
1129 } else if let Some(op) = self.check_assoc_op()
1130 && op.node.can_continue_expr_unambiguously()
1131 {
1132 true
1133 } else {
1134 false
1135 };
1136 if suggest_eq && let Some(ty) = &local.ty {
1137 e.span_suggestion_verbose(
1138 local.pat.span.between(ty.span),
1139 "use `=` if you meant to assign",
1140 " = ",
1141 Applicability::MaybeIncorrect,
1142 );
1143 }
1144 }
1145 return Err(e);
1146 }
1147 }
1148 }
1149 eat_semi = false;
1150 }
1151 StmtKind::Empty | StmtKind::Item(_) | StmtKind::Semi(_) => eat_semi = false,
1152 }
1153
1154 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))) {
1155 stmt = stmt.add_trailing_semicolon();
1156 }
1157
1158 stmt.span = stmt.span.to(self.prev_token.span);
1159 Ok(stmt)
1160 }
1161
1162 pub(super) fn mk_block(
1163 &self,
1164 stmts: ThinVec<Stmt>,
1165 rules: BlockCheckMode,
1166 span: Span,
1167 ) -> Box<Block> {
1168 Box::new(Block { stmts, id: DUMMY_NODE_ID, rules, span })
1169 }
1170
1171 pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
1172 Stmt { id: DUMMY_NODE_ID, kind, span }
1173 }
1174
1175 pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
1176 self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
1177 }
1178
1179 pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> Box<Block> {
1180 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)
1181 }
1182}