From 8782d0fe78762db6c8cf750cc9707ede94026832 Mon Sep 17 00:00:00 2001 From: sinkuu Date: Fri, 6 Oct 2017 23:57:00 +0900 Subject: [PATCH 1/5] Better error for missing tuple pattern in args (#44150) --- src/librustc/traits/error_reporting.rs | 94 ++++++++++++++----- .../ui/mismatched_types/closure-arg-count.rs | 2 + .../mismatched_types/closure-arg-count.stderr | 18 +++- 3 files changed, 87 insertions(+), 27 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index c7c8141f4f768..372f05647261f 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -718,7 +718,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { return; } let expected_trait_ty = expected_trait_ref.self_ty(); - let found_span = expected_trait_ty.ty_to_def_id().and_then(|did| { + + let found_did = expected_trait_ty.ty_to_def_id(); + let found_span = found_did.and_then(|did| { self.tcx.hir.span_if_local(did) }); @@ -727,10 +729,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::TyTuple(ref tys, _) => tys.len(), _ => 1, }; - let arg_ty_count = + let (arg_tys, arg_ty_count) = match actual_trait_ref.skip_binder().substs.type_at(1).sty { - ty::TyTuple(ref tys, _) => tys.len(), - _ => 1, + ty::TyTuple(ref tys, _) => + (tys.iter().map(|t| &t.sty).collect(), tys.len()), + ref sty => (vec![sty], 1), }; if self_ty_count == arg_ty_count { self.report_closure_arg_mismatch(span, @@ -738,12 +741,45 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { expected_trait_ref, actual_trait_ref) } else { - // Expected `|| { }`, found `|x, y| { }` - // Expected `fn(x) -> ()`, found `|| { }` + let arg_tuple = if arg_ty_count == 1 { + arg_tys.first().and_then(|t| { + if let &&ty::TyTuple(ref tuptys, _) = t { + Some(tuptys.len()) + } else { + None + } + }) + } else { + None + }; + + // FIXME(#44150): Expand this to "N args expected bug a N-tuple found". + // Type of the 1st expected argument is somehow provided as type of a + // found one in that case. + // + // ``` + // [1i32, 2, 3].sort_by(|(a, b)| ..) + // // ^^^^^^^^ + // // actual_trait_ref: std::ops::FnMut<(&i32, &i32)> + // // expected_trait_ref: std::ops::FnMut<(&i32,)> + // ``` + + let closure_args_span = 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 + { + Some(span) + } else { + None + } + }); + self.report_arg_count_mismatch( span, - found_span, + closure_args_span.or(found_span), arg_ty_count, + arg_tuple, self_ty_count, expected_trait_ty.is_closure() ) @@ -771,28 +807,42 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { span: Span, found_span: Option, expected: usize, + expected_tuple: Option, found: usize, is_closure: bool) -> DiagnosticBuilder<'tcx> { + 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!( + "{} argument{}", + args, + if args == 1 { "" } else { "s" } + ) + }; + + let found_str = tuple_or_args(None, found); + let expected_str = tuple_or_args(expected_tuple, expected); + let mut err = struct_span_err!(self.tcx.sess, span, E0593, - "{} takes {} argument{} but {} argument{} {} required", - if is_closure { "closure" } else { "function" }, - found, - if found == 1 { "" } else { "s" }, - expected, - if expected == 1 { "" } else { "s" }, - if expected == 1 { "is" } else { "are" }); - - err.span_label(span, format!("expected {} that takes {} argument{}", - if is_closure { "closure" } else { "function" }, - expected, - if expected == 1 { "" } else { "s" })); + "{} takes {} but {} {} required", + kind, + found_str, + expected_str, + if expected_tuple.is_some() || expected == 1 { "is" } else { "are" }); + + err.span_label( + span, + format!("expected {} that takes {}", kind, expected_str) + ); + if let Some(span) = found_span { - err.span_label(span, format!("takes {} argument{}", - found, - if found == 1 { "" } else { "s" })); + err.span_label(span, format!("takes {}", found_str)); } + err } diff --git a/src/test/ui/mismatched_types/closure-arg-count.rs b/src/test/ui/mismatched_types/closure-arg-count.rs index f94471a73ca27..a2a31d44a499f 100644 --- a/src/test/ui/mismatched_types/closure-arg-count.rs +++ b/src/test/ui/mismatched_types/closure-arg-count.rs @@ -16,4 +16,6 @@ fn main() { [1, 2, 3].sort_by(|tuple| panic!()); [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); f(|| panic!()); + + let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); } diff --git a/src/test/ui/mismatched_types/closure-arg-count.stderr b/src/test/ui/mismatched_types/closure-arg-count.stderr index 3031a77b1e828..e59a585149b89 100644 --- a/src/test/ui/mismatched_types/closure-arg-count.stderr +++ b/src/test/ui/mismatched_types/closure-arg-count.stderr @@ -2,7 +2,7 @@ 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!()); - | ^^^^^^^ ----------- takes 0 arguments + | ^^^^^^^ -- takes 0 arguments | | | expected closure that takes 2 arguments @@ -10,7 +10,7 @@ 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!()); - | ^^^^^^^ ---------------- takes 1 argument + | ^^^^^^^ ------- takes 1 argument | | | expected closure that takes 2 arguments @@ -27,7 +27,7 @@ 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!()); - | ^^^^^^^ -------------------------- takes 1 argument + | ^^^^^^^ ----------------- takes 1 argument | | | expected closure that takes 2 arguments @@ -35,11 +35,19 @@ error[E0593]: closure takes 0 arguments but 1 argument is required --> $DIR/closure-arg-count.rs:18:5 | 18 | f(|| panic!()); - | ^ ----------- takes 0 arguments + | ^ -- takes 0 arguments | | | expected closure that takes 1 argument | = note: required by `f` -error: aborting due to 5 previous errors +error[E0593]: closure takes 2 arguments but a 2-tuple 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 + | | + | expected closure that takes a 2-tuple + +error: aborting due to 6 previous errors From b6eef82c684268bd719d3ced4f4cca69b014aaff Mon Sep 17 00:00:00 2001 From: sinkuu Date: Sat, 7 Oct 2017 00:26:41 +0900 Subject: [PATCH 2/5] Rename variables --- src/librustc/traits/error_reporting.rs | 42 +++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 372f05647261f..115782ca2ade1 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -711,38 +711,38 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } - OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, _) => { + OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { + let found_trait_ref = self.resolve_type_vars_if_possible(&*found_trait_ref); let expected_trait_ref = self.resolve_type_vars_if_possible(&*expected_trait_ref); - let actual_trait_ref = self.resolve_type_vars_if_possible(&*actual_trait_ref); - if actual_trait_ref.self_ty().references_error() { + if expected_trait_ref.self_ty().references_error() { return; } - let expected_trait_ty = expected_trait_ref.self_ty(); + let found_trait_ty = found_trait_ref.self_ty(); - let found_did = expected_trait_ty.ty_to_def_id(); + let found_did = found_trait_ty.ty_to_def_id(); let found_span = found_did.and_then(|did| { self.tcx.hir.span_if_local(did) }); - let self_ty_count = - match expected_trait_ref.skip_binder().substs.type_at(1).sty { + let found_ty_count = + match found_trait_ref.skip_binder().substs.type_at(1).sty { ty::TyTuple(ref tys, _) => tys.len(), _ => 1, }; - let (arg_tys, arg_ty_count) = - match actual_trait_ref.skip_binder().substs.type_at(1).sty { + let (expected_tys, expected_ty_count) = + match expected_trait_ref.skip_binder().substs.type_at(1).sty { ty::TyTuple(ref tys, _) => (tys.iter().map(|t| &t.sty).collect(), tys.len()), ref sty => (vec![sty], 1), }; - if self_ty_count == arg_ty_count { + if found_ty_count == expected_ty_count { self.report_closure_arg_mismatch(span, found_span, - expected_trait_ref, - actual_trait_ref) + found_trait_ref, + expected_trait_ref) } else { - let arg_tuple = if arg_ty_count == 1 { - arg_tys.first().and_then(|t| { + let expected_tuple = if expected_ty_count == 1 { + expected_tys.first().and_then(|t| { if let &&ty::TyTuple(ref tuptys, _) = t { Some(tuptys.len()) } else { @@ -753,15 +753,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { None }; - // FIXME(#44150): Expand this to "N args expected bug a N-tuple found". + // FIXME(#44150): Expand this to "N args expected but a N-tuple found." // Type of the 1st expected argument is somehow provided as type of a // found one in that case. // // ``` // [1i32, 2, 3].sort_by(|(a, b)| ..) // // ^^^^^^^^ - // // actual_trait_ref: std::ops::FnMut<(&i32, &i32)> - // // expected_trait_ref: std::ops::FnMut<(&i32,)> + // // expected_trait_ref: std::ops::FnMut<(&i32, &i32)> + // // found_trait_ref: std::ops::FnMut<(&i32,)> // ``` let closure_args_span = found_did.and_then(|did| self.tcx.hir.get_if_local(did)) @@ -778,10 +778,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.report_arg_count_mismatch( span, closure_args_span.or(found_span), - arg_ty_count, - arg_tuple, - self_ty_count, - expected_trait_ty.is_closure() + expected_ty_count, + expected_tuple, + found_ty_count, + found_trait_ty.is_closure() ) } } From c74bda1075757cd00f72a918e2d7fdaf47f85c2d Mon Sep 17 00:00:00 2001 From: sinkuu Date: Sat, 7 Oct 2017 15:12:56 +0900 Subject: [PATCH 3/5] Add suggestion --- src/librustc/traits/error_reporting.rs | 131 +++++++++++++----- .../ui/mismatched_types/closure-arg-count.rs | 2 + .../mismatched_types/closure-arg-count.stderr | 32 +++-- 3 files changed, 126 insertions(+), 39 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 115782ca2ade1..5056876f3ad53 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::>>(); + + 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::>>(); + + 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, - expected: usize, - expected_tuple: Option, - found: usize, - is_closure: bool) - -> DiagnosticBuilder<'tcx> - { + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option, + expected: usize, + expected_tuple: Option, + found: usize, + closure_args: Option<(Vec, Vec>)>, + 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::>().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 a2a31d44a499f..5d2d1d2b04c50 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 e59a585149b89..24860faf2d528 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 From fa14f797f32acbea481989371f6c32739975f160 Mon Sep 17 00:00:00 2001 From: sinkuu Date: Tue, 10 Oct 2017 11:03:23 +0900 Subject: [PATCH 4/5] Reword --- src/librustc/traits/error_reporting.rs | 25 ++++++++++--------- .../mismatched_types/closure-arg-count.stderr | 16 ++++++------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 5056876f3ad53..a192dc6d6ea9d 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -845,33 +845,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let kind = if is_closure { "closure" } else { "function" }; - let args_str = |n| format!( - "{} argument{}", + let args_str = |n, distinct| format!( + "{} {}argument{}", n, - if n == 1 { "" } else { "s" } + if distinct && n >= 2 { "distinct " } else { "" }, + if n == 1 { "" } else { "s" }, ); let mut err = struct_span_err!(self.tcx.sess, span, E0593, - "{} takes {}, but {} {} required", + "{} is expected to take {}, but it takes {}", kind, if expected_tuple.is_some() { - Cow::from("multiple arguments") + Cow::from("a single tuple as argument") } else { - Cow::from(args_str(found)) + Cow::from(args_str(expected, false)) }, if expected_tuple.is_some() { - Cow::from("a tuple argument") + args_str(found, true) } else { - Cow::from(args_str(expected)) + args_str(found, false) }, - if expected == 1 { "is" } else { "are" }); + ); err.span_label( span, format!( "expected {} that takes {}{}", kind, - args_str(expected), + args_str(expected, false), if let Some(n) = expected_tuple { assert!(expected == 1); Cow::from(format!(", a {}-tuple", n)) @@ -884,7 +885,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { if let Some(span) = found_span { 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))); + err.span_label(span, format!("takes {}", args_str(found, true))); } else { let sugg = format!( "|({}){}|", @@ -908,7 +909,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { err.span_suggestion(span, "consider changing to", sugg); } } else { - err.span_label(span, format!("takes {}", args_str(found))); + err.span_label(span, format!("takes {}", args_str(found, false))); } } diff --git a/src/test/ui/mismatched_types/closure-arg-count.stderr b/src/test/ui/mismatched_types/closure-arg-count.stderr index 24860faf2d528..8f508ade68ca1 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 is expected to take 2 arguments, but it takes 0 arguments --> $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 is expected to take 2 arguments, but it takes 1 argument --> $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 is expected to take 2 arguments, but it takes 1 argument --> $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 is expected to take 1 argument, but it takes 0 arguments --> $DIR/closure-arg-count.rs:18:5 | 18 | f(|| panic!()); @@ -41,7 +41,7 @@ error[E0593]: closure takes 0 arguments, but 1 argument is required | = note: required by `f` -error[E0593]: closure takes multiple arguments, but a tuple argument is required +error[E0593]: closure is expected to take a single tuple as argument, but it takes 2 distinct arguments --> $DIR/closure-arg-count.rs:20:53 | 20 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); @@ -49,7 +49,7 @@ error[E0593]: closure takes multiple arguments, but a tuple argument is required | | | expected closure that takes 1 argument, a 2-tuple -error[E0593]: closure takes multiple arguments, but a tuple argument is required +error[E0593]: closure is expected to take a single tuple as argument, but it takes 2 distinct arguments --> $DIR/closure-arg-count.rs:21:53 | 21 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i); @@ -57,11 +57,11 @@ error[E0593]: closure takes multiple arguments, but a tuple argument is required | | | expected closure that takes 1 argument, a 2-tuple -error[E0593]: closure takes multiple arguments, but a tuple argument is required +error[E0593]: closure is expected to take a single tuple as argument, but it takes 3 distinct arguments --> $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 + | ^^^ --------- takes 3 distinct arguments | | | expected closure that takes 1 argument, a 2-tuple From f577847aa22cda4935562fe8e7c9420bcc58f148 Mon Sep 17 00:00:00 2001 From: sinkuu Date: Fri, 13 Oct 2017 09:56:50 +0900 Subject: [PATCH 5/5] Reword --- src/librustc/traits/error_reporting.rs | 51 +++++++++++-------- .../mismatched_types/closure-arg-count.stderr | 12 ++--- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index a192dc6d6ea9d..030b7e4f646f9 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -852,40 +852,45 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { if n == 1 { "" } else { "s" }, ); + let expected_str = if let Some(n) = expected_tuple { + assert!(expected == 1); + if closure_args.as_ref().map(|&(ref pats, _)| pats.len()) == Some(n) { + Cow::from("a single tuple as argument") + } else { + // be verbose when numbers differ + Cow::from(format!("a single {}-tuple as argument", n)) + } + } else { + Cow::from(args_str(expected, false)) + }; + + let found_str = if expected_tuple.is_some() { + args_str(found, true) + } else { + args_str(found, false) + }; + + let mut err = struct_span_err!(self.tcx.sess, span, E0593, "{} is expected to take {}, but it takes {}", kind, - if expected_tuple.is_some() { - Cow::from("a single tuple as argument") - } else { - Cow::from(args_str(expected, false)) - }, - if expected_tuple.is_some() { - args_str(found, true) - } else { - args_str(found, false) - }, + expected_str, + found_str, ); err.span_label( span, format!( - "expected {} that takes {}{}", + "expected {} that takes {}", kind, - args_str(expected, false), - if let Some(n) = expected_tuple { - assert!(expected == 1); - Cow::from(format!(", a {}-tuple", n)) - } else { - Cow::from("") - } + expected_str, ) ); if let Some(span) = found_span { 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, true))); + err.span_label(span, format!("takes {}", found_str)); } else { let sugg = format!( "|({}){}|", @@ -906,10 +911,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }, ); - err.span_suggestion(span, "consider changing to", sugg); + err.span_suggestion( + span, + "consider changing the closure to accept a tuple", + sugg + ); } } else { - err.span_label(span, format!("takes {}", args_str(found, false))); + err.span_label(span, format!("takes {}", found_str)); } } diff --git a/src/test/ui/mismatched_types/closure-arg-count.stderr b/src/test/ui/mismatched_types/closure-arg-count.stderr index 8f508ade68ca1..9d4ac6305465a 100644 --- a/src/test/ui/mismatched_types/closure-arg-count.stderr +++ b/src/test/ui/mismatched_types/closure-arg-count.stderr @@ -45,25 +45,25 @@ error[E0593]: closure is expected to take a single tuple as argument, but it tak --> $DIR/closure-arg-count.rs:20:53 | 20 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); - | ^^^ ------ help: consider changing to: `|(i, x)|` + | ^^^ ------ help: consider changing the closure to accept a tuple: `|(i, x)|` | | - | expected closure that takes 1 argument, a 2-tuple + | expected closure that takes a single tuple as argument error[E0593]: closure is expected to take a single tuple as argument, but it takes 2 distinct arguments --> $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, _)|` + | ^^^ ------------- help: consider changing the closure to accept a tuple: `|(i, x): (usize, _)|` | | - | expected closure that takes 1 argument, a 2-tuple + | expected closure that takes a single tuple as argument -error[E0593]: closure is expected to take a single tuple as argument, but it takes 3 distinct arguments +error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 3 distinct arguments --> $DIR/closure-arg-count.rs:22:53 | 22 | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x, y| i); | ^^^ --------- takes 3 distinct arguments | | - | expected closure that takes 1 argument, a 2-tuple + | expected closure that takes a single 2-tuple as argument error: aborting due to 8 previous errors