Skip to content

Commit f09576c

Browse files
committed
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
2 parents 76eea74 + 345b833 commit f09576c

File tree

7 files changed

+116
-6
lines changed

7 files changed

+116
-6
lines changed

src/librustc/traits/error_reporting.rs

+32-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use hir::{self, intravisit, Local, Pat, Body};
2929
use hir::intravisit::{Visitor, NestedVisitorMap};
3030
use hir::map::NodeExpr;
3131
use hir::def_id::DefId;
32-
use infer::{self, InferCtxt};
32+
use infer::{self, InferCtxt, InferTables, InferTablesRef};
3333
use infer::type_variable::TypeVariableOrigin;
3434
use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
3535
use std::fmt;
@@ -640,16 +640,44 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
640640
ty::Predicate::ClosureKind(closure_def_id, kind) => {
641641
let found_kind = self.closure_kind(closure_def_id).unwrap();
642642
let closure_span = self.tcx.hir.span_if_local(closure_def_id).unwrap();
643+
let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
643644
let mut err = struct_span_err!(
644645
self.tcx.sess, closure_span, E0525,
645646
"expected a closure that implements the `{}` trait, \
646647
but this closure only implements `{}`",
647648
kind,
648649
found_kind);
649-
err.span_note(
650+
651+
err.span_label(
650652
obligation.cause.span,
651-
&format!("the requirement to implement \
652-
`{}` derives from here", kind));
653+
format!("the requirement to implement `{}` derives from here", kind));
654+
655+
let infer_tables = match self.tables {
656+
InferTables::Interned(tables) =>
657+
Some(InferTablesRef::Interned(tables)),
658+
InferTables::InProgress(tables) =>
659+
Some(InferTablesRef::InProgress(tables.borrow())),
660+
InferTables::Missing => None,
661+
};
662+
663+
// Additional context information explaining why the closure only implements
664+
// a particular trait.
665+
if let Some(tables) = infer_tables {
666+
match tables.closure_kinds.get(&node_id) {
667+
Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) => {
668+
err.span_note(span, &format!(
669+
"closure is `FnOnce` because it moves the \
670+
variable `{}` out of its environment", name));
671+
},
672+
Some(&(ty::ClosureKind::FnMut, Some((span, name)))) => {
673+
err.span_note(span, &format!(
674+
"closure is `FnMut` because it mutates the \
675+
variable `{}` here", name));
676+
},
677+
_ => {}
678+
}
679+
}
680+
653681
err.emit();
654682
return;
655683
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn foo() -> Box<Fn()> {
12+
let num = 5;
13+
14+
let closure = || {
15+
num += 1;
16+
};
17+
18+
Box::new(closure)
19+
}
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
2+
--> $DIR/issue-26046-fn-mut.rs:14:19
3+
|
4+
14 | let closure = || {
5+
| ___________________^
6+
15 | | num += 1;
7+
16 | | };
8+
| |_____^
9+
17 |
10+
18 | Box::new(closure)
11+
| ----------------- the requirement to implement `Fn` derives from here
12+
|
13+
note: closure is `FnMut` because it mutates the variable `num` here
14+
--> $DIR/issue-26046-fn-mut.rs:15:9
15+
|
16+
15 | num += 1;
17+
| ^^^
18+
19+
error: aborting due to previous error(s)
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn get_closure() -> Box<Fn() -> Vec<u8>> {
12+
let vec = vec![1u8, 2u8];
13+
14+
let closure = move || {
15+
vec
16+
};
17+
18+
Box::new(closure)
19+
}
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
2+
--> $DIR/issue-26046-fn-once.rs:14:19
3+
|
4+
14 | let closure = move || {
5+
| ___________________^
6+
15 | | vec
7+
16 | | };
8+
| |_____^
9+
17 |
10+
18 | Box::new(closure)
11+
| ----------------- the requirement to implement `Fn` derives from here
12+
|
13+
note: closure is `FnOnce` because it moves the variable `vec` out of its environment
14+
--> $DIR/issue-26046-fn-once.rs:15:9
15+
|
16+
15 | vec
17+
| ^^^
18+
19+
error: aborting due to previous error(s)
20+

src/test/ui/fn_once-moved.stderr renamed to src/test/ui/closure_context/issue-42065.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
error[E0382]: use of moved value: `debug_dump_dict`
2-
--> $DIR/fn_once-moved.rs:21:5
2+
--> $DIR/issue-42065.rs:21:5
33
|
44
20 | debug_dump_dict();
55
| --------------- value moved here
66
21 | debug_dump_dict();
77
| ^^^^^^^^^^^^^^^ value used here after move
88
|
99
note: closure cannot be invoked more than once because it moves the variable `dict` out of its environment
10-
--> $DIR/fn_once-moved.rs:16:29
10+
--> $DIR/issue-42065.rs:16:29
1111
|
1212
16 | for (key, value) in dict {
1313
| ^^^^

0 commit comments

Comments
 (0)