Suggest not mutably borrowing a mutable reference
This commit is concerned with the case where the user tries to mutably borrow a mutable reference, thereby triggering an error. Instead of the existing suggestion to make the binding mutable, the compiler will now suggest to avoid borrowing altogether.
This commit is contained in:
parent
aa094a43cc
commit
3303e6847b
6 changed files with 114 additions and 39 deletions
|
@ -899,7 +899,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.note_and_explain_mutbl_error(&mut db, &err, &error_span);
|
self.note_and_explain_mutbl_error(&mut db, &err, &error_span);
|
||||||
self.note_immutability_blame(&mut db, err.cmt.immutability_blame());
|
self.note_immutability_blame(&mut db, err.cmt.immutability_blame(), err.cmt.id);
|
||||||
db.emit();
|
db.emit();
|
||||||
}
|
}
|
||||||
err_out_of_scope(super_scope, sub_scope, cause) => {
|
err_out_of_scope(super_scope, sub_scope, cause) => {
|
||||||
|
@ -1105,7 +1105,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||||
Origin::Ast)
|
Origin::Ast)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.note_immutability_blame(&mut err, blame);
|
self.note_immutability_blame(&mut err, blame, cmt.id);
|
||||||
|
|
||||||
if is_closure {
|
if is_closure {
|
||||||
err.help("closures behind references must be called via `&mut`");
|
err.help("closures behind references must be called via `&mut`");
|
||||||
|
@ -1184,25 +1184,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
fn note_immutability_blame(&self,
|
fn note_immutability_blame(&self,
|
||||||
db: &mut DiagnosticBuilder,
|
db: &mut DiagnosticBuilder,
|
||||||
blame: Option<ImmutabilityBlame>) {
|
blame: Option<ImmutabilityBlame>,
|
||||||
|
error_node_id: ast::NodeId) {
|
||||||
match blame {
|
match blame {
|
||||||
None => {}
|
None => {}
|
||||||
Some(ImmutabilityBlame::ClosureEnv(_)) => {}
|
Some(ImmutabilityBlame::ClosureEnv(_)) => {}
|
||||||
Some(ImmutabilityBlame::ImmLocal(node_id)) => {
|
Some(ImmutabilityBlame::ImmLocal(node_id)) => {
|
||||||
let let_span = self.tcx.hir.span(node_id);
|
self.note_immutable_local(db, error_node_id, node_id)
|
||||||
if let ty::BindByValue(..) = self.local_binding_mode(node_id) {
|
|
||||||
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) {
|
|
||||||
let (_, is_implicit_self) = self.local_ty(node_id);
|
|
||||||
if is_implicit_self && snippet != "self" {
|
|
||||||
// avoid suggesting `mut &self`.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
db.span_label(
|
|
||||||
let_span,
|
|
||||||
format!("consider changing this to `mut {}`", snippet)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(ImmutabilityBlame::LocalDeref(node_id)) => {
|
Some(ImmutabilityBlame::LocalDeref(node_id)) => {
|
||||||
let let_span = self.tcx.hir.span(node_id);
|
let let_span = self.tcx.hir.span(node_id);
|
||||||
|
@ -1242,6 +1230,46 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Suggest a fix when trying to mutably borrow an immutable local
|
||||||
|
// binding: either to make the binding mutable (if its type is
|
||||||
|
// not a mutable reference) or to avoid borrowing altogether
|
||||||
|
fn note_immutable_local(&self,
|
||||||
|
db: &mut DiagnosticBuilder,
|
||||||
|
borrowed_node_id: ast::NodeId,
|
||||||
|
binding_node_id: ast::NodeId) {
|
||||||
|
let let_span = self.tcx.hir.span(binding_node_id);
|
||||||
|
if let ty::BindByValue(..) = self.local_binding_mode(binding_node_id) {
|
||||||
|
if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) {
|
||||||
|
let (ty, is_implicit_self) = self.local_ty(binding_node_id);
|
||||||
|
if is_implicit_self && snippet != "self" {
|
||||||
|
// avoid suggesting `mut &self`.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let Some(&hir::TyRptr(
|
||||||
|
_,
|
||||||
|
hir::MutTy {
|
||||||
|
mutbl: hir::MutMutable,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
)) = ty.map(|t| &t.node)
|
||||||
|
{
|
||||||
|
let borrow_expr_id = self.tcx.hir.get_parent_node(borrowed_node_id);
|
||||||
|
db.span_suggestion(
|
||||||
|
self.tcx.hir.span(borrow_expr_id),
|
||||||
|
"consider removing the `&mut`, as it is an \
|
||||||
|
immutable binding to a mutable reference",
|
||||||
|
snippet
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
db.span_label(
|
||||||
|
let_span,
|
||||||
|
format!("consider changing this to `mut {}`", snippet),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn report_out_of_scope_escaping_closure_capture(&self,
|
fn report_out_of_scope_escaping_closure_capture(&self,
|
||||||
err: &BckError<'a, 'tcx>,
|
err: &BckError<'a, 'tcx>,
|
||||||
capture_span: Span)
|
capture_span: Span)
|
||||||
|
|
|
@ -5190,36 +5190,36 @@ impl<'a> Parser<'a> {
|
||||||
// Only a limited set of initial token sequences is considered self parameters, anything
|
// Only a limited set of initial token sequences is considered self parameters, anything
|
||||||
// else is parsed as a normal function parameter list, so some lookahead is required.
|
// else is parsed as a normal function parameter list, so some lookahead is required.
|
||||||
let eself_lo = self.span;
|
let eself_lo = self.span;
|
||||||
let (eself, eself_ident) = match self.token {
|
let (eself, eself_ident, eself_hi) = match self.token {
|
||||||
token::BinOp(token::And) => {
|
token::BinOp(token::And) => {
|
||||||
// &self
|
// &self
|
||||||
// &mut self
|
// &mut self
|
||||||
// &'lt self
|
// &'lt self
|
||||||
// &'lt mut self
|
// &'lt mut self
|
||||||
// ¬_self
|
// ¬_self
|
||||||
if isolated_self(self, 1) {
|
(if isolated_self(self, 1) {
|
||||||
self.bump();
|
self.bump();
|
||||||
(SelfKind::Region(None, Mutability::Immutable), expect_ident(self))
|
SelfKind::Region(None, Mutability::Immutable)
|
||||||
} else if self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
|
} else if self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
|
||||||
isolated_self(self, 2) {
|
isolated_self(self, 2) {
|
||||||
self.bump();
|
self.bump();
|
||||||
self.bump();
|
self.bump();
|
||||||
(SelfKind::Region(None, Mutability::Mutable), expect_ident(self))
|
SelfKind::Region(None, Mutability::Mutable)
|
||||||
} else if self.look_ahead(1, |t| t.is_lifetime()) &&
|
} else if self.look_ahead(1, |t| t.is_lifetime()) &&
|
||||||
isolated_self(self, 2) {
|
isolated_self(self, 2) {
|
||||||
self.bump();
|
self.bump();
|
||||||
let lt = self.expect_lifetime();
|
let lt = self.expect_lifetime();
|
||||||
(SelfKind::Region(Some(lt), Mutability::Immutable), expect_ident(self))
|
SelfKind::Region(Some(lt), Mutability::Immutable)
|
||||||
} else if self.look_ahead(1, |t| t.is_lifetime()) &&
|
} else if self.look_ahead(1, |t| t.is_lifetime()) &&
|
||||||
self.look_ahead(2, |t| t.is_keyword(keywords::Mut)) &&
|
self.look_ahead(2, |t| t.is_keyword(keywords::Mut)) &&
|
||||||
isolated_self(self, 3) {
|
isolated_self(self, 3) {
|
||||||
self.bump();
|
self.bump();
|
||||||
let lt = self.expect_lifetime();
|
let lt = self.expect_lifetime();
|
||||||
self.bump();
|
self.bump();
|
||||||
(SelfKind::Region(Some(lt), Mutability::Mutable), expect_ident(self))
|
SelfKind::Region(Some(lt), Mutability::Mutable)
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}, expect_ident(self), self.prev_span)
|
||||||
}
|
}
|
||||||
token::BinOp(token::Star) => {
|
token::BinOp(token::Star) => {
|
||||||
// *self
|
// *self
|
||||||
|
@ -5227,43 +5227,45 @@ impl<'a> Parser<'a> {
|
||||||
// *mut self
|
// *mut self
|
||||||
// *not_self
|
// *not_self
|
||||||
// Emit special error for `self` cases.
|
// Emit special error for `self` cases.
|
||||||
if isolated_self(self, 1) {
|
(if isolated_self(self, 1) {
|
||||||
self.bump();
|
self.bump();
|
||||||
self.span_err(self.span, "cannot pass `self` by raw pointer");
|
self.span_err(self.span, "cannot pass `self` by raw pointer");
|
||||||
(SelfKind::Value(Mutability::Immutable), expect_ident(self))
|
SelfKind::Value(Mutability::Immutable)
|
||||||
} else if self.look_ahead(1, |t| t.is_mutability()) &&
|
} else if self.look_ahead(1, |t| t.is_mutability()) &&
|
||||||
isolated_self(self, 2) {
|
isolated_self(self, 2) {
|
||||||
self.bump();
|
self.bump();
|
||||||
self.bump();
|
self.bump();
|
||||||
self.span_err(self.span, "cannot pass `self` by raw pointer");
|
self.span_err(self.span, "cannot pass `self` by raw pointer");
|
||||||
(SelfKind::Value(Mutability::Immutable), expect_ident(self))
|
SelfKind::Value(Mutability::Immutable)
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}, expect_ident(self), self.prev_span)
|
||||||
}
|
}
|
||||||
token::Ident(..) => {
|
token::Ident(..) => {
|
||||||
if isolated_self(self, 0) {
|
if isolated_self(self, 0) {
|
||||||
// self
|
// self
|
||||||
// self: TYPE
|
// self: TYPE
|
||||||
let eself_ident = expect_ident(self);
|
let eself_ident = expect_ident(self);
|
||||||
if self.eat(&token::Colon) {
|
let eself_hi = self.prev_span;
|
||||||
|
(if self.eat(&token::Colon) {
|
||||||
let ty = self.parse_ty()?;
|
let ty = self.parse_ty()?;
|
||||||
(SelfKind::Explicit(ty, Mutability::Immutable), eself_ident)
|
SelfKind::Explicit(ty, Mutability::Immutable)
|
||||||
} else {
|
} else {
|
||||||
(SelfKind::Value(Mutability::Immutable), eself_ident)
|
SelfKind::Value(Mutability::Immutable)
|
||||||
}
|
}, eself_ident, eself_hi)
|
||||||
} else if self.token.is_keyword(keywords::Mut) &&
|
} else if self.token.is_keyword(keywords::Mut) &&
|
||||||
isolated_self(self, 1) {
|
isolated_self(self, 1) {
|
||||||
// mut self
|
// mut self
|
||||||
// mut self: TYPE
|
// mut self: TYPE
|
||||||
self.bump();
|
self.bump();
|
||||||
let eself_ident = expect_ident(self);
|
let eself_ident = expect_ident(self);
|
||||||
if self.eat(&token::Colon) {
|
let eself_hi = self.prev_span;
|
||||||
|
(if self.eat(&token::Colon) {
|
||||||
let ty = self.parse_ty()?;
|
let ty = self.parse_ty()?;
|
||||||
(SelfKind::Explicit(ty, Mutability::Mutable), eself_ident)
|
SelfKind::Explicit(ty, Mutability::Mutable)
|
||||||
} else {
|
} else {
|
||||||
(SelfKind::Value(Mutability::Mutable), eself_ident)
|
SelfKind::Value(Mutability::Mutable)
|
||||||
}
|
}, eself_ident, eself_hi)
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
@ -5271,7 +5273,7 @@ impl<'a> Parser<'a> {
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let eself = codemap::respan(eself_lo.to(self.prev_span), eself);
|
let eself = codemap::respan(eself_lo.to(eself_hi), eself);
|
||||||
Ok(Some(Arg::from_self(eself, eself_ident)))
|
Ok(Some(Arg::from_self(eself, eself_ident)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
Normal file
9
src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
error[E0596]: cannot borrow immutable item `b` as mutable
|
||||||
|
--> $DIR/mut-borrow-of-mut-ref.rs:18:7
|
||||||
|
|
|
||||||
|
LL | g(&mut b) //~ ERROR cannot borrow
|
||||||
|
| ^^^^^^ cannot borrow as mutable
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0596`.
|
21
src/test/ui/borrowck/mut-borrow-of-mut-ref.rs
Normal file
21
src/test/ui/borrowck/mut-borrow-of-mut-ref.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 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.
|
||||||
|
|
||||||
|
// Suggest not mutably borrowing a mutable reference
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f(&mut 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f(b: &mut i32) {
|
||||||
|
g(&mut b) //~ ERROR cannot borrow
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g(_: &mut i32) {}
|
13
src/test/ui/borrowck/mut-borrow-of-mut-ref.stderr
Normal file
13
src/test/ui/borrowck/mut-borrow-of-mut-ref.stderr
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
error[E0596]: cannot borrow immutable argument `b` as mutable
|
||||||
|
--> $DIR/mut-borrow-of-mut-ref.rs:18:12
|
||||||
|
|
|
||||||
|
LL | g(&mut b) //~ ERROR cannot borrow
|
||||||
|
| ^ cannot borrow mutably
|
||||||
|
help: consider removing the `&mut`, as it is an immutable binding to a mutable reference
|
||||||
|
|
|
||||||
|
LL | g(b) //~ ERROR cannot borrow
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0596`.
|
|
@ -10,10 +10,12 @@ LL | (&mut self).bar(); //~ ERROR cannot borrow
|
||||||
error[E0596]: cannot borrow immutable argument `self` as mutable
|
error[E0596]: cannot borrow immutable argument `self` as mutable
|
||||||
--> $DIR/issue-31424.rs:23:15
|
--> $DIR/issue-31424.rs:23:15
|
||||||
|
|
|
|
||||||
LL | fn bar(self: &mut Self) {
|
|
||||||
| --------------- consider changing this to `mut self: &mut Self`
|
|
||||||
LL | (&mut self).bar(); //~ ERROR cannot borrow
|
LL | (&mut self).bar(); //~ ERROR cannot borrow
|
||||||
| ^^^^ cannot borrow mutably
|
| ^^^^ cannot borrow mutably
|
||||||
|
help: consider removing the `&mut`, as it is an immutable binding to a mutable reference
|
||||||
|
|
|
||||||
|
LL | self.bar(); //~ ERROR cannot borrow
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue