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