Detect more cases of =
to :
typo
When a `Local` is fully parsed, but not followed by a `;`, keep the `:` span arround and mention it. If the type could continue being parsed as an expression, suggest replacing the `:` with a `=`. ``` error: expected one of `!`, `+`, `->`, `::`, `;`, or `=`, found `.` --> file.rs:2:32 | 2 | let _: std::env::temp_dir().join("foo"); | - ^ expected one of `!`, `+`, `->`, `::`, `;`, or `=` | | | while parsing the type for `_` | help: use `=` if you meant to assign ``` Fix #119665.
This commit is contained in:
parent
c475e2303b
commit
bde2dfb127
17 changed files with 146 additions and 57 deletions
|
@ -430,7 +430,7 @@ impl<'a> Parser<'a> {
|
|||
/// The method does not advance the current token.
|
||||
///
|
||||
/// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively.
|
||||
fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
|
||||
pub fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
|
||||
let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) {
|
||||
// When parsing const expressions, stop parsing when encountering `>`.
|
||||
(
|
||||
|
@ -994,7 +994,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
|
||||
pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
|
||||
match self.token.uninterpolate().kind {
|
||||
token::Ident(..) => self.parse_dot_suffix(base, lo),
|
||||
token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => {
|
||||
|
|
|
@ -294,17 +294,22 @@ impl<'a> Parser<'a> {
|
|||
let (pat, colon) =
|
||||
self.parse_pat_before_ty(None, RecoverComma::Yes, PatternLocation::LetBinding)?;
|
||||
|
||||
let (err, ty) = if colon {
|
||||
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)),
|
||||
Ok(ty) => (None, Some(ty), Some(colon_sp)),
|
||||
Err(mut err) => {
|
||||
if let Ok(snip) = self.span_to_snippet(pat.span) {
|
||||
err.span_label(pat.span, format!("while parsing the type for `{snip}`"));
|
||||
}
|
||||
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
|
||||
|
@ -317,11 +322,11 @@ impl<'a> Parser<'a> {
|
|||
mem::replace(self, parser_snapshot_before_type);
|
||||
Some((parser_snapshot_after_type, colon_sp, err))
|
||||
};
|
||||
(err, None)
|
||||
(err, None, Some(colon_sp))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
(None, None, None)
|
||||
};
|
||||
let init = match (self.parse_initializer(err.is_some()), err) {
|
||||
(Ok(init), None) => {
|
||||
|
@ -380,7 +385,16 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
};
|
||||
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), attrs, tokens: None }))
|
||||
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) {
|
||||
|
@ -750,7 +764,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
|
||||
StmtKind::Local(local) if let Err(e) = self.expect_semi() => {
|
||||
StmtKind::Local(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, _) => {
|
||||
|
@ -758,7 +772,47 @@ impl<'a> Parser<'a> {
|
|||
// We found `foo<bar, baz>`, have we fully recovered?
|
||||
self.expect_semi()?;
|
||||
}
|
||||
LocalKind::Decl => return Err(e),
|
||||
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"),
|
||||
),
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue