about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-01-12 01:01:06 +0000
committerbors <bors@rust-lang.org>2017-01-12 01:01:06 +0000
commit1ca100d0428985f916eea153886762bed3909771 (patch)
treee8610f263985913c19297202fbd0b1f1c5a419b3
parentb27c7095606ad9386a7038d7af03abd7f948f02f (diff)
parentd723e02dfc94e8ab2e8d3cb7930febeed0a7651c (diff)
downloadrust-1ca100d0428985f916eea153886762bed3909771.tar.gz
rust-1ca100d0428985f916eea153886762bed3909771.zip
Auto merge of #38605 - estebank:fix-38371, r=nikomatsakis
Suggest solutions for `fn foo(&foo: Foo)`

For a given file:

```rust
struct Foo {}

fn foo(&foo: Foo) {}
```

suggest:

```
error[E0308]: mismatched types
 --> file.rs:3:8
  |
3 | fn foo(&foo: Foo) {}
  |        ^^^^ expected struct `Foo`, found reference
  |
  = note: expected type `Foo`
  = note:    found type `&_`
  = help: did you mean `foo: &Foo`?
```

Fix #38371.
-rw-r--r--src/librustc_typeck/check/_match.rs31
-rw-r--r--src/librustc_typeck/check/demand.rs18
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/librustc_typeck/check/wfcheck.rs4
-rw-r--r--src/test/ui/mismatched_types/issue-38371.rs37
-rw-r--r--src/test/ui/mismatched_types/issue-38371.stderr36
6 files changed, 120 insertions, 8 deletions
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 0fa9062df45..99c664cebea 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -27,9 +27,17 @@ use syntax_pos::Span;
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) {
+        self.check_pat_arg(pat, expected, false);
+    }
+
+    /// The `is_arg` argument indicates whether this pattern is the
+    /// *outermost* pattern in an argument (e.g., in `fn foo(&x:
+    /// &u32)`, it is true for the `&x` pattern but not `x`). This is
+    /// used to tailor error reporting.
+    pub fn check_pat_arg(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>, is_arg: bool) {
         let tcx = self.tcx;
 
-        debug!("check_pat(pat={:?},expected={:?})", pat, expected);
+        debug!("check_pat(pat={:?},expected={:?},is_arg={})", pat, expected, is_arg);
 
         let ty = match pat.node {
             PatKind::Wild => {
@@ -202,6 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     // can, to avoid creating needless variables.  This
                     // also helps with the bad interactions of the given
                     // hack detailed in (*) below.
+                    debug!("check_pat_arg: expected={:?}", expected);
                     let (rptr_ty, inner_ty) = match expected.sty {
                         ty::TyRef(_, mt) if mt.mutbl == mutbl => {
                             (expected, mt.ty)
@@ -212,7 +221,25 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl };
                             let region = self.next_region_var(infer::PatternRegion(pat.span));
                             let rptr_ty = tcx.mk_ref(region, mt);
-                            self.demand_eqtype(pat.span, expected, rptr_ty);
+                            debug!("check_pat_arg: demanding {:?} = {:?}", expected, rptr_ty);
+                            let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty);
+
+                            // Look for a case like `fn foo(&foo: u32)` and suggest
+                            // `fn foo(foo: &u32)`
+                            if let Some(mut err) = err {
+                                if is_arg {
+                                    if let PatKind::Binding(..) = inner.node {
+                                        if let Ok(snippet) = self.sess().codemap()
+                                                                        .span_to_snippet(pat.span)
+                                        {
+                                            err.help(&format!("did you mean `{}: &{}`?",
+                                                              &snippet[1..],
+                                                              expected));
+                                        }
+                                    }
+                                }
+                                err.emit();
+                            }
                             (rptr_ty, inner_ty)
                         }
                     };
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 3bc3d1a2c97..a77887cd221 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -19,6 +19,7 @@ use syntax_pos::{self, Span};
 use rustc::hir;
 use rustc::hir::def::Def;
 use rustc::ty::{self, AssociatedItem};
+use errors::DiagnosticBuilder;
 
 use super::method::probe;
 
@@ -38,20 +39,29 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     }
 
     pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
