about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2019-10-18 10:33:25 -0700
committerEsteban Küber <esteban@kuber.com.ar>2019-10-26 15:26:08 -0700
commit6206a5a1b42072dfad10f923c68c66e552aa1c49 (patch)
tree4ca88b2e4ed349a33ff0f8a21374ae1febdb774c
parent46e6c533d08a2c6d22083a2756a0b569e001c3c4 (diff)
downloadrust-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.rs15
-rw-r--r--src/librustc_resolve/late/diagnostics.rs25
-rw-r--r--src/test/ui/suggestions/let-binding-init-expr-as-ty.rs11
-rw-r--r--src/test/ui/suggestions/let-binding-init-expr-as-ty.stderr41
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`.