From 8782d0fe78762db6c8cf750cc9707ede94026832 Mon Sep 17 00:00:00 2001 From: sinkuu Date: Fri, 6 Oct 2017 23:57:00 +0900 Subject: [PATCH] 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 c7c8141f4f76..372f05647261 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 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" }); + 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 {} 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) + ); - err.span_label(span, format!("expected {} that takes {} argument{}", - if is_closure { "closure" } else { "function" }, - expected, - if expected == 1 { "" } else { "s" })); 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 f94471a73ca2..a2a31d44a499 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 3031a77b1e82..e59a585149b8 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