Skip to content

Commit f293eb4

Browse files
committed
Use heuristics to suggest assignment
When detecting a possible `=` -> `:` typo in a `let` binding, suggest assigning instead of setting the type.
1 parent e5b8c11 commit f293eb4

File tree

4 files changed

+89
-3
lines changed

4 files changed

+89
-3
lines changed

src/librustc_resolve/late.rs

+15
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,9 @@ struct LateResolutionVisitor<'a, 'b> {
354354

355355
/// Only used for better errors on `fn(): fn()`.
356356
current_type_ascription: Vec<Span>,
357+
358+
/// Only used for better errors on `let <pat>: <expr, not type>;`.
359+
current_let_binding: Option<(Span, Span)>,
357360
}
358361

359362
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
@@ -377,7 +380,18 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
377380
self.resolve_expr(expr, None);
378381
}
379382
fn visit_local(&mut self, local: &'tcx Local) {
383+
debug!("visit_local {:?} {:?} {:?}", local, local.pat, local.pat.kind);
384+
let val = match local {
385+
Local { pat, ty: Some(ty), init: None, .. } => match pat.kind {
386+
// We check for this to avoid tuple struct fields.
387+
PatKind::Wild => None,
388+
_ => Some((pat.span, ty.span)),
389+
},
390+
_ => None,
391+
};
392+
let original = replace(&mut self.current_let_binding, val);
380393
self.resolve_local(local);
394+
self.current_let_binding = original;
381395
}
382396
fn visit_ty(&mut self, ty: &'tcx Ty) {
383397
match ty.kind {
@@ -554,6 +568,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
554568
current_function: None,
555569
unused_labels: Default::default(),
556570
current_type_ascription: Vec::new(),
571+
current_let_binding: None,
557572
}
558573
}
559574

src/librustc_resolve/late/diagnostics.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,14 @@ impl<'a> LateResolutionVisitor<'a, '_> {
7272
let path_str = Segment::names_to_string(path);
7373
let item_str = path.last().unwrap().ident;
7474
let code = source.error_code(res.is_some());
75-
let (base_msg, fallback_label, base_span) = if let Some(res) = res {
75+
let (base_msg, fallback_label, base_span, is_local) = if let Some(res) = res {
7676
(format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
7777
format!("not a {}", expected),
78-
span)
78+
span,
79+
match res {
80+
Res::Local(_) => true,
81+
_ => false,
82+
})
7983
} else {
8084
let item_span = path.last().unwrap().ident.span;
8185
let (mod_prefix, mod_str) = if path.len() == 1 {
@@ -94,7 +98,8 @@ impl<'a> LateResolutionVisitor<'a, '_> {
9498
};
9599
(format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
96100
format!("not found in {}", mod_str),
97-
item_span)
101+
item_span,
102+
false)
98103
};
99104

100105
let code = DiagnosticId::Error(code.into());
@@ -257,6 +262,20 @@ impl<'a> LateResolutionVisitor<'a, '_> {
257262
if !levenshtein_worked {
258263
err.span_label(base_span, fallback_label);
259264
self.type_ascription_suggestion(&mut err, base_span);
265+
if let Some(span) = self.current_let_binding.and_then(|(pat_sp, ty_sp)| {
266+
if ty_sp.contains(base_span) && is_local {
267+
Some(pat_sp.between(ty_sp))
268+
} else {
269+
None
270+
}
271+
}) {
272+
err.span_suggestion(
273+
span,
274+
"maybe you meant to assign a value instead of defining this let binding's type",
275+
" = ".to_string(),
276+
Applicability::MaybeIncorrect,
277+
);
278+
}
260279
}
261280
(err, candidates)
262281
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pub fn foo(num: i32) -> i32 { //~ ERROR mismatched types
2+
let foo: i32::from_be(num);
3+
//~^ ERROR expected type, found local variable `num`
4+
//~| ERROR parenthesized type parameters may only be used with a `Fn` trait
5+
//~| ERROR ambiguous associated type
6+
//~| WARNING this was previously accepted by the compiler but is being phased out
7+
}
8+
9+
fn main() {
10+
let _ = foo(42);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error[E0573]: expected type, found local variable `num`
2+
--> $DIR/let-binding-init-expr-as-ty.rs:2:27
3+
|
4+
LL | let foo: i32::from_be(num);
5+
| ^^^ not a type
6+
help: maybe you meant to assign a value instead of defining this let binding's type
7+
|
8+
LL | let foo = i32::from_be(num);
9+
| ^
10+
11+
error: parenthesized type parameters may only be used with a `Fn` trait
12+
--> $DIR/let-binding-init-expr-as-ty.rs:2:19
13+
|
14+
LL | let foo: i32::from_be(num);
15+
| ^^^^^^^^^^^^
16+
|
17+
= note: `#[deny(parenthesized_params_in_types_and_modules)]` on by default
18+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
19+
= note: for more information, see issue #42238 <https://github.com/rust-lang/rust/issues/42238>
20+
21+
error[E0223]: ambiguous associated type
22+
--> $DIR/let-binding-init-expr-as-ty.rs:2:14
23+
|
24+
LL | let foo: i32::from_be(num);
25+
| ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<i32 as Trait>::from_be`
26+
27+
error[E0308]: mismatched types
28+
--> $DIR/let-binding-init-expr-as-ty.rs:1:25
29+
|
30+
LL | pub fn foo(num: i32) -> i32 {
31+
| --- ^^^ expected i32, found ()
32+
| |
33+
| implicitly returns `()` as its body has no tail or `return` expression
34+
|
35+
= note: expected type `i32`
36+
found type `()`
37+
38+
error: aborting due to 4 previous errors
39+
40+
Some errors have detailed explanations: E0223, E0308, E0573.
41+
For more information about an error, try `rustc --explain E0223`.

0 commit comments

Comments
 (0)