diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2019-10-18 10:33:25 -0700 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2019-10-26 15:26:08 -0700 |
| commit | 6206a5a1b42072dfad10f923c68c66e552aa1c49 (patch) | |
| tree | 4ca88b2e4ed349a33ff0f8a21374ae1febdb774c | |
| parent | 46e6c533d08a2c6d22083a2756a0b569e001c3c4 (diff) | |
| download | rust-6206a5a1b42072dfad10f923c68c66e552aa1c49.tar.gz rust-6206a5a1b42072dfad10f923c68c66e552aa1c49.zip | |
Use heuristics to suggest assignment
When detecting a possible `=` -> `:` typo in a `let` binding, suggest assigning instead of setting the type.
| -rw-r--r-- | src/librustc_resolve/late.rs | 15 | ||||
| -rw-r--r-- | src/librustc_resolve/late/diagnostics.rs | 25 | ||||
| -rw-r--r-- | src/test/ui/suggestions/let-binding-init-expr-as-ty.rs | 11 | ||||
| -rw-r--r-- | src/test/ui/suggestions/let-binding-init-expr-as-ty.stderr | 41 |
4 files changed, 89 insertions, 3 deletions
diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 136ab1f0444..048d4cefa57 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -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, } } diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index 2721df4c687..1912649f8b5 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -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) } diff --git a/src/test/ui/suggestions/let-binding-init-expr-as-ty.rs b/src/test/ui/suggestions/let-binding-init-expr-as-ty.rs new file mode 100644 index 00000000000..4b572d6255b --- /dev/null +++ b/src/test/ui/suggestions/let-binding-init-expr-as-ty.rs @@ -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); +} diff --git a/src/test/ui/suggestions/let-binding-init-expr-as-ty.stderr b/src/test/ui/suggestions/let-binding-init-expr-as-ty.stderr new file mode 100644 index 00000000000..b472c267987 --- /dev/null +++ b/src/test/ui/suggestions/let-binding-init-expr-as-ty.stderr @@ -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`. |
