Skip to content

Commit fe29a4c

Browse files
committed
Auto merge of #52242 - ashtneoi:suggest-ref-mut, r=pnkfelix
NLL: Suggest `ref mut` and `&mut self` Fixes #51244. Supersedes #51249, I think. Under the old lexical lifetimes, the compiler provided helpful suggestions about adding `mut` when you tried to mutate a variable bound as `&self` or (explicit) `ref`. NLL doesn't have those suggestions yet. This pull request adds them. I didn't bother making the help text exactly the same as without NLL, but I can if that's important. (Originally this was supposed to be part of #51612, but I got bogged down trying to fit everything in one PR.)
2 parents bce32b5 + 1ed8619 commit fe29a4c

File tree

17 files changed

+239
-60
lines changed

17 files changed

+239
-60
lines changed

src/librustc_borrowck/borrowck/mod.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use rustc::middle::free_region::RegionRelations;
3939
use rustc::ty::{self, Ty, TyCtxt};
4040
use rustc::ty::query::Providers;
4141
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
42+
use rustc_mir::util::suggest_ref_mut;
4243
use rustc::util::nodemap::FxHashSet;
4344

4445
use std::cell::RefCell;
@@ -1206,15 +1207,15 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
12061207
self.note_immutable_local(db, error_node_id, node_id)
12071208
}
12081209
Some(ImmutabilityBlame::LocalDeref(node_id)) => {
1209-
let let_span = self.tcx.hir.span(node_id);
12101210
match self.local_binding_mode(node_id) {
12111211
ty::BindByReference(..) => {
1212-
let snippet = self.tcx.sess.codemap().span_to_snippet(let_span);
1213-
if let Ok(snippet) = snippet {
1214-
db.span_label(
1212+
let let_span = self.tcx.hir.span(node_id);
1213+
let suggestion = suggest_ref_mut(self.tcx, let_span);
1214+
if let Some((let_span, replace_str)) = suggestion {
1215+
db.span_suggestion(
12151216
let_span,
1216-
format!("consider changing this to `{}`",
1217-
snippet.replace("ref ", "ref mut "))
1217+
"use a mutable reference instead",
1218+
replace_str,
12181219
);
12191220
}
12201221
}

src/librustc_mir/borrow_check/mod.rs

+56-32
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use dataflow::{EverInitializedPlaces, MovingOutStatements};
4444
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
4545
use util::borrowck_errors::{BorrowckErrors, Origin};
4646
use util::collect_writes::FindAssignments;
47+
use util::suggest_ref_mut;
4748

4849
use self::borrow_set::{BorrowData, BorrowSet};
4950
use self::flows::Flows;
@@ -1837,17 +1838,41 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18371838
Place::Projection(box Projection {
18381839
base: Place::Local(local),
18391840
elem: ProjectionElem::Deref,
1840-
}) if self.mir.local_decls[*local].is_nonref_binding() =>
1841-
{
1842-
let (err_help_span, suggested_code) =
1843-
find_place_to_suggest_ampmut(self.tcx, self.mir, *local);
1844-
err.span_suggestion(
1845-
err_help_span,
1846-
"consider changing this to be a mutable reference",
1847-
suggested_code,
1848-
);
1849-
1841+
}) if self.mir.local_decls[*local].is_user_variable.is_some() => {
18501842
let local_decl = &self.mir.local_decls[*local];
1843+
let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
1844+
ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => {
1845+
Some(suggest_ampmut_self(local_decl))
1846+
},
1847+
1848+
ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
1849+
binding_mode: ty::BindingMode::BindByValue(_),
1850+
opt_ty_info,
1851+
..
1852+
})) => Some(suggest_ampmut(
1853+
self.tcx,
1854+
self.mir,
1855+
*local,
1856+
local_decl,
1857+
*opt_ty_info,
1858+
)),
1859+
1860+
ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
1861+
binding_mode: ty::BindingMode::BindByReference(_),
1862+
..
1863+
})) => suggest_ref_mut(self.tcx, local_decl.source_info.span),
1864+
1865+
ClearCrossCrate::Clear => bug!("saw cleared local state"),
1866+
};
1867+
1868+
if let Some((err_help_span, suggested_code)) = suggestion {
1869+
err.span_suggestion(
1870+
err_help_span,
1871+
"consider changing this to be a mutable reference",
1872+
suggested_code,
1873+
);
1874+
}
1875+
18511876
if let Some(name) = local_decl.name {
18521877
err.span_label(
18531878
span,
@@ -1874,13 +1899,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18741899
err.emit();
18751900
return true;
18761901

1877-
// Returns the span to highlight and the associated text to
1878-
// present when suggesting that the user use an `&mut`.
1879-
//
1902+
fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(
1903+
local_decl: &mir::LocalDecl<'tcx>,
1904+
) -> (Span, String) {
1905+
(local_decl.source_info.span, "&mut self".to_string())
1906+
}
1907+
18801908
// When we want to suggest a user change a local variable to be a `&mut`, there
18811909
// are three potential "obvious" things to highlight:
18821910
//
1883-
// let ident [: Type] [= RightHandSideExresssion];
1911+
// let ident [: Type] [= RightHandSideExpression];
18841912
// ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
18851913
// (1.) (2.) (3.)
18861914
//
@@ -1889,48 +1917,44 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18891917
// for example, if the RHS is present and the Type is not, then the type is going to
18901918
// be inferred *from* the RHS, which means we should highlight that (and suggest
18911919
// that they borrow the RHS mutably).
1892-
fn find_place_to_suggest_ampmut<'cx, 'gcx, 'tcx>(
1920+
//
1921+
// This implementation attempts to emulate AST-borrowck prioritization
1922+
// by trying (3.), then (2.) and finally falling back on (1.).
1923+
fn suggest_ampmut<'cx, 'gcx, 'tcx>(
18931924
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
18941925
mir: &Mir<'tcx>,
18951926
local: Local,
1927+
local_decl: &mir::LocalDecl<'tcx>,
1928+
opt_ty_info: Option<Span>,
18961929
) -> (Span, String) {
1897-
// This implementation attempts to emulate AST-borrowck prioritization
1898-
// by trying (3.), then (2.) and finally falling back on (1.).
18991930
let locations = mir.find_assignments(local);
19001931
if locations.len() > 0 {
19011932
let assignment_rhs_span = mir.source_info(locations[0]).span;
19021933
let snippet = tcx.sess.codemap().span_to_snippet(assignment_rhs_span);
19031934
if let Ok(src) = snippet {
1904-
// pnkfelix inherited code; believes intention is
1905-
// highlighted text will always be `&<expr>` and
1906-
// thus can transform to `&mut` by slicing off
1907-
// first ASCII character and prepending "&mut ".
19081935
if src.starts_with('&') {
19091936
let borrowed_expr = src[1..].to_string();
1910-
return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
1937+
return (
1938+
assignment_rhs_span,
1939+
format!("&mut {}", borrowed_expr),
1940+
);
19111941
}
19121942
}
19131943
}
19141944

1915-
let local_decl = &mir.local_decls[local];
1916-
let highlight_span = match local_decl.is_user_variable {
1945+
let highlight_span = match opt_ty_info {
19171946
// if this is a variable binding with an explicit type,
19181947
// try to highlight that for the suggestion.
1919-
Some(ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
1920-
opt_ty_info: Some(ty_span),
1921-
..
1922-
}))) => ty_span,
1923-
1924-
Some(ClearCrossCrate::Clear) => bug!("saw cleared local state"),
1948+
Some(ty_span) => ty_span,
19251949

19261950
// otherwise, just highlight the span associated with
19271951
// the (MIR) LocalDecl.
1928-
_ => local_decl.source_info.span,
1952+
None => local_decl.source_info.span,
19291953
};
19301954

19311955
let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
19321956
assert_eq!(ty_mut.mutbl, hir::MutImmutable);
1933-
return (highlight_span, format!("&mut {}", ty_mut.ty));
1957+
(highlight_span, format!("&mut {}", ty_mut.ty))
19341958
}
19351959
}
19361960

src/librustc_mir/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
3333
#![feature(never_type)]
3434
#![feature(specialization)]
3535
#![feature(try_trait)]
36+
#![feature(unicode_internals)]
3637

3738
#![recursion_limit="256"]
3839

@@ -56,6 +57,7 @@ extern crate rustc_target;
5657
extern crate log_settings;
5758
extern crate rustc_apfloat;
5859
extern crate byteorder;
60+
extern crate core;
5961

6062
mod diagnostics;
6163

src/librustc_mir/util/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use core::unicode::property::Pattern_White_Space;
12+
use rustc::ty;
13+
use syntax_pos::Span;
14+
1115
pub mod borrowck_errors;
1216
pub mod elaborate_drops;
1317
pub mod def_use;
@@ -23,3 +27,19 @@ pub use self::alignment::is_disaligned;
2327
pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};
2428
pub use self::graphviz::{write_mir_graphviz};
2529
pub use self::graphviz::write_node_label as write_graphviz_node_label;
30+
31+
/// If possible, suggest replacing `ref` with `ref mut`.
32+
pub fn suggest_ref_mut<'cx, 'gcx, 'tcx>(
33+
tcx: ty::TyCtxt<'cx, 'gcx, 'tcx>,
34+
pattern_span: Span,
35+
) -> Option<(Span, String)> {
36+
let hi_src = tcx.sess.codemap().span_to_snippet(pattern_span).unwrap();
37+
if hi_src.starts_with("ref")
38+
&& hi_src["ref".len()..].starts_with(Pattern_White_Space)
39+
{
40+
let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
41+
Some((pattern_span, replacement))
42+
} else {
43+
None
44+
}
45+
}

