Skip to content

Commit

Permalink
Detect more cases of = to : typo
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
estebank committed Mar 1, 2024
1 parent c475e23 commit bde2dfb
Show file tree
Hide file tree
Showing 17 changed files with 146 additions and 57 deletions.
13 changes: 12 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,16 @@ impl Pat {
});
contains_never_pattern
}

/// Return a name suitable for diagnostics.
pub fn descr(&self) -> Option<String> {
match &self.kind {
PatKind::Wild => Some("_".to_string()),
PatKind::Ident(BindingAnnotation::NONE, ident, None) => Some(format!("{ident}")),
PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())),
_ => None,
}
}
}

/// A single field in a struct pattern.
Expand Down Expand Up @@ -1052,6 +1062,7 @@ pub struct Local {
pub ty: Option<P<Ty>>,
pub kind: LocalKind,
pub span: Span,
pub colon_sp: Option<Span>,
pub attrs: AttrVec,
pub tokens: Option<LazyAttrTokenStream>,
}
Expand Down Expand Up @@ -3325,7 +3336,7 @@ mod size_asserts {
static_assert_size!(Item, 136);
static_assert_size!(ItemKind, 64);
static_assert_size!(LitKind, 24);
static_assert_size!(Local, 72);
static_assert_size!(Local, 80);
static_assert_size!(MetaItemLit, 40);
static_assert_size!(Param, 40);
static_assert_size!(Pat, 72);
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
}

pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
let Local { id, pat, ty, kind, span, attrs, tokens } = local.deref_mut();
let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
vis.visit_id(id);
vis.visit_pat(pat);
visit_opt(ty, |ty| vis.visit_ty(ty));
Expand All @@ -624,6 +624,7 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
}
}
vis.visit_span(span);
visit_opt(colon_sp, |sp| vis.visit_span(sp));
visit_attrs(attrs, vis);
visit_lazy_tts(tokens, vis);
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ impl<'a> ExtCtxt<'a> {
id: ast::DUMMY_NODE_ID,
kind: LocalKind::Init(ex),
span: sp,
colon_sp: None,
attrs: AttrVec::new(),
tokens: None,
});
Expand Down Expand Up @@ -194,6 +195,7 @@ impl<'a> ExtCtxt<'a> {
id: ast::DUMMY_NODE_ID,
kind: LocalKind::Init(ex),
span: sp,
colon_sp: None,
attrs: AttrVec::new(),
tokens: None,
});
Expand All @@ -208,6 +210,7 @@ impl<'a> ExtCtxt<'a> {
id: ast::DUMMY_NODE_ID,
kind: LocalKind::Decl,
span,
colon_sp: None,
attrs: AttrVec::new(),
tokens: None,
});
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 `>`.
(
Expand Down Expand Up @@ -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 }) => {
Expand Down
74 changes: 64 additions & 10 deletions compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) => {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -750,15 +764,55 @@ 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, _) => {
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
// 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;
}
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/const-generics/bad-const-generic-exprs.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ error: expected one of `,` or `>`, found `0`
--> $DIR/bad-const-generic-exprs.rs:43:17
|
LL | let _: Wow<!0>;
| - ^ expected one of `,` or `>`
| |
| while parsing the type for `_`
| - ^ expected one of `,` or `>`
| |
| while parsing the type for `_`
|
help: you might have meant to end the type parameters here
|
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/issues/issue-34334.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ error: expected one of `,`, `:`, or `>`, found `=`
--> $DIR/issue-34334.rs:2:29
|
LL | let sr: Vec<(u32, _, _) = vec![];
| -- ^ expected one of `,`, `:`, or `>`
| |
| while parsing the type for `sr`
| - ^ expected one of `,`, `:`, or `>`
| |
| while parsing the type for `sr`
|
help: you might have meant to end the type parameters here
|
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/parser/better-expected.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `]`, found `3`
--> $DIR/better-expected.rs:2:19
|
LL | let x: [isize 3];
| - ^ expected one of 7 possible tokens
| |
| while parsing the type for `x`
| - ^ expected one of 7 possible tokens
| |
| while parsing the type for `x`

error: aborting due to 1 previous error

