Skip to content

Commit

Permalink
Better error for missing tuple pattern in args (#44150)
Browse files Browse the repository at this point in the history
  • Loading branch information
sinkuu committed Oct 6, 2017
1 parent 3ed8b69 commit 8782d0f
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 27 deletions.
94 changes: 72 additions & 22 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
});

Expand All @@ -727,23 +729,57 @@ 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,
found_span,
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()
)
Expand Down Expand Up @@ -771,28 +807,42 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
span: Span,
found_span: Option<Span>,
expected: usize,
expected_tuple: Option<usize>,
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
}

Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/mismatched_types/closure-arg-count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
18 changes: 13 additions & 5 deletions src/test/ui/mismatched_types/closure-arg-count.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ 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

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

Expand All @@ -27,19 +27,27 @@ 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

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

0 comments on commit 8782d0f

Please sign in to comment.