Skip to content

Commit

Permalink
Improve error message where a closure escapes fn while trying to borrow
Browse files Browse the repository at this point in the history
from the current fn. Employ the new `span_suggestion` to show how you
can use `move`.
  • Loading branch information
nikomatsakis committed Apr 9, 2015
1 parent 56ad009 commit df89b4d
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 9 deletions.
62 changes: 53 additions & 9 deletions src/librustc_borrowck/borrowck/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,16 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
}

pub fn report(&self, err: BckError<'tcx>) {
// Catch and handle some particular cases.
match (&err.code, &err.cause) {
(&err_out_of_scope(ty::ReScope(_), ty::ReStatic), &euv::ClosureCapture(span)) |
(&err_out_of_scope(ty::ReScope(_), ty::ReFree(..)), &euv::ClosureCapture(span)) => {
return self.report_out_of_scope_escaping_closure_capture(&err, span);
}
_ => { }
}

// General fallback.
self.span_err(
err.span,
&self.bckerr_to_string(&err));
Expand Down Expand Up @@ -795,16 +805,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
format!("{} does not live long enough", msg)
}
err_borrowed_pointer_too_short(..) => {
let descr = match opt_loan_path(&err.cmt) {
Some(lp) => {
format!("`{}`", self.loan_path_to_string(&*lp))
}
None => self.cmt_to_string(&*err.cmt),
};

let descr = self.cmt_to_path_or_string(&err.cmt);
format!("lifetime of {} is too short to guarantee \
its contents can be safely reborrowed",
descr)
its contents can be safely reborrowed",
descr)
}
}
}
Expand Down Expand Up @@ -886,6 +890,39 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
}
}

fn report_out_of_scope_escaping_closure_capture(&self,
err: &BckError<'tcx>,
capture_span: Span)
{
let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt);

span_err!(
self.tcx.sess, err.span, E0371,
"closure may outlive the current function, \
but it borrows {}, \
which is owned by the current function",
cmt_path_or_string);

self.tcx.sess.span_note(
capture_span,
&format!("{} is borrowed here",
cmt_path_or_string));

let suggestion =
match self.tcx.sess.codemap().span_to_snippet(err.span) {
Ok(string) => format!("move {}", string.lines().next().unwrap()),
Err(_) => format!("move |<args>| <body>")
};

self.tcx.sess.span_suggestion(
err.span,
&format!("to force the closure to take ownership of {} \
(and any other referenced variables), \
use the `move` keyword, as shown:",
cmt_path_or_string),
suggestion);
}

pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
let code = err.code;
match code {
Expand Down Expand Up @@ -1033,6 +1070,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
cmt.descriptive_string(self.tcx)
}

pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String {
match opt_loan_path(cmt) {
Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)),
None => self.cmt_to_string(cmt),
}
}
}

fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
Expand Down
17 changes: 17 additions & 0 deletions src/librustc_borrowck/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2014 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.

#![allow(non_snake_case)]

register_diagnostics! {
E0371 // closure may outlive current fn, but it borrows {}, which is owned by current fn
}

__build_diagnostic_array! { DIAGNOSTICS }
4 changes: 4 additions & 0 deletions src/librustc_borrowck/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub use borrowck::check_crate;
pub use borrowck::build_borrowck_dataflow_data_for_fn;
pub use borrowck::FnPartsWithCFG;

// NB: This module needs to be declared first so diagnostics are
// registered before they are used.
pub mod diagnostics;

mod borrowck;

pub mod graphviz;

0 comments on commit df89b4d

Please sign in to comment.