Skip to content

Commit cd8ee78

Browse files
committed
auto merge of #11718 : ktt3ja/rust/borrowck-error-msg, r=brson
A mutable and immutable borrow place some restrictions on what you can with the variable until the borrow ends. This commit attempts to convey to the user what those restrictions are. Also, if the original borrow is a mutable borrow, the error message has been changed (more specifically, i. "cannot borrow `x` as immutable because it is also borrowed as mutable" and ii. "cannot borrow `x` as mutable more than once" have been changed to "cannot borrow `x` because it is already borrowed as mutable"). In addition, this adds a (custom) span note to communicate where the original borrow ends. ```rust fn main() { match true { true => { let mut x = 1; let y = &x; let z = &mut x; } false => () } } test.rs:6:21: 6:27 error: cannot borrow `x` as mutable because it is already borrowed as immutable test.rs:6 let z = &mut x; ^~~~~~ test.rs:5:21: 5:23 note: previous borrow of `x` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `x` until the borrow ends test.rs:5 let y = &x; ^~ test.rs:7:10: 7:10 note: previous borrow ends here test.rs:3 true => { test.rs:4 let mut x = 1; test.rs:5 let y = &x; test.rs:6 let z = &mut x; test.rs:7 } ^ ``` ```rust fn foo3(t0: &mut &mut int) { let t1 = &mut *t0; let p: &int = &**t0; } fn main() {} test.rs:3:19: 3:24 error: cannot borrow `**t0` because it is already borrowed as mutable test.rs:3 let p: &int = &**t0; ^~~~~ test.rs:2:14: 2:22 note: previous borrow of `**t0` as mutable occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `**t0` until the borrow ends test.rs:2 let t1 = &mut *t0; ^~~~~~~~ test.rs:4:2: 4:2 note: previous borrow ends here test.rs:1 fn foo3(t0: &mut &mut int) { test.rs:2 let t1 = &mut *t0; test.rs:3 let p: &int = &**t0; test.rs:4 } ^ ``` For the "previous borrow ends here" note, if the span is too long (has too many lines), then only the first and last lines are printed, and the middle is replaced with dot dot dot: ```rust fn foo3(t0: &mut &mut int) { let t1 = &mut *t0; let p: &int = &**t0; } fn main() {} test.rs:3:19: 3:24 error: cannot borrow `**t0` because it is already borrowed as mutable test.rs:3 let p: &int = &**t0; ^~~~~ test.rs:2:14: 2:22 note: previous borrow of `**t0` as mutable occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `**t0` until the borrow ends test.rs:2 let t1 = &mut *t0; ^~~~~~~~ test.rs:7:2: 7:2 note: previous borrow ends here test.rs:1 fn foo3(t0: &mut &mut int) { ... test.rs:7 } ^ ``` (Sidenote: the `span_end_note` currently also has issue #11715)
2 parents bf9c255 + b3290d3 commit cd8ee78

File tree

9 files changed

+144
-33
lines changed

9 files changed

+144
-33
lines changed

src/librustc/driver/session.rs

+3
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ impl Session_ {
250250
pub fn span_note(&self, sp: Span, msg: &str) {
251251
self.span_diagnostic.span_note(sp, msg)
252252
}
253+
pub fn span_end_note(&self, sp: Span, msg: &str) {
254+
self.span_diagnostic.span_end_note(sp, msg)
255+
}
253256
pub fn note(&self, msg: &str) {
254257
self.span_diagnostic.handler().note(msg)
255258
}

src/librustc/middle/borrowck/check_loans.rs

+27-14
Original file line numberDiff line numberDiff line change
@@ -231,34 +231,47 @@ impl<'a> CheckLoanCtxt<'a> {
231231
if restr.loan_path != loan2.loan_path { continue; }
232232

233233
match (new_loan.mutbl, old_loan.mutbl) {
234-
(MutableMutability, MutableMutability) => {
234+
(_, MutableMutability) => {
235+
let var = self.bccx.loan_path_to_str(new_loan.loan_path);
235236
self.bccx.span_err(
236237
new_loan.span,
237-
format!("cannot borrow `{}` as mutable \
238-
more than once at a time",
239-
self.bccx.loan_path_to_str(new_loan.loan_path)));
238+
format!("cannot borrow `{}` because it is already \
239+
borrowed as mutable", var));
240240
self.bccx.span_note(
241241
old_loan.span,
242-
format!("previous borrow of `{}` as mutable occurs here",
243-
self.bccx.loan_path_to_str(new_loan.loan_path)));
244-
return false;
242+
format!("previous borrow of `{0}` as mutable occurs \
243+
here; the mutable borrow prevents subsequent \
244+
moves, borrows, or modification of `{0}` \
245+
until the borrow ends", var));
245246
}
246247

247-
_ => {
248+
(_, mutability) => {
248249
self.bccx.span_err(
249250
new_loan.span,
250251
format!("cannot borrow `{}` as {} because \
251-
it is also borrowed as {}",
252+
it is already borrowed as {}",
252253
self.bccx.loan_path_to_str(new_loan.loan_path),
253254
self.bccx.mut_to_str(new_loan.mutbl),
254255
self.bccx.mut_to_str(old_loan.mutbl)));
255-
self.bccx.span_note(
256-
old_loan.span,
257-
format!("previous borrow of `{}` occurs here",
258-
self.bccx.loan_path_to_str(new_loan.loan_path)));
259-
return false;
256+
257+
let var = self.bccx.loan_path_to_str(new_loan.loan_path);
258+
let mut note = format!("previous borrow of `{}` occurs \
259+
here", var);
260+
if mutability == ImmutableMutability {
261+
note.push_str(format!("; the immutable borrow prevents \
262+
subsequent moves or mutable
263+
borrows of `{}` until the
264+
borrow ends", var));
265+
}
266+
self.bccx.span_note(old_loan.span, note);
260267
}
261268
}
269+
270+
let old_loan_span = ast_map::node_span(self.tcx().items,
271+
old_loan.kill_scope);
272+
self.bccx.span_end_note(old_loan_span,
273+
"previous borrow ends here");
274+
return false;
262275
}
263276

264277
true

src/librustc/middle/borrowck/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,10 @@ impl BorrowckCtxt {
631631
self.tcx.sess.span_note(s, m);
632632
}
633633

634+
pub fn span_end_note(&self, s: Span, m: &str) {
635+
self.tcx.sess.span_end_note(s, m);
636+
}
637+
634638
pub fn bckerr_to_str(&self, err: BckError) -> ~str {
635639
match err.code {
636640
err_mutbl(lk) => {

src/libsyntax/codemap.rs

-4
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,6 @@ impl CodeMap {
308308
}
309309
}
310310

311-
pub fn adjust_span(&self, sp: Span) -> Span {
312-
sp
313-
}
314-
315311
pub fn span_to_str(&self, sp: Span) -> ~str {
316312
{
317313
let files = self.files.borrow();

src/libsyntax/diagnostic.rs

+76-12
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ use extra::term;
1919

2020
static BUG_REPORT_URL: &'static str =
2121
"http://static.rust-lang.org/doc/master/complement-bugreport.html";
22+
// maximum number of lines we will print for each error; arbitrary.
23+
static MAX_LINES: uint = 6u;
2224

2325
pub trait Emitter {
2426
fn emit(&self,
2527
cmsp: Option<(&codemap::CodeMap, Span)>,
2628
msg: &str,
2729
lvl: Level);
30+
fn custom_emit(&self, cm: &codemap::CodeMap,
31+
sp: Span, msg: &str, lvl: Level);
2832
}
2933

3034
/// This structure is used to signify that a task has failed with a fatal error
@@ -55,6 +59,9 @@ impl SpanHandler {
5559
pub fn span_note(@self, sp: Span, msg: &str) {
5660
self.handler.emit(Some((&*self.cm, sp)), msg, Note);
5761
}
62+
pub fn span_end_note(@self, sp: Span, msg: &str) {
63+
self.handler.custom_emit(&*self.cm, sp, msg, Note);
64+
}
5865
pub fn span_bug(@self, sp: Span, msg: &str) -> ! {
5966
self.span_fatal(sp, ice_msg(msg));
6067
}
@@ -122,6 +129,10 @@ impl Handler {
122129
lvl: Level) {
123130
self.emit.emit(cmsp, msg, lvl);
124131
}
132+
pub fn custom_emit(@self, cm: &codemap::CodeMap,
133+
sp: Span, msg: &str, lvl: Level) {
134+
self.emit.custom_emit(cm, sp, msg, lvl);
135+
}
125136
}
126137
127138
pub fn ice_msg(msg: &str) -> ~str {
@@ -239,17 +250,34 @@ impl Emitter for DefaultEmitter {
239250
msg: &str,
240251
lvl: Level) {
241252
match cmsp {
242-
Some((cm, sp)) => {
243-
let sp = cm.adjust_span(sp);
244-
let ss = cm.span_to_str(sp);
245-
let lines = cm.span_to_lines(sp);
246-
print_diagnostic(ss, lvl, msg);
247-
highlight_lines(cm, sp, lvl, lines);
248-
print_macro_backtrace(cm, sp);
249-
}
253+
Some((cm, sp)) => emit(cm, sp, msg, lvl, false),
250254
None => print_diagnostic("", lvl, msg),
251255
}
252256
}
257+
258+
fn custom_emit(&self, cm: &codemap::CodeMap,
259+
sp: Span, msg: &str, lvl: Level) {
260+
emit(cm, sp, msg, lvl, true);
261+
}
262+
}
263+
264+
fn emit(cm: &codemap::CodeMap, sp: Span,
265+
msg: &str, lvl: Level, custom: bool) {
266+
let ss = cm.span_to_str(sp);
267+
let lines = cm.span_to_lines(sp);
268+
if custom {
269+
// we want to tell compiletest/runtest to look at the last line of the
270+
// span (since `custom_highlight_lines` displays an arrow to the end of
271+
// the span)
272+
let span_end = Span { lo: sp.hi, hi: sp.hi, expn_info: sp.expn_info};
273+
let ses = cm.span_to_str(span_end);
274+
print_diagnostic(ses, lvl, msg);
275+
custom_highlight_lines(cm, sp, lvl, lines);
276+
} else {
277+
print_diagnostic(ss, lvl, msg);
278+
highlight_lines(cm, sp, lvl, lines);
279+
}
280+
print_macro_backtrace(cm, sp);
253281
}
254282

255283
fn highlight_lines(cm: &codemap::CodeMap,
@@ -260,12 +288,10 @@ fn highlight_lines(cm: &codemap::CodeMap,
260288
let mut err = io::stderr();
261289
let err = &mut err as &mut io::Writer;
262290

263-
// arbitrarily only print up to six lines of the error
264-
let max_lines = 6u;
265291
let mut elided = false;
266292
let mut display_lines = lines.lines.as_slice();
267-
if display_lines.len() > max_lines {
268-
display_lines = display_lines.slice(0u, max_lines);
293+
if display_lines.len() > MAX_LINES {
294+
display_lines = display_lines.slice(0u, MAX_LINES);
269295
elided = true;
270296
}
271297
// Print the offending lines
@@ -319,6 +345,44 @@ fn highlight_lines(cm: &codemap::CodeMap,
319345
}
320346
}
321347

348+
// Here are the differences between this and the normal `highlight_lines`:
349+
// `custom_highlight_lines` will always put arrow on the last byte of the
350+
// span (instead of the first byte). Also, when the span is too long (more
351+
// than 6 lines), `custom_highlight_lines` will print the first line, then
352+
// dot dot dot, then last line, whereas `highlight_lines` prints the first
353+
// six lines.
354+
fn custom_highlight_lines(cm: &codemap::CodeMap,
355+
sp: Span,
356+
lvl: Level,
357+
lines: &codemap::FileLines) {
358+
let fm = lines.file;
359+
let mut err = io::stderr();
360+
let err = &mut err as &mut io::Writer;
361+
362+
let lines = lines.lines.as_slice();
363+
if lines.len() > MAX_LINES {
364+
write!(err, "{}:{} {}\n", fm.name,
365+
lines[0] + 1, fm.get_line(lines[0] as int));
366+
write!(err, "...\n");
367+
let last_line = lines[lines.len()-1];
368+
write!(err, "{}:{} {}\n", fm.name,
369+
last_line + 1, fm.get_line(last_line as int));
370+
} else {
371+
for line in lines.iter() {
372+
write!(err, "{}:{} {}\n", fm.name,
373+
*line + 1, fm.get_line(*line as int));
374+
}
375+
}
376+
let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1]+1);
377+
let hi = cm.lookup_char_pos(sp.hi);
378+
// Span seems to use half-opened interval, so subtract 1
379+
let skip = last_line_start.len() + hi.col.to_uint() - 1;
380+
let mut s = ~"";
381+
skip.times(|| s.push_char(' '));
382+
s.push_char('^');
383+
print_maybe_styled(s + "\n", term::attr::ForegroundColor(lvl.color()));
384+
}
385+
322386
fn print_macro_backtrace(cm: &codemap::CodeMap, sp: Span) {
323387
for ei in sp.expn_info.iter() {
324388
let ss = ei.callee.span.as_ref().map_or(~"", |span| cm.span_to_str(*span));

src/test/compile-fail/borrowck-borrow-mut-object-twice.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ trait Foo {
1818

1919
fn test(x: &mut Foo) {
2020
let _y = x.f1();
21-
x.f2(); //~ ERROR cannot borrow `*x` as mutable more than once at a time
21+
x.f2(); //~ ERROR cannot borrow `*x` because it is already borrowed as mutable
2222
}
2323

2424
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#[allow(dead_code)];
2+
fn main() {
3+
// Original borrow ends at end of function
4+
let mut x = 1u;
5+
let y = &mut x;
6+
let z = &x; //~ ERROR cannot borrow
7+
}
8+
//~^ NOTE previous borrow ends here
9+
10+
fn foo() {
11+
match true {
12+
true => {
13+
// Original borrow ends at end of match arm
14+
let mut x = 1u;
15+
let y = &x;
16+
let z = &mut x; //~ ERROR cannot borrow
17+
}
18+
//~^ NOTE previous borrow ends here
19+
false => ()
20+
}
21+
}
22+
23+
fn bar() {
24+
// Original borrow ends at end of closure
25+
|| {
26+
let mut x = 1u;
27+
let y = &mut x;
28+
let z = &mut x; //~ ERROR cannot borrow
29+
};
30+
//~^ NOTE previous borrow ends here
31+
}

src/test/compile-fail/mut-cant-alias.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ fn main() {
1414
let m = RefCell::new(0);
1515
let mut b = m.borrow_mut();
1616
let b1 = b.get();
17-
let b2 = b.get(); //~ ERROR cannot borrow `b` as mutable more than once at a time
17+
let b2 = b.get(); //~ ERROR cannot borrow `b` because it is already borrowed as mutable
1818
}

src/test/compile-fail/vec-mut-iter-borrow.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ fn main() {
1212
let mut xs = ~[1, 2, 3, 4];
1313

1414
for x in xs.mut_iter() {
15-
xs.push(1) //~ ERROR cannot borrow `xs` as mutable
15+
xs.push(1) //~ ERROR cannot borrow `xs` because it is already borrowed as mutable
1616
}
1717
}

0 commit comments

Comments
 (0)