diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2024-02-27 00:48:32 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2024-03-01 02:03:00 +0000 |
| commit | bde2dfb127a524321d6629607df5f3008921327e (patch) | |
| tree | f253a5db3a5863c1fe223a954be357555c4bca1d /compiler/rustc_parse/src/parser | |
| parent | c475e2303b551d726307c646181e0677af1e0069 (diff) | |
| download | rust-bde2dfb127a524321d6629607df5f3008921327e.tar.gz rust-bde2dfb127a524321d6629607df5f3008921327e.zip | |
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.
Diffstat (limited to 'compiler/rustc_parse/src/parser')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 74 |
2 files changed, 66 insertions, 12 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f5a7bfd42ff..dad43181586 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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 }) => { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 9fe74e80a2b..65c0f7d49c1 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -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; } |
