add borrowck info inline in main snippet
This uses the new `span_label` APIs
This commit is contained in:
parent
e7c7a18d94
commit
5b150cf0ca
|
@ -1569,5 +1569,5 @@ register_diagnostics! {
|
|||
E0490, // a value of type `..` is borrowed for too long
|
||||
E0491, // in type `..`, reference has a longer lifetime than the data it...
|
||||
E0495, // cannot infer an appropriate lifetime due to conflicting requirements
|
||||
E0524, // expected a closure that implements `..` but this closure only implements `..`
|
||||
E0525, // expected a closure that implements `..` but this closure only implements `..`
|
||||
}
|
||||
|
|
|
@ -447,22 +447,24 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
|||
// borrow ends
|
||||
|
||||
let common = new_loan.loan_path.common(&old_loan.loan_path);
|
||||
let (nl, ol, new_loan_msg, old_loan_msg) =
|
||||
let (nl, ol, new_loan_msg, old_loan_msg) = {
|
||||
if new_loan.loan_path.has_fork(&old_loan.loan_path) && common.is_some() {
|
||||
let nl = self.bccx.loan_path_to_string(&common.unwrap());
|
||||
let ol = nl.clone();
|
||||
let new_loan_msg = format!(" (here through borrowing `{}`)",
|
||||
let new_loan_msg = format!(" (via `{}`)",
|
||||
self.bccx.loan_path_to_string(
|
||||
&new_loan.loan_path));
|
||||
let old_loan_msg = format!(" (through borrowing `{}`)",
|
||||
let old_loan_msg = format!(" (via `{}`)",
|
||||
self.bccx.loan_path_to_string(
|
||||
&old_loan.loan_path));
|
||||
(nl, ol, new_loan_msg, old_loan_msg)
|
||||
} else {
|
||||
(self.bccx.loan_path_to_string(&new_loan.loan_path),
|
||||
self.bccx.loan_path_to_string(&old_loan.loan_path),
|
||||
String::new(), String::new())
|
||||
};
|
||||
String::new(),
|
||||
String::new())
|
||||
}
|
||||
};
|
||||
|
||||
let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
|
||||
"it".to_string()
|
||||
|
@ -470,12 +472,48 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
|||
format!("`{}`", ol)
|
||||
};
|
||||
|
||||
// We want to assemble all the relevant locations for the error.
|
||||
//
|
||||
// 1. Where did the new loan occur.
|
||||
// - if due to closure creation, where was the variable used in closure?
|
||||
// 2. Where did old loan occur.
|
||||
// 3. Where does old loan expire.
|
||||
|
||||
let previous_end_span =
|
||||
self.tcx().map.span(old_loan.kill_scope.node_id(&self.tcx().region_maps))
|
||||
.end_point();
|
||||
|
||||
let mut err = match (new_loan.kind, old_loan.kind) {
|
||||
(ty::MutBorrow, ty::MutBorrow) => {
|
||||
struct_span_err!(self.bccx, new_loan.span, E0499,
|
||||
"cannot borrow `{}`{} as mutable \
|
||||
more than once at a time",
|
||||
nl, new_loan_msg)
|
||||
.span_label(
|
||||
old_loan.span,
|
||||
&format!("first mutable borrow occurs here{}", old_loan_msg))
|
||||
.span_label(
|
||||
new_loan.span,
|
||||
&format!("second mutable borrow occurs here{}", new_loan_msg))
|
||||
.span_label(
|
||||
previous_end_span,
|
||||
&format!("first borrow ends here"))
|
||||
}
|
||||
|
||||
(ty::UniqueImmBorrow, ty::UniqueImmBorrow) => {
|
||||
struct_span_err!(self.bccx, new_loan.span, E0524,
|
||||
"two closures require unique access to `{}` \
|
||||
at the same time",
|
||||
nl)
|
||||
.span_label(
|
||||
old_loan.span,
|
||||
&format!("first closure is constructed here"))
|
||||
.span_label(
|
||||
new_loan.span,
|
||||
&format!("second closure is constructed here"))
|
||||
.span_label(
|
||||
previous_end_span,
|
||||
&format!("borrow from first closure ends here"))
|
||||
}
|
||||
|
||||
(ty::UniqueImmBorrow, _) => {
|
||||
|
@ -483,6 +521,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
|||
"closure requires unique access to `{}` \
|
||||
but {} is already borrowed{}",
|
||||
nl, ol_pronoun, old_loan_msg)
|
||||
.span_label(
|
||||
new_loan.span,
|
||||
&format!("closure construction occurs here{}", new_loan_msg))
|
||||
.span_label(
|
||||
old_loan.span,
|
||||
&format!("borrow occurs here{}", old_loan_msg))
|
||||
.span_label(
|
||||
previous_end_span,
|
||||
&format!("borrow ends here"))
|
||||
}
|
||||
|
||||
(_, ty::UniqueImmBorrow) => {
|
||||
|
@ -490,6 +537,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
|||
"cannot borrow `{}`{} as {} because \
|
||||
previous closure requires unique access",
|
||||
nl, new_loan_msg, new_loan.kind.to_user_str())
|
||||
.span_label(
|
||||
new_loan.span,
|
||||
&format!("borrow occurs here{}", new_loan_msg))
|
||||
.span_label(
|
||||
old_loan.span,
|
||||
&format!("closure construction occurs here{}", old_loan_msg))
|
||||
.span_label(
|
||||
previous_end_span,
|
||||
&format!("borrow from closure ends here"))
|
||||
}
|
||||
|
||||
(_, _) => {
|
||||
|
@ -502,70 +558,42 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
|||
ol_pronoun,
|
||||
old_loan.kind.to_user_str(),
|
||||
old_loan_msg)
|
||||
.span_label(
|
||||
new_loan.span,
|
||||
&format!("{} borrow occurs here{}",
|
||||
new_loan.kind.to_user_str(),
|
||||
new_loan_msg))
|
||||
.span_label(
|
||||
old_loan.span,
|
||||
&format!("{} borrow occurs here{}",
|
||||
old_loan.kind.to_user_str(),
|
||||
old_loan_msg))
|
||||
.span_label(
|
||||
previous_end_span,
|
||||
&format!("{} borrow ends here",
|
||||
old_loan.kind.to_user_str()))
|
||||
}
|
||||
};
|
||||
|
||||
match new_loan.cause {
|
||||
euv::ClosureCapture(span) => {
|
||||
err.span_note(
|
||||
err = err.span_label(
|
||||
span,
|
||||
&format!("borrow occurs due to use of `{}` in closure",
|
||||
nl));
|
||||
&format!("borrow occurs due to use of `{}` in closure", nl));
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
|
||||
let rule_summary = match old_loan.kind {
|
||||
ty::MutBorrow => {
|
||||
format!("the mutable borrow prevents subsequent \
|
||||
moves, borrows, or modification of `{0}` \
|
||||
until the borrow ends",
|
||||
ol)
|
||||
match old_loan.cause {
|
||||
euv::ClosureCapture(span) => {
|
||||
err = err.span_label(
|
||||
span,
|
||||
&format!("previous borrow occurs due to use of `{}` in closure",
|
||||
ol));
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
|
||||
ty::ImmBorrow => {
|
||||
format!("the immutable borrow prevents subsequent \
|
||||
moves or mutable borrows of `{0}` \
|
||||
until the borrow ends",
|
||||
ol)
|
||||
}
|
||||
|
||||
ty::UniqueImmBorrow => {
|
||||
format!("the unique capture prevents subsequent \
|
||||
moves or borrows of `{0}` \
|
||||
until the borrow ends",
|
||||
ol)
|
||||
}
|
||||
};
|
||||
|
||||
let borrow_summary = match old_loan.cause {
|
||||
euv::ClosureCapture(_) => {
|
||||
format!("previous borrow of `{}` occurs here{} due to \
|
||||
use in closure",
|
||||
ol, old_loan_msg)
|
||||
}
|
||||
|
||||
euv::OverloadedOperator |
|
||||
euv::AddrOf |
|
||||
euv::AutoRef |
|
||||
euv::AutoUnsafe |
|
||||
euv::ClosureInvocation |
|
||||
euv::ForLoop |
|
||||
euv::RefBinding |
|
||||
euv::MatchDiscriminant => {
|
||||
format!("previous borrow of `{}` occurs here{}",
|
||||
ol, old_loan_msg)
|
||||
}
|
||||
};
|
||||
|
||||
err.span_note(
|
||||
old_loan.span,
|
||||
&format!("{}; {}", borrow_summary, rule_summary));
|
||||
|
||||
let old_loan_span = self.tcx().map.span(
|
||||
old_loan.kill_scope.node_id(&self.tcx().region_maps));
|
||||
err.span_end_note(old_loan_span,
|
||||
"previous borrow ends here");
|
||||
err.emit();
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -34,14 +34,14 @@ use rustc::middle::free_region::FreeRegionMap;
|
|||
use rustc::middle::mem_categorization as mc;
|
||||
use rustc::middle::mem_categorization::Categorization;
|
||||
use rustc::middle::region;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast;
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::codemap::{MultiSpan, Span};
|
||||
use syntax::errors::DiagnosticBuilder;
|
||||
|
||||
use rustc::hir;
|
||||
|
@ -633,23 +633,22 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
|||
lp: &LoanPath<'tcx>,
|
||||
the_move: &move_data::Move,
|
||||
moved_lp: &LoanPath<'tcx>,
|
||||
param_env: &ty::ParameterEnvironment<'b,'tcx>) {
|
||||
let verb = match use_kind {
|
||||
MovedInUse => "use",
|
||||
MovedInCapture => "capture",
|
||||
_param_env: &ty::ParameterEnvironment<'b,'tcx>) {
|
||||
let (verb, verb_participle) = match use_kind {
|
||||
MovedInUse => ("use", "used"),
|
||||
MovedInCapture => ("capture", "captured"),
|
||||
};
|
||||
|
||||
let (ol, moved_lp_msg, mut err) = match the_move.kind {
|
||||
let (_ol, _moved_lp_msg, mut err) = match the_move.kind {
|
||||
move_data::Declared => {
|
||||
let err = struct_span_err!(
|
||||
// If this is an uninitialized variable, just emit a simple warning
|
||||
// and return.
|
||||
struct_span_err!(
|
||||
self.tcx.sess, use_span, E0381,
|
||||
"{} of possibly uninitialized variable: `{}`",
|
||||
verb,
|
||||
self.loan_path_to_string(lp));
|
||||
|
||||
(self.loan_path_to_string(moved_lp),
|
||||
String::new(),
|
||||
err)
|
||||
self.loan_path_to_string(lp)).emit();
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
// If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
|
||||
|
@ -688,122 +687,52 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
|||
self.tcx.sess, use_span, E0382,
|
||||
"{} of {}moved value: `{}`",
|
||||
verb, msg, nl);
|
||||
(ol, moved_lp_msg, err)
|
||||
}
|
||||
(ol, moved_lp_msg, err)}
|
||||
};
|
||||
|
||||
match the_move.kind {
|
||||
move_data::Declared => {}
|
||||
|
||||
move_data::MoveExpr => {
|
||||
let (expr_ty, expr_span) = match self.tcx
|
||||
.map
|
||||
.find(the_move.id) {
|
||||
Some(hir_map::NodeExpr(expr)) => {
|
||||
(self.tcx.expr_ty_adjusted(&expr), expr.span)
|
||||
}
|
||||
r => {
|
||||
bug!("MoveExpr({}) maps to {:?}, not Expr",
|
||||
the_move.id,
|
||||
r)
|
||||
}
|
||||
};
|
||||
let (suggestion, _) =
|
||||
move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
|
||||
// If the two spans are the same, it's because the expression will be evaluated
|
||||
// multiple times. Avoid printing the same span and adjust the wording so it makes
|
||||
// more sense that it's from multiple evalutations.
|
||||
if expr_span == use_span {
|
||||
err.note(
|
||||
&format!("`{}` was previously moved here{} because it has type `{}`, \
|
||||
which is {}",
|
||||
ol,
|
||||
moved_lp_msg,
|
||||
expr_ty,
|
||||
suggestion));
|
||||
} else {
|
||||
err.span_note(
|
||||
expr_span,
|
||||
&format!("`{}` moved here{} because it has type `{}`, which is {}",
|
||||
ol,
|
||||
moved_lp_msg,
|
||||
expr_ty,
|
||||
suggestion));
|
||||
}
|
||||
// Get type of value and span where it was previously
|
||||
// moved.
|
||||
let (move_span, move_note) = match the_move.kind {
|
||||
move_data::Declared => {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
move_data::MovePat => {
|
||||
let pat_ty = self.tcx.node_id_to_type(the_move.id);
|
||||
let span = self.tcx.map.span(the_move.id);
|
||||
err.span_note(span,
|
||||
&format!("`{}` moved here{} because it has type `{}`, \
|
||||
which is moved by default",
|
||||
ol,
|
||||
moved_lp_msg,
|
||||
pat_ty));
|
||||
match self.tcx.sess.codemap().span_to_snippet(span) {
|
||||
Ok(string) => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
&format!("if you would like to borrow the value instead, \
|
||||
use a `ref` binding as shown:"),
|
||||
format!("ref {}", string));
|
||||
},
|
||||
Err(_) => {
|
||||
err.fileline_help(span,
|
||||
"use `ref` to override");
|
||||
},
|
||||
}
|
||||
}
|
||||
move_data::MoveExpr |
|
||||
move_data::MovePat =>
|
||||
(self.tcx.map.span(the_move.id), ""),
|
||||
|
||||
move_data::Captured =>
|
||||
(match self.tcx.map.expect_expr(the_move.id).node {
|
||||
hir::ExprClosure(_, _, _, fn_decl_span) => fn_decl_span,
|
||||
ref r => bug!("Captured({}) maps to non-closure: {:?}",
|
||||
the_move.id, r),
|
||||
}, " (into closure)"),
|
||||
};
|
||||
|
||||
// Annotate the use and the move in the span. Watch out for
|
||||
// the case where the use and the move are the same. This
|
||||
// means the use is in a loop.
|
||||
err = if use_span == move_span {
|
||||
err.span_label(
|
||||
use_span,
|
||||
&format!("value moved{} here in previous iteration of loop",
|
||||
move_note))
|
||||
} else {
|
||||
err.span_label(use_span, &format!("value {} here after move", verb_participle))
|
||||
.span_label(move_span, &format!("value moved{} here", move_note))
|
||||
};
|
||||
|
||||
err.note(&format!("move occurs because `{}` has type `{}`, \
|
||||
which does not implement the `Copy` trait",
|
||||
self.loan_path_to_string(moved_lp),
|
||||
moved_lp.ty));
|
||||
|
||||
// Note: we used to suggest adding a `ref binding` or calling
|
||||
// `clone` but those suggestions have been removed because
|
||||
// they are often not what you actually want to do, and were
|
||||
// not considered particularly helpful.
|
||||
|
||||
move_data::Captured => {
|
||||
let (expr_ty, expr_span) = match self.tcx
|
||||
.map
|
||||
.find(the_move.id) {
|
||||
Some(hir_map::NodeExpr(expr)) => {
|
||||
(self.tcx.expr_ty_adjusted(&expr), expr.span)
|
||||
}
|
||||
r => {
|
||||
bug!("Captured({}) maps to {:?}, not Expr",
|
||||
the_move.id,
|
||||
r)
|
||||
}
|
||||
};
|
||||
let (suggestion, help) =
|
||||
move_suggestion(param_env,
|
||||
expr_span,
|
||||
expr_ty,
|
||||
("moved by default",
|
||||
"make a copy and capture that instead to override"));
|
||||
err.span_note(
|
||||
expr_span,
|
||||
&format!("`{}` moved into closure environment here{} because it \
|
||||
has type `{}`, which is {}",
|
||||
ol,
|
||||
moved_lp_msg,
|
||||
moved_lp.ty,
|
||||
suggestion));
|
||||
err.fileline_help(expr_span, help);
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
|
||||
fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>,
|
||||
default_msgs: (&'static str, &'static str))
|
||||
-> (&'static str, &'static str) {
|
||||
match ty.sty {
|
||||
_ => {
|
||||
if ty.moves_by_default(param_env, span) {
|
||||
("non-copyable",
|
||||
"perhaps you meant to use `clone()`?")
|
||||
} else {
|
||||
default_msgs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_partial_reinitialization_of_uninitialized_structure(
|
||||
|
@ -833,19 +762,20 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
|||
self.tcx.sess.span_err(s, m);
|
||||
}
|
||||
|
||||
pub fn struct_span_err(&self, s: Span, m: &str) -> DiagnosticBuilder<'a> {
|
||||
pub fn struct_span_err<S: Into<MultiSpan>>(&self, s: S, m: &str)
|
||||
-> DiagnosticBuilder<'a> {
|
||||
self.tcx.sess.struct_span_err(s, m)
|
||||
}
|
||||
|
||||
pub fn struct_span_err_with_code(&self,
|
||||
s: Span,
|
||||
msg: &str,
|
||||
code: &str)
|
||||
-> DiagnosticBuilder<'a> {
|
||||
pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self,
|
||||
s: S,
|
||||
msg: &str,
|
||||
code: &str)
|
||||
-> DiagnosticBuilder<'a> {
|
||||
self.tcx.sess.struct_span_err_with_code(s, msg, code)
|
||||
}
|
||||
|
||||
pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
|
||||
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, s: S, msg: &str, code: &str) {
|
||||
self.tcx.sess.span_err_with_code(s, msg, code);
|
||||
}
|
||||
|
||||
|
|
|
@ -444,4 +444,5 @@ register_diagnostics! {
|
|||
E0506, // cannot assign to `..` because it is borrowed
|
||||
E0508, // cannot move out of type `..`, a non-copy fixed-size array
|
||||
E0509, // cannot move out of type `..`, which defines the `Drop` trait
|
||||
E0524, // two closures require unique access to `..` at the same time
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue