Change:
Currently, this produces a somewhat confusing error: which could potentially be improved for this common case (an N-tuple was expected, but the closure takes N arguments instead). This could extend to other cases where there is a tuple : multiple arguments mismatch.
The original error is now handled, but leaving open to keep track of that still needs to be handled:

Discussion:
} } <del> OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, _) => { </del> <ins> OutputTypeParameterMismatch(ref found_trait_ref, ref expected_trait_ref, _) => { let found_trait_ref = self.resolve_type_vars_if_possible(&*found_trait_ref); </ins> let expected_trait_ref = self.resolve_type_vars_if_possible(&*expected_trait_ref); <del> let actual_trait_ref = self.resolve_type_vars_if_possible(&*actual_trait_ref); if actual_trait_ref.self_ty().references_error() { </del> <ins> if expected_trait_ref.self_ty().references_error() { </ins> return; } <del> let expected_trait_ty = expected_trait_ref.self_ty(); let found_span = expected_trait_ty.ty_to_def_id().and_then(|did| { </del> <ins> let found_trait_ty = found_trait_ref.self_ty(); let found_did = found_trait_ty.ty_to_def_id(); let found_span = found_did.and_then(|did| { </ins> self.tcx.hir.span_if_local(did) }); <del> let self_ty_count = match expected_trait_ref.skip_binder().substs.type_at(1).sty { </del> <ins> let found_ty_count = match found_trait_ref.skip_binder().substs.type_at(1).sty { </ins> ty::TyTuple(ref tys, _) => tys.len(), _ => 1, }; <del> let arg_ty_count = match actual_trait_ref.skip_binder().substs.type_at(1).sty { ty::TyTuple(ref tys, _) => tys.len(), _ => 1, </del> <ins> 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), </ins> }; <del> if self_ty_count == arg_ty_count { </del> <ins> if found_ty_count == expected_ty_count { </ins> self.report_closure_arg_mismatch(span, found_span, <del> expected_trait_ref, actual_trait_ref) </del> <ins> found_trait_ref, expected_trait_ref) </ins> } else { <del> // Expected `|| { }`, found `|x, y| { }` // Expected `fn(x) -> ()`, found `|| { }` </del> <ins> 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 { None } }) } else { None }; // 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)| ..) // //                   ^^^^^^^^ // // expected_trait_ref:  std::ops::FnMut<(&i32, &i32)> // //    found_trait_ref:  std::ops::FnMut<(&i32,)> // ``` 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(_, ref decl, id, span, _), .. }) = node { 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))); </ins> self.report_arg_count_mismatch( span, <del> found_span, arg_ty_count, self_ty_count, expected_trait_ty.is_closure() </del> <ins> closure_span.or(found_span), expected_ty_count, expected_tuple, found_ty_count, closure_args, found_trait_ty.is_closure() </ins> ) } }

err.emit(); } <del> fn report_arg_count_mismatch(&self, span: Span, found_span: Option<Span>, expected: usize, found: usize, is_closure: bool) -> DiagnosticBuilder<'tcx> { </del> <ins> 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 args_str = |n, distinct| format!( "{} {}argument{}", n, if distinct && n >= 2 { "distinct " } else { "" }, 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) }; </ins> let mut err = struct_span_err!(self.tcx.sess, span, E0593, <del> "{} 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" })); </del> <ins> "{} is expected to take {}, but it takes {}", kind, expected_str, found_str, ); err.span_label( span, format!( "expected {} that takes {}", kind, expected_str, ) ); </ins> if let Some(span) = found_span { <del> err.span_label(span, format!("takes {} argument{}", found, if found == 1 { "" } else { "s" })); </del> <ins> 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 {}", found_str)); } 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 the closure to accept a tuple", sugg ); } } else { err.span_label(span, format!("takes {}", found_str)); } </ins> } err }

[1, 2, 3].sort_by(|tuple| panic!()); [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); f(|| panic!()); <ins> 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); </ins> }

<del> error[E0593]: closure takes 0 arguments but 2 arguments are required </del> <ins> error[E0593]: closure is expected to take 2 arguments, but it takes 0 arguments </ins> --> $DIR/closure-arg-count.rs:15:15 | 15 |     [1, 2, 3].sort_by(|| panic!()); <del> |               ^^^^^^^ ----------- takes 0 arguments </del> <ins> |               ^^^^^^^ -- takes 0 arguments </ins> |               | |               expected closure that takes 2 arguments <del> error[E0593]: closure takes 1 argument but 2 arguments are required </del> <ins> error[E0593]: closure is expected to take 2 arguments, but it takes 1 argument </ins> --> $DIR/closure-arg-count.rs:16:15 | 16 |     [1, 2, 3].sort_by(|tuple| panic!()); <del> |               ^^^^^^^ ---------------- takes 1 argument </del> <ins> |               ^^^^^^^ ------- takes 1 argument </ins> |               | |               expected closure that takes 2 arguments

= note: expected type `&{integer}` found type `(_, _)` <del> error[E0593]: closure takes 1 argument but 2 arguments are required </del> <ins> error[E0593]: closure is expected to take 2 arguments, but it takes 1 argument </ins> --> $DIR/closure-arg-count.rs:17:15 | 17 |     [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); <del> |               ^^^^^^^ -------------------------- takes 1 argument </del> <ins> |               ^^^^^^^ ----------------- takes 1 argument </ins> |               | |               expected closure that takes 2 arguments <del> error[E0593]: closure takes 0 arguments but 1 argument is required </del> <ins> error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments </ins> --> $DIR/closure-arg-count.rs:18:5 | 18 |     f(|| panic!()); <del> |     ^ ----------- takes 0 arguments </del> <ins> |     ^ -- takes 0 arguments </ins> |     | |     expected closure that takes 1 argument | = note: required by `f` <del> error: aborting due to 5 previous errors </del> <ins> 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); |                                                     ^^^ ------ help: consider changing the closure to accept a tuple: `|(i, x)|` |                                                     | |                                                     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 the closure to accept a tuple: `|(i, x): (usize, _)|` |                                                     | |                                                     expected closure that takes a single tuple as argument 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 a single 2-tuple as argument error: aborting due to 8 previous errors </ins>