Use heuristics to suggest assignment
When detecting a possible `=` -> `:` typo in a `let` binding, suggest assigning instead of setting the type.
This commit is contained in:
parent
46e6c533d0
commit
6206a5a1b4
4 changed files with 89 additions and 3 deletions
|
@ -350,6 +350,9 @@ struct LateResolutionVisitor<'a, 'b> {
|
|||
|
||||
/// Only used for better errors on `fn(): fn()`.
|
||||
current_type_ascription: Vec<Span>,
|
||||
|
||||
/// Only used for better errors on `let <pat>: <expr, not type>;`.
|
||||
current_let_binding: Option<(Span, Span)>,
|
||||
}
|
||||
|
||||
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
|
||||
|
@ -373,7 +376,18 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
|
|||
self.resolve_expr(expr, None);
|
||||
}
|
||||
fn visit_local(&mut self, local: &'tcx Local) {
|
||||
debug!("visit_local {:?} {:?} {:?}", local, local.pat, local.pat.kind);
|
||||
let val = match local {
|
||||
Local { pat, ty: Some(ty), init: None, .. } => match pat.kind {
|
||||
// We check for this to avoid tuple struct fields.
|
||||
PatKind::Wild => None,
|
||||
_ => Some((pat.span, ty.span)),
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
let original = replace(&mut self.current_let_binding, val);
|
||||
self.resolve_local(local);
|
||||
self.current_let_binding = original;
|
||||
}
|
||||
fn visit_ty(&mut self, ty: &'tcx Ty) {
|
||||
match ty.kind {
|
||||
|
@ -533,6 +547,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
|
|||
current_function: None,
|
||||
unused_labels: Default::default(),
|
||||
current_type_ascription: Vec::new(),
|
||||
current_let_binding: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,10 +72,14 @@ impl<'a> LateResolutionVisitor<'a, '_> {
|
|||
let path_str = Segment::names_to_string(path);
|
||||
let item_str = path.last().unwrap().ident;
|
||||
let code = source.error_code(res.is_some());
|
||||
let (base_msg, fallback_label, base_span) = if let Some(res) = res {
|
||||
let (base_msg, fallback_label, base_span, is_local) = if let Some(res) = res {
|
||||
(format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
|
||||
format!("not a {}", expected),
|
||||
span)
|
||||
span,
|
||||
match res {
|
||||
Res::Local(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
let item_span = path.last().unwrap().ident.span;
|
||||
let (mod_prefix, mod_str) = if path.len() == 1 {
|
||||
|
@ -94,7 +98,8 @@ impl<'a> LateResolutionVisitor<'a, '_> {
|
|||
};
|
||||
(format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
|
||||
format!("not found in {}", mod_str),
|
||||
item_span)
|
||||
item_span,
|
||||
false)
|
||||
};
|
||||
|
||||
let code = DiagnosticId::Error(code.into());
|
||||
|
@ -257,6 +262,20 @@ impl<'a> LateResolutionVisitor<'a, '_> {
|
|||
if !levenshtein_worked {
|
||||
err.span_label(base_span, fallback_label);
|
||||
self.type_ascription_suggestion(&mut err, base_span);
|
||||
if let Some(span) = self.current_let_binding.and_then(|(pat_sp, ty_sp)| {
|
||||
if ty_sp.contains(base_span) && is_local {
|
||||
Some(pat_sp.between(ty_sp))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"maybe you meant to assign a value instead of defining this let binding's type",
|
||||
" = ".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
(err, candidates)
|
||||
}
|
||||
|
|
11
src/test/ui/suggestions/let-binding-init-expr-as-ty.rs
Normal file
11
src/test/ui/suggestions/let-binding-init-expr-as-ty.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
pub fn foo(num: i32) -> i32 { //~ ERROR mismatched types
|
||||
let foo: i32::from_be(num);
|
||||
//~^ ERROR expected type, found local variable `num`
|
||||
//~| ERROR parenthesized type parameters may only be used with a `Fn` trait
|
||||
//~| ERROR ambiguous associated type
|
||||
//~| WARNING this was previously accepted by the compiler but is being phased out
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = foo(42);
|
||||
}
|
41
src/test/ui/suggestions/let-binding-init-expr-as-ty.stderr
Normal file
41
src/test/ui/suggestions/let-binding-init-expr-as-ty.stderr
Normal file
|
@ -0,0 +1,41 @@
|
|||
error[E0573]: expected type, found local variable `num`
|
||||
--> $DIR/let-binding-init-expr-as-ty.rs:2:27
|
||||
|
|
||||
LL | let foo: i32::from_be(num);
|
||||
| ^^^ not a type
|
||||
help: maybe you meant to assign a value instead of defining this let binding's type
|
||||
|
|
||||
LL | let foo = i32::from_be(num);
|
||||
| ^
|
||||
|
||||
error: parenthesized type parameters may only be used with a `Fn` trait
|
||||
--> $DIR/let-binding-init-expr-as-ty.rs:2:19
|
||||
|
|
||||
LL | let foo: i32::from_be(num);
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[deny(parenthesized_params_in_types_and_modules)]` on by default
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #42238 <https://github.com/rust-lang/rust/issues/42238>
|
||||
|
||||
error[E0223]: ambiguous associated type
|
||||
--> $DIR/let-binding-init-expr-as-ty.rs:2:14
|
||||
|
|
||||
LL | let foo: i32::from_be(num);
|
||||
| ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<i32 as Trait>::from_be`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/let-binding-init-expr-as-ty.rs:1:25
|
||||
|
|
||||
LL | pub fn foo(num: i32) -> i32 {
|
||||
| --- ^^^ expected i32, found ()
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `()`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0223, E0308, E0573.
|
||||
For more information about an error, try `rustc --explain E0223`.
|
Loading…
Add table
Add a link
Reference in a new issue