-        self.demand_eqtype_with_origin(&self.misc(sp), expected, actual);
+        if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
+            err.emit();
+        }
+    }
+
+    pub fn demand_eqtype_diag(&self,
+                             sp: Span,
+                             expected: Ty<'tcx>,
+                             actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
+        self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
     }
 
     pub fn demand_eqtype_with_origin(&self,
                                      cause: &ObligationCause<'tcx>,
                                      expected: Ty<'tcx>,
-                                     actual: Ty<'tcx>)
-    {
+                                     actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
         match self.eq_types(false, cause, actual, expected) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
+                None
             },
             Err(e) => {
-                self.report_mismatched_types(cause, expected, actual, e).emit();
+                Some(self.report_mismatched_types(cause, expected, actual, e))
             }
         }
     }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index ceb97c63d13..0b316c5d375 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -796,7 +796,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
         fcx.register_old_wf_obligation(arg_ty, arg.pat.span, traits::MiscObligation);
 
         // Check the pattern.
-        fcx.check_pat(&arg.pat, arg_ty);
+        fcx.check_pat_arg(&arg.pat, arg_ty, true);
         fcx.write_ty(arg.id, arg_ty);
     }
 
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index c80db7fa4d0..4c124cdd60c 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -505,7 +505,9 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> {
         debug!("check_method_receiver: receiver ty = {:?}", rcvr_ty);
 
         let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
-        fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty);
+        if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, rcvr_ty, self_arg_ty) {
+            err.emit();
+        }
     }
 
     fn check_variances_for_type_defn(&self,
diff --git a/src/test/ui/mismatched_types/issue-38371.rs b/src/test/ui/mismatched_types/issue-38371.rs
new file mode 100644
index 00000000000..cf66330017f
--- /dev/null
+++ b/src/test/ui/mismatched_types/issue-38371.rs
@@ -0,0 +1,37 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#![feature(slice_patterns)]
+
+
+struct Foo {
+}
+
+fn foo(&foo: Foo) {
+}
+
+fn bar(foo: Foo) {
+}
+
+fn qux(foo: &Foo) {
+}
+
+fn zar(&foo: &Foo) {
+}
+
+fn agh(&&bar: &u32) {
+}
+
+fn bgh(&&bar: u32) {
+}
+
+fn ugh(&[bar]: &u32) {
+}
+
+fn main() {}
diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr
new file mode 100644
index 00000000000..b0e56094fcf
--- /dev/null
+++ b/src/test/ui/mismatched_types/issue-38371.stderr
@@ -0,0 +1,36 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-38371.rs:16:8
+   |
+16 | fn foo(&foo: Foo) {
+   |        ^^^^ expected struct `Foo`, found reference
+   |
+   = note: expected type `Foo`
+   = note:    found type `&_`
+   = help: did you mean `foo: &Foo`?
+
+error[E0308]: mismatched types
+  --> $DIR/issue-38371.rs:28:9
+   |
+28 | fn agh(&&bar: &u32) {
+   |         ^^^^ expected u32, found reference
+   |
+   = note: expected type `u32`
+   = note:    found type `&_`
+
+error[E0308]: mismatched types
+  --> $DIR/issue-38371.rs:31:8
+   |
+31 | fn bgh(&&bar: u32) {
+   |        ^^^^^ expected u32, found reference
+   |
+   = note: expected type `u32`
+   = note:    found type `&_`
+
+error[E0529]: expected an array or slice, found `u32`
+  --> $DIR/issue-38371.rs:34:9
+   |
+34 | fn ugh(&[bar]: &u32) {
+   |         ^^^^^ pattern cannot match with input type `u32`
+
+error: aborting due to 4 previous errors
+