about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorsinkuu <sinkuu@sinkuu.xyz>2017-10-07 15:12:56 +0900
committersinkuu <sinkuu@sinkuu.xyz>2017-10-07 15:12:56 +0900
commitc74bda1075757cd00f72a918e2d7fdaf47f85c2d (patch)
treed28384914262e40c2552d66b129c54df45a84869 /src
parentb6eef82c684268bd719d3ced4f4cca69b014aaff (diff)
Add suggestion
Diffstat (limited to 'src')
-rw-r--r--src/librustc/traits/error_reporting.rs131
-rw-r--r--src/test/ui/mismatched_types/closure-arg-count.rs2
-rw-r--r--src/test/ui/mismatched_types/closure-arg-count.stderr32
3 files changed, 126 insertions, 39 deletions
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 115782ca2ad..5056876f3ad 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -764,23 +764,51 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     // //    found_trait_ref:  std::ops::FnMut<(&i32,)>
                     // ```
 
-                    let closure_args_span = found_did.and_then(|did| self.tcx.hir.get_if_local(did))
+                    let (closure_span, closure_args) = found_did
+                        .and_then(|did| self.tcx.hir.get_if_local(did))
                         .and_then(|node| {
                             if let hir::map::NodeExpr(
-                                &hir::Expr { node: hir::ExprClosure(_, _, _, span, _), .. }) = node
+                                &hir::Expr {
+                                    node: hir::ExprClosure(_, ref decl, id, span, _),
+                                    ..
+                                }) = node
                             {
-                                Some(span)
+                                let ty_snips = decl.inputs.iter()
+                                    .map(|ty| {
+                                        self.tcx.sess.codemap().span_to_snippet(ty.span).ok()
+                                            .and_then(|snip| {
+                                                // filter out dummy spans
+                                                if snip == "," || snip == "|" {
+                                                    None
+                                                } else {
+                                                    Some(snip)
+                                                }
+                                            })
+                                    })
+                                    .collect::<Vec<Option<String>>>();
+
+                                let body = self.tcx.hir.body(id);
+                                let pat_snips = body.arguments.iter()
+                                    .map(|arg|
+                                        self.tcx.sess.codemap().span_to_snippet(arg.pat.span).ok())
+                                    .collect::<Option<Vec<String>>>();
+
+                                Some((span, pat_snips, ty_snips))
                             } else {
                                 None
                             }
-                        });
+                        })
+                        .map(|(span, pat, ty)| (Some(span), Some((pat, ty))))
+                        .unwrap_or((None, None));
+                    let closure_args = closure_args.and_then(|(pat, ty)| Some((pat?, ty)));
 
                     self.report_arg_count_mismatch(
                         span,
-                        closure_args_span.or(found_span),
+                        closure_span.or(found_span),
                         expected_ty_count,
                         expected_tuple,
                         found_ty_count,
+                        closure_args,
                         found_trait_ty.is_closure()
                     )
                 }
@@ -803,44 +831,85 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         err.emit();
     }
 
-    fn report_arg_count_mismatch(&self,
-                                 span: Span,
-                                 found_span: Option<Span>,
-                                 expected: usize,
-                                 expected_tuple: Option<usize>,
-                                 found: usize,
-                                 is_closure: bool)
-        -> DiagnosticBuilder<'tcx>
-    {
+    fn report_arg_count_mismatch(
+        &self,
+        span: Span,
+        found_span: Option<Span>,
+        expected: usize,
+        expected_tuple: Option<usize>,
+        found: usize,
+        closure_args: Option<(Vec<String>, Vec<Option<String>>)>,
+        is_closure: bool
+    ) -> DiagnosticBuilder<'tcx> {
+        use std::borrow::Cow;
+
         let kind = if is_closure { "closure" } else { "function" };
 
-        let tuple_or_args = |tuple, args|  if let Some(n) = tuple {
-            format!("a {}-tuple", n)
-        } else {
-            format!(
+        let args_str = |n| format!(
                 "{} argument{}",
-                args,
-                if args == 1 { "" } else { "s" }
-            )
-        };
-
-        let found_str = tuple_or_args(None, found);
-        let expected_str = tuple_or_args(expected_tuple, expected);
+                n,
+                if n == 1 { "" } else { "s" }
+            );
 
         let mut err = struct_span_err!(self.tcx.sess, span, E0593,
-            "{} takes {} but {} {} required",
+            "{} takes {}, but {} {} required",
             kind,
-            found_str,
-            expected_str,
-            if expected_tuple.is_some() || expected == 1 { "is" } else { "are" });
+            if expected_tuple.is_some() {
+                Cow::from("multiple arguments")
+            } else {
+                Cow::from(args_str(found))
+            },
+            if expected_tuple.is_some() {
+                Cow::from("a tuple argument")
+            } else {
+                Cow::from(args_str(expected))
+            },
+            if expected == 1 { "is" } else { "are" });
 
         err.span_label(
             span,
-            format!("expected {} that takes {}", kind, expected_str)
+            format!(
+                "expected {} that takes {}{}",
+                kind,
+                args_str(expected),
+                if let Some(n) = expected_tuple {
+                    assert!(expected == 1);
+                    Cow::from(format!(", a {}-tuple", n))
+                } else {
+                    Cow::from("")
+                }
+            )
         );
 
         if let Some(span) = found_span {
-            err.span_label(span, format!("takes {}", found_str));
+            if let (Some(expected_tuple), Some((pats, tys))) = (expected_tuple, closure_args) {
+                if expected_tuple != found || pats.len() != found {
+                    err.span_label(span, format!("takes {}", args_str(found)));
+                } else {
+                    let sugg = format!(
+                        "|({}){}|",
+                        pats.join(", "),
+
+                        // add type annotations if available
+                        if tys.iter().any(|ty| ty.is_some()) {
+                            Cow::from(format!(
+                                ": ({})",
+                                tys.into_iter().map(|ty| if let Some(ty) = ty {
+                                    ty
+                                } else {
+                                    "_".to_string()
+                                }).collect::<Vec<String>>().join(", ")
+                            ))
+                        } else {
+                            Cow::from("")
+                        },
+                    );
+
+                    err.span_suggestion(span, "consider changing to", sugg);
+                }
+            } else {
+                err.span_label(span, format!("takes {}", args_str(found)));
+            }
         }
 
         err
