Auto merge of #42443 - tommyip:better_closure_msg, r=nikomatsakis

Better closure error message

Use tracked data introduced in #42196 to provide a better closure
error message by showing why a closure implements `FnOnce`.

```
error[E0525]: expected a closure that implements the `Fn` trait, but
this closure only implements `FnOnce`
 --> $DIR/issue_26046.rs:4:19
  |
4 |       let closure = move || {
  |  ___________________^
5 | |         vec
6 | |     };
  | |_____^
  |
note: closure is `FnOnce` because it moves the variable `vec` out of
its environment
 --> $DIR/issue_26046.rs:5:9
  |
5 |         vec
  |         ^^^

error: aborting due to previous error(s)
```

Fixes #26046

r? @nikomatsakis
cc @doomrobo
This commit is contained in:
bors 2017-06-08 08:37:48 +00:00
commit f09576c4a4
7 changed files with 116 additions and 6 deletions

View File

@ -29,7 +29,7 @@ use hir::{self, intravisit, Local, Pat, Body};
use hir::intravisit::{Visitor, NestedVisitorMap};
use hir::map::NodeExpr;
use hir::def_id::DefId;
use infer::{self, InferCtxt};
use infer::{self, InferCtxt, InferTables, InferTablesRef};
use infer::type_variable::TypeVariableOrigin;
use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
use std::fmt;
@ -640,16 +640,44 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
ty::Predicate::ClosureKind(closure_def_id, kind) => {
let found_kind = self.closure_kind(closure_def_id).unwrap();
let closure_span = self.tcx.hir.span_if_local(closure_def_id).unwrap();
let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
let mut err = struct_span_err!(
self.tcx.sess, closure_span, E0525,
"expected a closure that implements the `{}` trait, \
but this closure only implements `{}`",
kind,
found_kind);
err.span_note(
err.span_label(
obligation.cause.span,
&format!("the requirement to implement \
`{}` derives from here", kind));
format!("the requirement to implement `{}` derives from here", kind));
let infer_tables = match self.tables {
InferTables::Interned(tables) =>
Some(InferTablesRef::Interned(tables)),
InferTables::InProgress(tables) =>
Some(InferTablesRef::InProgress(tables.borrow())),
InferTables::Missing => None,
};
// Additional context information explaining why the closure only implements
// a particular trait.
if let Some(tables) = infer_tables {
match tables.closure_kinds.get(&node_id) {
Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) => {
err.span_note(span, &format!(
"closure is `FnOnce` because it moves the \
variable `{}` out of its environment", name));
},
Some(&(ty::ClosureKind::FnMut, Some((span, name)))) => {
err.span_note(span, &format!(
"closure is `FnMut` because it mutates the \
variable `{}` here", name));
},
_ => {}
}
}
err.emit();
return;
}

View File

@ -0,0 +1,21 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo() -> Box<Fn()> {
let num = 5;
let closure = || {
num += 1;
};
Box::new(closure)
}
fn main() {}

View File

@ -0,0 +1,20 @@
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
--> $DIR/issue-26046-fn-mut.rs:14:19
|
14 | let closure = || {
| ___________________^
15 | | num += 1;
16 | | };
| |_____^
17 |
18 | Box::new(closure)
| ----------------- the requirement to implement `Fn` derives from here
|
note: closure is `FnMut` because it mutates the variable `num` here
--> $DIR/issue-26046-fn-mut.rs:15:9
|
15 | num += 1;
| ^^^
error: aborting due to previous error(s)

View File

@ -0,0 +1,21 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn get_closure() -> Box<Fn() -> Vec<u8>> {
let vec = vec![1u8, 2u8];
let closure = move || {
vec
};
Box::new(closure)
}
fn main() {}

View File

@ -0,0 +1,20 @@
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> $DIR/issue-26046-fn-once.rs:14:19
|
14 | let closure = move || {
| ___________________^
15 | | vec
16 | | };
| |_____^
17 |
18 | Box::new(closure)
| ----------------- the requirement to implement `Fn` derives from here
|
note: closure is `FnOnce` because it moves the variable `vec` out of its environment
--> $DIR/issue-26046-fn-once.rs:15:9
|
15 | vec
| ^^^
error: aborting due to previous error(s)

View File

@ -1,5 +1,5 @@
error[E0382]: use of moved value: `debug_dump_dict`
--> $DIR/fn_once-moved.rs:21:5
--> $DIR/issue-42065.rs:21:5
|
20 | debug_dump_dict();
| --------------- value moved here
@ -7,7 +7,7 @@ error[E0382]: use of moved value: `debug_dump_dict`
| ^^^^^^^^^^^^^^^ value used here after move
|
note: closure cannot be invoked more than once because it moves the variable `dict` out of its environment
--> $DIR/fn_once-moved.rs:16:29
--> $DIR/issue-42065.rs:16:29
|
16 | for (key, value) in dict {
| ^^^^