From 9d188e680e5558af2842231e6c32a676afc0086b Mon Sep 17 00:00:00 2001 From: Tommy Ip Date: Mon, 5 Jun 2017 11:14:48 +0100 Subject: [PATCH] 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 --- src/librustc/traits/error_reporting.rs | 32 ++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 049d5e488c9..e29e4786671 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -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,38 @@ 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( - obligation.cause.span, - &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, + }; + + if let Some(tables) = infer_tables { + if let Some(&(ty::ClosureKind::FnOnce, Some((span, name)))) = + tables.closure_kinds.get(&node_id) + { + err.span_note( + span, + &format!("closure is `FnOnce` because it moves the \ + variable `{}` out of its environment", name)); + } + } else { + err.span_note( + obligation.cause.span, + &format!("the requirement to implement `{}` \ + derives from here", kind)); + } + err.emit(); return; }