Skip to content

Commit

Permalink
Rollup merge of rust-lang#48047 - etaoins:fix-ice-for-mismatched-args…
Browse files Browse the repository at this point in the history
…-on-target-without-span, r=estebank

Fix ICE for mismatched args on target without span

Commit 7ed00ca improved our error reporting by including the target function in our error messages when there is an argument count mismatch. A simple example from the UI tests is:

```
error[E0593]: function is expected to take a single 2-tuple as argument, but it takes 0 arguments
  --> $DIR/closure-arg-count.rs:32:53
   |
32 |     let _it = vec![1, 2, 3].into_iter().enumerate().map(foo);
   |                                                     ^^^ expected function that takes a single 2-tuple as argument
...
44 | fn foo() {}
   | -------- takes 0 arguments
```

However, this assumed the target span was always available. This does not hold true if the target function is in `std` or another crate. A simple example from rust-lang#48046 is assigning `str::split` to a function type with a different number of arguments.

Fix by omitting all of the labels and suggestions related to the target span when it's not found.

Fixes rust-lang#48046

r? @estebank
  • Loading branch information
kennytm authored Feb 9, 2018
2 parents ebbc35b + daaa9a4 commit 67d4800
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 47 deletions.
93 changes: 49 additions & 44 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,8 +764,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
} else {
let (closure_span, found) = found_did
.and_then(|did| self.tcx.hir.get_if_local(did))
.map(|node| self.get_fn_like_arguments(node))
.unwrap_or((found_span.unwrap(), found));
.map(|node| {
let (found_span, found) = self.get_fn_like_arguments(node);
(Some(found_span), found)
}).unwrap_or((found_span, found));

self.report_arg_count_mismatch(span,
closure_span,
Expand Down Expand Up @@ -875,7 +877,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
fn report_arg_count_mismatch(
&self,
span: Span,
found_span: Span,
found_span: Option<Span>,
expected_args: Vec<ArgKind>,
found_args: Vec<ArgKind>,
is_closure: bool,
Expand Down Expand Up @@ -913,48 +915,51 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
);

err.span_label(span, format!( "expected {} that takes {}", kind, expected_str));
err.span_label(found_span, format!("takes {}", found_str));

if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
if fields.len() == expected_args.len() {
let sugg = fields.iter()
.map(|(name, _)| name.to_owned())
.collect::<Vec<String>>().join(", ");
err.span_suggestion(found_span,
"change the closure to take multiple arguments instead of \
a single tuple",
format!("|{}|", sugg));

if let Some(found_span) = found_span {
err.span_label(found_span, format!("takes {}", found_str));

if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
if fields.len() == expected_args.len() {
let sugg = fields.iter()
.map(|(name, _)| name.to_owned())
.collect::<Vec<String>>().join(", ");
err.span_suggestion(found_span,
"change the closure to take multiple arguments instead of \
a single tuple",
format!("|{}|", sugg));
}
}
}
if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] {
if fields.len() == found_args.len() && is_closure {
let sugg = format!(
"|({}){}|",
found_args.iter()
.map(|arg| match arg {
ArgKind::Arg(name, _) => name.to_owned(),
_ => "_".to_owned(),
})
.collect::<Vec<String>>()
.join(", "),
// add type annotations if available
if found_args.iter().any(|arg| match arg {
ArgKind::Arg(_, ty) => ty != "_",
_ => false,
}) {
format!(": ({})",
fields.iter()
.map(|(_, ty)| ty.to_owned())
.collect::<Vec<String>>()
.join(", "))
} else {
"".to_owned()
},
);
err.span_suggestion(found_span,
"change the closure to accept a tuple instead of individual \
arguments",
sugg);
if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] {
if fields.len() == found_args.len() && is_closure {
let sugg = format!(
"|({}){}|",
found_args.iter()
.map(|arg| match arg {
ArgKind::Arg(name, _) => name.to_owned(),
_ => "_".to_owned(),
})
.collect::<Vec<String>>()
.join(", "),
// add type annotations if available
if found_args.iter().any(|arg| match arg {
ArgKind::Arg(_, ty) => ty != "_",
_ => false,
}) {
format!(": ({})",
fields.iter()
.map(|(_, ty)| ty.to_owned())
.collect::<Vec<String>>()
.join(", "))
} else {
"".to_owned()
},
);
err.span_suggestion(found_span,
"change the closure to accept a tuple instead of \
individual arguments",
sugg);
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/mismatched_types/closure-arg-count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ fn main() {
//~^ ERROR closure is expected to take
let _it = vec![1, 2, 3].into_iter().enumerate().map(qux);
//~^ ERROR function is expected to take

let _it = vec![1, 2, 3].into_iter().map(usize::checked_add);
//~^ ERROR function is expected to take
}

fn foo() {}
Expand Down
12 changes: 9 additions & 3 deletions src/test/ui/mismatched_types/closure-arg-count.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ error[E0593]: function is expected to take a single 2-tuple as argument, but it
32 | let _it = vec![1, 2, 3].into_iter().enumerate().map(foo);
| ^^^ expected function that takes a single 2-tuple as argument
...
41 | fn foo() {}
44 | fn foo() {}
| -------- takes 0 arguments

error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 3 distinct arguments
Expand All @@ -107,8 +107,14 @@ error[E0593]: function is expected to take a single 2-tuple as argument, but it
37 | let _it = vec![1, 2, 3].into_iter().enumerate().map(qux);
| ^^^ expected function that takes a single 2-tuple as argument
...
42 | fn qux(x: usize, y: usize) {}
45 | fn qux(x: usize, y: usize) {}
| -------------------------- takes 2 distinct arguments

error: aborting due to 11 previous errors
error[E0593]: function is expected to take 1 argument, but it takes 2 arguments
--> $DIR/closure-arg-count.rs:40:41
|
40 | let _it = vec![1, 2, 3].into_iter().map(usize::checked_add);
| ^^^ expected function that takes 1 argument

error: aborting due to 12 previous errors

0 comments on commit 67d4800

Please sign in to comment.