blob: 6601011665b41bcb0f863e71eb0f1e5564016d70 [file] [log] [blame]
use super::attr::InnerAttrForbiddenReason;
use super::diagnostics::AttemptLocalParseRecovery;
use super::expr::LhsExpr;
use super::pat::{PatternLocation, RecoverComma};
use super::path::PathStyle;
use super::TrailingToken;
use super::{
AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
};
use crate::errors;
use crate::maybe_whole;
use crate::errors::MalformedLoopLabel;
use crate::parser::Recovered;
use ast::Label;
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::util::classify;
use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt};
use rustc_ast::{StmtKind, DUMMY_NODE_ID};
use rustc_errors::{Applicability, Diag, PResult};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, ErrorGuaranteed, Span};
use std::borrow::Cow;
use std::mem;
use thin_vec::{thin_vec, ThinVec};
impl<'a> Parser<'a> {
/// Parses a statement. This stops just before trailing semicolons on everything but items.
/// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
// Public for rustfmt usage.
pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| {
e.emit();
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
None
}))
}
/// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of
/// whether or not we have attributes.
// Public for `cfg_eval` macro expansion.
pub fn parse_stmt_without_recovery(
&mut self,
capture_semi: bool,
force_collect: ForceCollect,
) -> PResult<'a, Option<Stmt>> {
let attrs = self.parse_outer_attributes()?;
let lo = self.token.span;
maybe_whole!(self, NtStmt, |stmt| {
stmt.visit_attrs(|stmt_attrs| {
attrs.prepend_to_nt_inner(stmt_attrs);
});
Some(stmt.into_inner())
});
if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
self.bump();
let mut_let_span = lo.to(self.token.span);
self.dcx().emit_err(errors::InvalidVariableDeclaration {
span: mut_let_span,
sub: errors::InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
});
}
Ok(Some(if self.token.is_keyword(kw::Let) {
self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
} else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
self.recover_stmt_local_after_let(
lo,
attrs,
errors::InvalidVariableDeclarationSub::MissingLet,
)?
} else if self.is_kw_followed_by_ident(kw::Auto) && self.may_recover() {
self.bump(); // `auto`
self.recover_stmt_local_after_let(
lo,
attrs,
errors::InvalidVariableDeclarationSub::UseLetNotAuto,
)?
} else if self.is_kw_followed_by_ident(sym::var) && self.may_recover() {
self.bump(); // `var`
self.recover_stmt_local_after_let(
lo,
attrs,
errors::InvalidVariableDeclarationSub::UseLetNotVar,
)?
} else if self.check_path()
&& !self.token.is_qpath_start()
&& !self.is_path_start_item()
&& !self.is_builtin()
{
// We have avoided contextual keywords like `union`, items with `crate` visibility,
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
// that starts like a path (1 token), but it fact not a path.
// Also, we avoid stealing syntax from `parse_item_`.
match force_collect {
ForceCollect::Yes => {
self.collect_tokens_no_attrs(|this| this.parse_stmt_path_start(lo, attrs))?
}
ForceCollect::No => match self.parse_stmt_path_start(lo, attrs) {
Ok(stmt) => stmt,
Err(mut err) => {
self.suggest_add_missing_let_for_stmt(&mut err);
return Err(err);
}
},
}
} else if let Some(item) = self.parse_item_common(
attrs.clone(),
false,
true,
FnParseMode { req_name: |_| true, req_body: true },
force_collect,
)? {
// FIXME: Bad copy of attrs
self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
} else if self.eat(&token::Semi) {
// Do not attempt to parse an expression if we're done here.
self.error_outer_attrs(attrs);
self.mk_stmt(lo, StmtKind::Empty)
} else if self.token != token::CloseDelim(Delimiter::Brace) {
// Remainder are line-expr stmts.
let e = match force_collect {
ForceCollect::Yes => self.collect_tokens_no_attrs(|this| {
this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs))
})?,
ForceCollect::No => self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs))?,
};
if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(kw::Else) {
let bl = self.parse_block()?;
// Destructuring assignment ... else.
// This is not allowed, but point it out in a nice way.
self.dcx().emit_err(errors::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
}
self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
} else {
self.error_outer_attrs(attrs);
return Ok(None);
}))
}
fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let path = this.parse_path(PathStyle::Expr)?;
if this.eat(&token::Not) {
let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
if this.token == token::Semi {
return Ok((stmt_mac, TrailingToken::Semi));
} else {
return Ok((stmt_mac, TrailingToken::None));
}
}
let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) {
this.parse_expr_struct(None, path, true)?
} else {
let hi = this.prev_token.span;
this.mk_expr(lo.to(hi), ExprKind::Path(None, path))
};
let expr = this.with_res(Restrictions::STMT_EXPR, |this| {
this.parse_expr_dot_or_call_with(expr, lo, attrs)
})?;
// `DUMMY_SP` will get overwritten later in this function
Ok((this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)), TrailingToken::None))
})?;
if let StmtKind::Expr(expr) = stmt.kind {
// Perform this outside of the `collect_tokens_trailing_token` closure,
// since our outer attributes do not apply to this part of the expression
let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
this.parse_expr_assoc_with(
0,
LhsExpr::AlreadyParsed { expr, starts_statement: true },
)
})?;
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
} else {
Ok(stmt)
}
}
/// Parses a statement macro `mac!(args)` provided a `path` representing `mac`.
/// At this point, the `!` token after the path has already been eaten.
fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
let args = self.parse_delim_args()?;
let hi = self.prev_token.span;
let style = match args.delim {
Delimiter::Brace => MacStmtStyle::Braces,
_ => MacStmtStyle::NoBraces,
};
let mac = P(MacCall { path, args });
let kind = if (style == MacStmtStyle::Braces
&& self.token != token::Dot
&& self.token != token::Question)
|| self.token == token::Semi
|| self.token == token::Eof
{
StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
} else {
// Since none of the above applied, this is an expression statement macro.
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
let e = self.maybe_recover_from_bad_qpath(e)?;
let e = self.parse_expr_dot_or_call_with(e, lo, attrs)?;
let e = self.parse_expr_assoc_with(
0,
LhsExpr::AlreadyParsed { expr: e, starts_statement: false },
)?;
StmtKind::Expr(e)
};
Ok(self.mk_stmt(lo.to(hi), kind))
}
/// Error on outer attributes in this context.
/// Also error if the previous token was a doc comment.
fn error_outer_attrs(&self, attrs: AttrWrapper) {
if !attrs.is_empty()
&& let attrs @ [.., last] = &*attrs.take_for_recovery(self.psess)
{
if last.is_doc_comment() {
self.dcx().emit_err(errors::DocCommentDoesNotDocumentAnything {
span: last.span,
missing_comma: None,
});
} else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
self.dcx().emit_err(errors::ExpectedStatementAfterOuterAttr { span: last.span });
}
}
}
fn recover_stmt_local_after_let(
&mut self,
lo: Span,
attrs: AttrWrapper,
subdiagnostic: fn(Span) -> errors::InvalidVariableDeclarationSub,
) -> PResult<'a, Stmt> {
let stmt =
self.collect_tokens_trailing_token(attrs, ForceCollect::Yes, |this, attrs| {
let local = this.parse_local(attrs)?;
// FIXME - maybe capture semicolon in recovery?
Ok((
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
TrailingToken::None,
))
})?;
self.dcx()
.emit_err(errors::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
Ok(stmt)
}
fn parse_local_mk(
&mut self,
lo: Span,
attrs: AttrWrapper,
capture_semi: bool,
force_collect: ForceCollect,
) -> PResult<'a, Stmt> {
self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
this.expect_keyword(kw::Let)?;
let local = this.parse_local(attrs)?;
let trailing = if capture_semi && this.token.kind == token::Semi {
TrailingToken::Semi
} else {
TrailingToken::None
};
Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
})
}
/// Parses a local variable declaration.
fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
let lo = self.prev_token.span;
if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
self.dcx().emit_err(errors::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
self.bump();
}
let (pat, colon) =
self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
let (err, ty, colon_sp) = if colon {
// Save the state of the parser before parsing type normally, in case there is a `:`
// instead of an `=` typo.
let parser_snapshot_before_type = self.clone();
let colon_sp = self.prev_token.span;
match self.parse_ty() {
Ok(ty) => (None, Some(ty), Some(colon_sp)),
Err(mut err) => {
err.span_label(
colon_sp,
format!(
"while parsing the type for {}",
pat.descr()
.map_or_else(|| "the binding".to_string(), |n| format!("`{n}`"))
),
);
// we use noexpect here because we don't actually expect Eq to be here
// but we are still checking for it in order to be able to handle it if
// it is there
let err = if self.check_noexpect(&token::Eq) {
err.emit();
None
} else {
// Rewind to before attempting to parse the type and continue parsing.
let parser_snapshot_after_type =
mem::replace(self, parser_snapshot_before_type);
Some((parser_snapshot_after_type, colon_sp, err))
};
(err, None, Some(colon_sp))
}
}
} else {
(None, None, None)
};
let init = match (self.parse_initializer(err.is_some()), err) {
(Ok(init), None) => {
// init parsed, ty parsed
init
}
(Ok(init), Some((_, colon_sp, mut err))) => {
// init parsed, ty error
// Could parse the type as if it were the initializer, it is likely there was a
// typo in the code: `:` instead of `=`. Add suggestion and emit the error.
err.span_suggestion_short(
colon_sp,
"use `=` if you meant to assign",
" =",
Applicability::MachineApplicable,
);
err.emit();
// As this was parsed successfully, continue as if the code has been fixed for the
// rest of the file. It will still fail due to the emitted error, but we avoid
// extra noise.
init
}
(Err(init_err), Some((snapshot, _, ty_err))) => {
// init error, ty error
init_err.cancel();
// Couldn't parse the type nor the initializer, only raise the type error and
// return to the parser state before parsing the type as the initializer.
// let x: <parse_error>;
*self = snapshot;
return Err(ty_err);
}
(Err(err), None) => {
// init error, ty parsed
// Couldn't parse the initializer and we're not attempting to recover a failed
// parse of the type, return the error.
return Err(err);
}
};
let kind = match init {
None => LocalKind::Decl,
Some(init) => {
if self.eat_keyword(kw::Else) {
if self.token.is_keyword(kw::If) {
// `let...else if`. Emit the same error that `parse_block()` would,
// but explicitly point out that this pattern is not allowed.
let msg = "conditional `else if` is not supported for `let...else`";
return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
}
let els = self.parse_block()?;
self.check_let_else_init_bool_expr(&init);
self.check_let_else_init_trailing_brace(&init);
LocalKind::InitElse(init, els)
} else {
LocalKind::Init(init)
}
}
};
let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
Ok(P(ast::Local {
ty,
pat,
kind,
id: DUMMY_NODE_ID,
span: lo.to(hi),
colon_sp,
attrs,
tokens: None,
}))
}
fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
if let ast::ExprKind::Binary(op, ..) = init.kind {
if op.node.is_lazy() {
self.dcx().emit_err(errors::InvalidExpressionInLetElse {
span: init.span,
operator: op.node.as_str(),
sugg: errors::WrapInParentheses::Expression {
left: init.span.shrink_to_lo(),
right: init.span.shrink_to_hi(),
},
});
}
}
}
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
if let Some(trailing) = classify::expr_trailing_brace(init) {
let sugg = match &trailing.kind {
ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs {
left: mac.args.dspan.open,
right: mac.args.dspan.close,
},
_ => errors::WrapInParentheses::Expression {
left: trailing.span.shrink_to_lo(),
right: trailing.span.shrink_to_hi(),
},
};
self.dcx().emit_err(errors::InvalidCurlyInLetElse {
span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
sugg,
});
}
}
/// Parses the RHS of a local variable declaration (e.g., `= 14;`).
fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> {
let eq_consumed = match self.token.kind {
token::BinOpEq(..) => {
// Recover `let x <op>= 1` as `let x = 1`
self.dcx()
.emit_err(errors::CompoundAssignmentExpressionInLet { span: self.token.span });
self.bump();
true
}
_ => self.eat(&token::Eq),
};
Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None })
}
/// Parses a block. No inner attributes are allowed.
pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
let (attrs, block) = self.parse_inner_attrs_and_block()?;
if let [.., last] = &*attrs {
self.error_on_forbidden_inner_attr(
last.span,
super::attr::InnerAttrPolicy::Forbidden(Some(
InnerAttrForbiddenReason::InCodeBlock,
)),
);
}
Ok(block)
}
fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
let sp = self.token.span;
let mut e = self.dcx().struct_span_err(sp, msg);
let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
// Check to see if the user has written something like
//
// if (cond)
// bar;
//
// which is valid in other languages, but not Rust.
match self.parse_stmt_without_recovery(false, ForceCollect::No) {
// If the next token is an open brace, e.g., we have:
//
// if expr other_expr {
// ^ ^ ^- lookahead(1) is a brace
// | |- current token is not "else"
// |- (statement we just parsed)
//
// the place-inside-a-block suggestion would be more likely wrong than right.
//
// FIXME(compiler-errors): this should probably parse an arbitrary expr and not
// just lookahead one token, so we can see if there's a brace after _that_,
// since we want to protect against:
// `if 1 1 + 1 {` being suggested as `if { 1 } 1 + 1 {`
// + +
Ok(Some(_))
if (!self.token.is_keyword(kw::Else)
&& self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)))
|| do_not_suggest_help => {}
// Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
Ok(Some(stmt)) => {
let stmt_own_line = self.psess.source_map().is_line_before_span_empty(sp);
let stmt_span = if stmt_own_line && self.eat(&token::Semi) {
// Expand the span to include the semicolon.
stmt.span.with_hi(self.prev_token.span.hi())
} else {
stmt.span
};
e.multipart_suggestion(
"try placing this code inside a block",
vec![
(stmt_span.shrink_to_lo(), "{ ".to_string()),
(stmt_span.shrink_to_hi(), " }".to_string()),
],
// Speculative; has been misleading in the past (#46836).
Applicability::MaybeIncorrect,
);
}
Err(e) => {
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
e.cancel();
}
_ => {}
}
e.span_label(sp, "expected `{`");
e
}
fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
let tok = super::token_descr(&self.token);
let msg = format!("expected `{{`, found {tok}");
Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
}
/// Parses a block. Inner attributes are allowed.
pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
self.parse_block_common(self.token.span, BlockCheckMode::Default, true)
}
/// Parses a block. Inner attributes are allowed.
pub(super) fn parse_block_common(
&mut self,
lo: Span,
blk_mode: BlockCheckMode,
can_be_struct_literal: bool,
) -> PResult<'a, (AttrVec, P<Block>)> {
maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block));
let maybe_ident = self.prev_token.clone();
self.maybe_recover_unexpected_block_label();
if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
return self.error_block_no_opening_brace();
}
let attrs = self.parse_inner_attributes()?;
let tail = match self.maybe_suggest_struct_literal(
lo,
blk_mode,
maybe_ident,
can_be_struct_literal,
) {
Some(tail) => tail?,
None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
};
Ok((attrs, tail))
}
/// Parses the rest of a block expression or function body.
/// Precondition: already parsed the '{'.
pub(crate) fn parse_block_tail(
&mut self,
lo: Span,
s: BlockCheckMode,
recover: AttemptLocalParseRecovery,
) -> PResult<'a, P<Block>> {
let mut stmts = ThinVec::new();
let mut snapshot = None;
while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
if self.token == token::Eof {
break;
}
if self.is_diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
// Account for `<<<<<<<` diff markers. We can't proactively error here because
// that can be a valid path start, so we snapshot and reparse only we've
// encountered another parse error.
snapshot = Some(self.create_snapshot_for_diagnostic());
}
let stmt = match self.parse_full_stmt(recover) {
Err(mut err) if recover.yes() => {
if let Some(ref mut snapshot) = snapshot {
snapshot.recover_diff_marker();
}
if self.token == token::Colon {
// if a previous and next token of the current one is
// integer literal (e.g. `1:42`), it's likely a range
// expression for Pythonistas and we can suggest so.
if self.prev_token.is_integer_lit()
&& self.may_recover()
&& self.look_ahead(1, |token| token.is_integer_lit())
{
// FIXME(hkmatsumoto): Might be better to trigger
// this only when parsing an index expression.
err.span_suggestion_verbose(
self.token.span,
"you might have meant a range expression",
"..",
Applicability::MaybeIncorrect,
);
} else {
// if next token is following a colon, it's likely a path
// and we can suggest a path separator
self.bump();
if self.token.span.lo() == self.prev_token.span.hi() {
err.span_suggestion_verbose(
self.prev_token.span,
"maybe write a path separator here",
"::",
Applicability::MaybeIncorrect,
);
}
if self.psess.unstable_features.is_nightly_build() {
// FIXME(Nilstrieb): Remove this again after a few months.
err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>");
}
}
}
let guar = err.emit();
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
Some(self.mk_stmt_err(self.token.span, guar))
}
Ok(stmt) => stmt,
Err(err) => return Err(err),
};
if let Some(stmt) = stmt {
stmts.push(stmt);
} else {
// Found only `;` or `}`.
continue;
};
}
Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
}
/// Parses a statement, including the trailing semicolon.
pub fn parse_full_stmt(
&mut self,
recover: AttemptLocalParseRecovery,
) -> PResult<'a, Option<Stmt>> {
// Skip looking for a trailing semicolon when we have an interpolated statement.
maybe_whole!(self, NtStmt, |stmt| Some(stmt.into_inner()));
let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else {
return Ok(None);
};
let mut eat_semi = true;
let mut add_semi_to_stmt = false;
match &mut stmt.kind {
// Expression without semicolon.
StmtKind::Expr(expr)
if classify::expr_requires_semi_to_be_stmt(expr)
&& !expr.attrs.is_empty()
&& ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)]
.contains(&self.token.kind) =>
{
// The user has written `#[attr] expr` which is unsupported. (#106020)
let guar = self.attr_on_non_tail_expr(&expr);
// We already emitted an error, so don't emit another type error
let sp = expr.span.to(self.prev_token.span);
*expr = self.mk_expr_err(sp, guar);
}
// Expression without semicolon.
StmtKind::Expr(expr)
if self.token != token::Eof && classify::expr_requires_semi_to_be_stmt(expr) =>
{
// Just check for errors and recover; do not eat semicolon yet.
let expect_result =
self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)]);
// Try to both emit a better diagnostic, and avoid further errors by replacing
// the `expr` with `ExprKind::Err`.
let replace_with_err = 'break_recover: {
match expect_result {
Ok(Recovered::No) => None,
Ok(Recovered::Yes) => {
// Skip type error to avoid extra errors.
let guar = self
.dcx()
.span_delayed_bug(self.prev_token.span, "expected `;` or `}`");
Some(guar)
}
Err(e) => {
if self.recover_colon_as_semi() {
// recover_colon_as_semi has already emitted a nicer error.
e.delay_as_bug();
add_semi_to_stmt = true;
eat_semi = false;
break 'break_recover None;
}
match &expr.kind {
ExprKind::Path(None, ast::Path { segments, .. })
if segments.len() == 1 =>
{
if self.token == token::Colon
&& self.look_ahead(1, |token| {
token.is_whole_block()
|| matches!(
token.kind,
token::Ident(
kw::For | kw::Loop | kw::While,
token::IdentIsRaw::No
) | token::OpenDelim(Delimiter::Brace)
)
})
{
let snapshot = self.create_snapshot_for_diagnostic();
let label = Label {
ident: Ident::from_str_and_span(
&format!("'{}", segments[0].ident),
segments[0].ident.span,
),
};
match self.parse_expr_labeled(label, false) {
Ok(labeled_expr) => {
e.cancel();
self.dcx().emit_err(MalformedLoopLabel {
span: label.ident.span,
correct_label: label.ident,
});
*expr = labeled_expr;
break 'break_recover None;
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
}
}
}
}
_ => {}
}
let res =
self.check_mistyped_turbofish_with_multiple_type_params(e, expr);
Some(if recover.no() {
res?
} else {
res.unwrap_or_else(|e| {
let guar = e.emit();
self.recover_stmt();
guar
})
})
}
}
};
if let Some(guar) = replace_with_err {
// We already emitted an error, so don't emit another type error
let sp = expr.span.to(self.prev_token.span);
*expr = self.mk_expr_err(sp, guar);
}
}
StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
StmtKind::Let(local) if let Err(mut e) = self.expect_semi() => {
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
match &mut local.kind {
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
// We found `foo<bar, baz>`, have we fully recovered?
self.expect_semi()?;
}
LocalKind::Decl => {
if let Some(colon_sp) = local.colon_sp {
e.span_label(
colon_sp,
format!(
"while parsing the type for {}",
local.pat.descr().map_or_else(
|| "the binding".to_string(),
|n| format!("`{n}`")
)
),
);
let suggest_eq = if self.token.kind == token::Dot
&& let _ = self.bump()
&& let mut snapshot = self.create_snapshot_for_diagnostic()
&& let Ok(_) = snapshot
.parse_dot_suffix_expr(
colon_sp,
self.mk_expr_err(
colon_sp,
self.dcx()
.delayed_bug("error during `:` -> `=` recovery"),
),
)
.map_err(Diag::cancel)
{
true
} else if let Some(op) = self.check_assoc_op()
&& op.node.can_continue_expr_unambiguously()
{
true
} else {
false
};
if suggest_eq {
e.span_suggestion_short(
colon_sp,
"use `=` if you meant to assign",
"=",
Applicability::MaybeIncorrect,
);
}
}
return Err(e);
}
}
eat_semi = false;
}
StmtKind::Empty | StmtKind::Item(_) | StmtKind::Let(_) | StmtKind::Semi(_) => {
eat_semi = false
}
}
if add_semi_to_stmt || (eat_semi && self.eat(&token::Semi)) {
stmt = stmt.add_trailing_semicolon();
}
stmt.span = stmt.span.to(self.prev_token.span);
Ok(Some(stmt))
}
pub(super) fn mk_block(
&self,
stmts: ThinVec<Stmt>,
rules: BlockCheckMode,
span: Span,
) -> P<Block> {
P(Block {
stmts,
id: DUMMY_NODE_ID,
rules,
span,
tokens: None,
could_be_bare_literal: false,
})
}
pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {
Stmt { id: DUMMY_NODE_ID, kind, span }
}
pub(super) fn mk_stmt_err(&self, span: Span, guar: ErrorGuaranteed) -> Stmt {
self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span, guar)))
}
pub(super) fn mk_block_err(&self, span: Span, guar: ErrorGuaranteed) -> P<Block> {
self.mk_block(thin_vec![self.mk_stmt_err(span, guar)], BlockCheckMode::Default, span)
}
}