src/test/ui/did_you_mean/issue-38147-1.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0596]: cannot borrow immutable item `*self.s` as mutable
22
--> $DIR/issue-38147-1.rs:27:9
33
|
44
LL | fn f(&self) {
5-
| ----- help: consider changing this to be a mutable reference: `&mut Foo<'_>`
5+
| ----- help: consider changing this to be a mutable reference: `&mut self`
66
LL | self.s.push('x'); //~ ERROR cannot borrow data mutably
77
| ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
88

src/test/ui/did_you_mean/issue-39544.nll.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ error[E0596]: cannot borrow immutable item `self.x` as mutable
1010
--> $DIR/issue-39544.rs:26:17
1111
|
1212
LL | fn foo<'z>(&'z self) {
13-
| -------- help: consider changing this to be a mutable reference: `&mut Z`
13+
| -------- help: consider changing this to be a mutable reference: `&mut self`
1414
LL | let _ = &mut self.x; //~ ERROR cannot borrow
1515
| ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
1616

1717
error[E0596]: cannot borrow immutable item `self.x` as mutable
1818
--> $DIR/issue-39544.rs:30:17
1919
|
2020
LL | fn foo1(&self, other: &Z) {
21-
| ----- help: consider changing this to be a mutable reference: `&mut Z`
21+
| ----- help: consider changing this to be a mutable reference: `&mut self`
2222
LL | let _ = &mut self.x; //~ ERROR cannot borrow
2323
| ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
2424

@@ -35,7 +35,7 @@ error[E0596]: cannot borrow immutable item `self.x` as mutable
3535
--> $DIR/issue-39544.rs:35:17
3636
|
3737
LL | fn foo2<'a>(&'a self, other: &Z) {
38-
| -------- help: consider changing this to be a mutable reference: `&mut Z`
38+
| -------- help: consider changing this to be a mutable reference: `&mut self`
3939
LL | let _ = &mut self.x; //~ ERROR cannot borrow
4040
| ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
4141

src/test/ui/nll/issue-51244.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(nll)]
12+
13+
fn main() {
14+
let ref my_ref @ _ = 0;
15+
*my_ref = 0;
16+
//~^ ERROR cannot assign to `*my_ref` which is behind a `&` reference [E0594]
17+
}

src/test/ui/nll/issue-51244.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0594]: cannot assign to `*my_ref` which is behind a `&` reference
2+
--> $DIR/issue-51244.rs:15:5
3+
|
4+
LL | let ref my_ref @ _ = 0;
5+
| -------------- help: consider changing this to be a mutable reference: `ref mut my_ref @ _`
6+
LL | *my_ref = 0;
7+
| ^^^^^^^^^^^ `my_ref` is a `&` reference, so the data it refers to cannot be written
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0594`.

src/test/ui/rfc-2005-default-binding-mode/enum.nll.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ error[E0594]: cannot assign to `*x` which is behind a `&` reference
22
--> $DIR/enum.rs:19:5
33
|
44
LL | *x += 1; //~ ERROR cannot assign to immutable
5-
| ^^^^^^^ cannot assign
5+
| ^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
66

77
error[E0594]: cannot assign to `*x` which is behind a `&` reference
88
--> $DIR/enum.rs:23:9
99
|
1010
LL | *x += 1; //~ ERROR cannot assign to immutable
11-
| ^^^^^^^ cannot assign
11+
| ^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
1212

1313
error[E0594]: cannot assign to `*x` which is behind a `&` reference
1414
--> $DIR/enum.rs:29:9
1515
|
1616
LL | *x += 1; //~ ERROR cannot assign to immutable
17-
| ^^^^^^^ cannot assign
17+
| ^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
1818

1919
error: aborting due to 3 previous errors
2020

src/test/ui/rfc-2005-default-binding-mode/enum.stderr

-6
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
error[E0594]: cannot assign to immutable borrowed content `*x`
22
--> $DIR/enum.rs:19:5
33
|
4-
LL | let Wrap(x) = &Wrap(3);
5-
| - consider changing this to `x`
64
LL | *x += 1; //~ ERROR cannot assign to immutable
75
| ^^^^^^^ cannot borrow as mutable
86

97
error[E0594]: cannot assign to immutable borrowed content `*x`
108
--> $DIR/enum.rs:23:9
119
|
12-
LL | if let Some(x) = &Some(3) {
13-
| - consider changing this to `x`
1410
LL | *x += 1; //~ ERROR cannot assign to immutable
1511
| ^^^^^^^ cannot borrow as mutable
1612

1713
error[E0594]: cannot assign to immutable borrowed content `*x`
1814
--> $DIR/enum.rs:29:9
1915
|
20-
LL | while let Some(x) = &Some(3) {
21-
| - consider changing this to `x`
2216
LL | *x += 1; //~ ERROR cannot assign to immutable
2317
| ^^^^^^^ cannot borrow as mutable
2418

src/test/ui/rfc-2005-default-binding-mode/explicit-mut.nll.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ error[E0594]: cannot assign to `*n` which is behind a `&` reference
22
--> $DIR/explicit-mut.rs:17:13
33
|
44
LL | *n += 1; //~ ERROR cannot assign to immutable
5-
| ^^^^^^^ cannot assign
5+
| ^^^^^^^ `n` is a `&` reference, so the data it refers to cannot be written
66

77
error[E0594]: cannot assign to `*n` which is behind a `&` reference
88
--> $DIR/explicit-mut.rs:25:13
99
|
1010
LL | *n += 1; //~ ERROR cannot assign to immutable
11-
| ^^^^^^^ cannot assign
11+
| ^^^^^^^ `n` is a `&` reference, so the data it refers to cannot be written
1212

1313
error[E0594]: cannot assign to `*n` which is behind a `&` reference
1414
--> $DIR/explicit-mut.rs:33:13
1515
|
1616
LL | *n += 1; //~ ERROR cannot assign to immutable
17-
| ^^^^^^^ cannot assign
17+
| ^^^^^^^ `n` is a `&` reference, so the data it refers to cannot be written
1818

1919
error: aborting due to 3 previous errors
2020

src/test/ui/rfc-2005-default-binding-mode/explicit-mut.stderr

-6
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
11
error[E0594]: cannot assign to immutable borrowed content `*n`
22
--> $DIR/explicit-mut.rs:17:13
33
|
4-
LL | Some(n) => {
5-
| - consider changing this to `n`
64
LL | *n += 1; //~ ERROR cannot assign to immutable
75
| ^^^^^^^ cannot borrow as mutable
86

97
error[E0594]: cannot assign to immutable borrowed content `*n`
108
--> $DIR/explicit-mut.rs:25:13
119
|
12-
LL | Some(n) => {
13-
| - consider changing this to `n`
1410
LL | *n += 1; //~ ERROR cannot assign to immutable
1511
| ^^^^^^^ cannot borrow as mutable
1612

1713
error[E0594]: cannot assign to immutable borrowed content `*n`
1814
--> $DIR/explicit-mut.rs:33:13
1915
|
20-
LL | Some(n) => {
21-
| - consider changing this to `n`
2216
LL | *n += 1; //~ ERROR cannot assign to immutable
2317
| ^^^^^^^ cannot borrow as mutable
2418

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0594]: cannot assign to `*my_ref` which is behind a `&` reference
2+
--> $DIR/issue-51244.rs:13:5
3+
|
4+
LL | let ref my_ref @ _ = 0;
5+
| -------------- help: consider changing this to be a mutable reference: `ref mut my_ref @ _`
6+
LL | *my_ref = 0; //~ ERROR cannot assign to immutable borrowed content `*my_ref` [E0594]
7+
| ^^^^^^^^^^^ `my_ref` is a `&` reference, so the data it refers to cannot be written
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0594`.

0 commit comments

Comments
 (0)