14 changes: 7 additions & 7 deletions tests/ui/parser/issues/issue-84117.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ error: expected one of `>`, a const expression, lifetime, or type, found `}`
--> $DIR/issue-84117.rs:2:67
|
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
| ----------- ^ expected one of `>`, a const expression, lifetime, or type
| |
| while parsing the type for `inner_local`
| - ^ expected one of `>`, a const expression, lifetime, or type
| |
| while parsing the type for `inner_local`
|
help: you might have meant to end the type parameters here
|
Expand All @@ -25,7 +25,7 @@ error: expected one of `,` or `>`, found `}`
--> $DIR/issue-84117.rs:8:1
|
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
| ----------- while parsing the type for `outer_local` - expected one of `,` or `>`
| - while parsing the type for `outer_local` - expected one of `,` or `>`
...
LL | }
| ^ unexpected token
Expand All @@ -43,9 +43,9 @@ error: expected one of `>`, a const expression, lifetime, or type, found `}`
--> $DIR/issue-84117.rs:2:67
|
LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
| ----------- ^ expected one of `>`, a const expression, lifetime, or type
| |
| while parsing the type for `inner_local`
| - ^ expected one of `>`, a const expression, lifetime, or type
| |
| while parsing the type for `inner_local`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: you might have meant to end the type parameters here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ error: expected one of `,`, `:`, or `>`, found `=`
--> $DIR/missing-closing-angle-bracket-eq-constraint.rs:7:23
|
LL | let v : Vec<(u32,_) = vec![];
| - ^ expected one of `,`, `:`, or `>`
| |
| while parsing the type for `v`
| - ^ expected one of `,`, `:`, or `>`
| |
| while parsing the type for `v`
|
help: you might have meant to end the type parameters here
|
Expand All @@ -15,9 +15,9 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `{`
--> $DIR/missing-closing-angle-bracket-eq-constraint.rs:13:32
|
LL | let foo : Foo::<T1, T2 = Foo {_a : arg1, _b : arg2};
| --- ^ expected one of 7 possible tokens
| |
| while parsing the type for `foo`
| - ^ expected one of 7 possible tokens
| |
| while parsing the type for `foo`
|
help: you might have meant to end the type parameters here
|
Expand All @@ -28,9 +28,9 @@ error: expected one of `,`, `:`, or `>`, found `=`
--> $DIR/missing-closing-angle-bracket-eq-constraint.rs:18:18
|
LL | let v : Vec<'a = vec![];
| - ^ expected one of `,`, `:`, or `>`
| |
| while parsing the type for `v`
| - ^ expected one of `,`, `:`, or `>`
| |
| while parsing the type for `v`
|
help: you might have meant to end the type parameters here
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: expected one of `,` or `>`, found `;`
--> $DIR/nested-missing-closing-angle-bracket.rs:2:46
|
LL | let v : Vec::<Vec<(u32,_,_)> = vec![vec![]];
| - while parsing the type for `v` ^ expected one of `,` or `>`
| - while parsing the type for `v` ^ expected one of `,` or `>`

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
let _: std::env::temp_dir().join("foo"); //~ ERROR expected one of
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: expected one of `!`, `+`, `->`, `::`, `;`, or `=`, found `.`
--> $DIR/recover-colon-instead-of-eq-in-local.rs:2:32
|
LL | let _: std::env::temp_dir().join("foo");
| - ^ expected one of `!`, `+`, `->`, `::`, `;`, or `=`
| |
| while parsing the type for `_`
| help: use `=` if you meant to assign

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ error: expected one of `->`, `;`, or `=`, found `~`
--> $DIR/removed-syntax-fn-sigil.rs:2:14
|
LL | let x: fn~() = || ();
| ^ expected one of `->`, `;`, or `=`
| - ^ expected one of `->`, `;`, or `=`
| |
| while parsing the type for `x`

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `@`
--> $DIR/nested-type-ascription-syntactically-invalid.rs:18:15
|
LL | let a: u8 @ b = 0;
| ^ expected one of 7 possible tokens
| - ^ expected one of 7 possible tokens
| |
| while parsing the type for `a`

error: expected one of `)`, `,`, `@`, or `|`, found `:`
--> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15
Expand All @@ -16,7 +18,9 @@ error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `@`
--> $DIR/nested-type-ascription-syntactically-invalid.rs:30:15
|
LL | let a: T1 @ Outer(b: T2);
| ^ expected one of 7 possible tokens
| - ^ expected one of 7 possible tokens
| |
| while parsing the type for `a`

error: aborting due to 3 previous errors

Loading

0 comments on commit bde2dfb

Please sign in to comment.