diff --git a/src/test/ui/mismatched_types/closure-arg-count.rs b/src/test/ui/mismatched_types/closure-arg-count.rs
index a2a31d44a49..5d2d1d2b04c 100644
--- a/src/test/ui/mismatched_types/closure-arg-count.rs
+++ b/src/test/ui/mismatched_types/closure-arg-count.rs
@@ -18,4 +18,6 @@ fn main() {
     f(|| panic!());
 
     let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i);
+    let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i);
+    let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x, y| i);
 }
diff --git a/src/test/ui/mismatched_types/closure-arg-count.stderr b/src/test/ui/mismatched_types/closure-arg-count.stderr
index e59a585149b..24860faf2d5 100644
--- a/src/test/ui/mismatched_types/closure-arg-count.stderr
+++ b/src/test/ui/mismatched_types/closure-arg-count.stderr
@@ -1,4 +1,4 @@
-error[E0593]: closure takes 0 arguments but 2 arguments are required
+error[E0593]: closure takes 0 arguments, but 2 arguments are required
   --> $DIR/closure-arg-count.rs:15:15
    |
 15 |     [1, 2, 3].sort_by(|| panic!());
@@ -6,7 +6,7 @@ error[E0593]: closure takes 0 arguments but 2 arguments are required
    |               |
    |               expected closure that takes 2 arguments
 
-error[E0593]: closure takes 1 argument but 2 arguments are required
+error[E0593]: closure takes 1 argument, but 2 arguments are required
   --> $DIR/closure-arg-count.rs:16:15
    |
 16 |     [1, 2, 3].sort_by(|tuple| panic!());
@@ -23,7 +23,7 @@ error[E0308]: mismatched types
    = note: expected type `&{integer}`
               found type `(_, _)`
 
-error[E0593]: closure takes 1 argument but 2 arguments are required
+error[E0593]: closure takes 1 argument, but 2 arguments are required
   --> $DIR/closure-arg-count.rs:17:15
    |
 17 |     [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
@@ -31,7 +31,7 @@ error[E0593]: closure takes 1 argument but 2 arguments are required
    |               |
    |               expected closure that takes 2 arguments
 
-error[E0593]: closure takes 0 arguments but 1 argument is required
+error[E0593]: closure takes 0 arguments, but 1 argument is required
   --> $DIR/closure-arg-count.rs:18:5
    |
 18 |     f(|| panic!());
@@ -41,13 +41,29 @@ error[E0593]: closure takes 0 arguments but 1 argument is required
    |
    = note: required by `f`
 
-error[E0593]: closure takes 2 arguments but a 2-tuple is required
+error[E0593]: closure takes multiple arguments, but a tuple argument is required
   --> $DIR/closure-arg-count.rs:20:53
    |
 20 |     let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i);
-   |                                                     ^^^ ------ takes 2 arguments
+   |                                                     ^^^ ------ help: consider changing to: `|(i, x)|`
    |                                                     |
-   |                                                     expected closure that takes a 2-tuple
+   |                                                     expected closure that takes 1 argument, a 2-tuple
 
-error: aborting due to 6 previous errors
+error[E0593]: closure takes multiple arguments, but a tuple argument is required
+  --> $DIR/closure-arg-count.rs:21:53
+   |
+21 |     let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i);
+   |                                                     ^^^ ------------- help: consider changing to: `|(i, x): (usize, _)|`
+   |                                                     |
+   |                                                     expected closure that takes 1 argument, a 2-tuple
+
+error[E0593]: closure takes multiple arguments, but a tuple argument is required
+  --> $DIR/closure-arg-count.rs:22:53
+   |
+22 |     let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x, y| i);
+   |                                                     ^^^ --------- takes 3 arguments
+   |                                                     |
+   |                                                     expected closure that takes 1 argument, a 2-tuple
+
+error: aborting due to 8 previous errors