Skip to content

Commit 52d6ae8

Browse files
committed
Reimplement some "add mut" suggestions under NLL
Specifically, `&self` -> `&mut self` and explicit `ref` -> `ref mut`. Implicit `ref` isn't handled yet and causes an ICE.
1 parent f8eb9a6 commit 52d6ae8

File tree

5 files changed

+88
-35
lines changed

5 files changed

+88
-35
lines changed

src/librustc_mir/borrow_check/mod.rs

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use rustc_data_structures::indexed_set::IdxSetBuf;
2929
use rustc_data_structures::indexed_vec::Idx;
3030
use rustc_data_structures::small_vec::SmallVec;
3131

32+
use core::unicode::property::Pattern_White_Space;
33+
3234
use std::rc::Rc;
3335

3436
use syntax_pos::Span;
@@ -1837,17 +1839,45 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18371839
Place::Projection(box Projection {
18381840
base: Place::Local(local),
18391841
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);
1842+
}) if self.mir.local_decls[*local].is_user_variable.is_some() => {
1843+
let local_decl = &self.mir.local_decls[*local];
1844+
let (err_help_span, suggested_code) = match local_decl.is_user_variable {
1845+
Some(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf)) => {
1846+
suggest_ampmut_self(local_decl)
1847+
},
1848+
1849+
Some(ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
1850+
binding_mode: ty::BindingMode::BindByValue(_),
1851+
opt_ty_info,
1852+
..
1853+
}))) => {
1854+
if let Some(x) = try_suggest_ampmut_rhs(
1855+
self.tcx, self.mir, *local,
1856+
) {
1857+
x
1858+
} else {
1859+
suggest_ampmut_type(local_decl, opt_ty_info)
1860+
}
1861+
},
1862+
1863+
Some(ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
1864+
binding_mode: ty::BindingMode::BindByReference(_),
1865+
..
1866+
}))) => {
1867+
suggest_ref_mut(self.tcx, local_decl)
1868+
},
1869+
1870+
Some(ClearCrossCrate::Clear) => bug!("saw cleared local state"),
1871+
1872+
None => bug!(),
1873+
};
1874+
18441875
err.span_suggestion(
18451876
err_help_span,
18461877
"consider changing this to be a mutable reference",
18471878
suggested_code,
18481879
);
18491880

1850-
let local_decl = &self.mir.local_decls[*local];
18511881
if let Some(name) = local_decl.name {
18521882
err.span_label(
18531883
span,
@@ -1874,13 +1904,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18741904
err.emit();
18751905
return true;
18761906

1877-
// Returns the span to highlight and the associated text to
1878-
// present when suggesting that the user use an `&mut`.
1879-
//
1907+
fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(
1908+
local_decl: &mir::LocalDecl<'tcx>,
1909+
) -> (Span, String) {
1910+
(local_decl.source_info.span, "&mut self".to_string())
1911+
}
1912+
18801913
// When we want to suggest a user change a local variable to be a `&mut`, there
18811914
// are three potential "obvious" things to highlight:
18821915
//
1883-
// let ident [: Type] [= RightHandSideExresssion];
1916+
// let ident [: Type] [= RightHandSideExpression];
18841917
// ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
18851918
// (1.) (2.) (3.)
18861919
//
@@ -1889,48 +1922,58 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18891922
// for example, if the RHS is present and the Type is not, then the type is going to
18901923
// be inferred *from* the RHS, which means we should highlight that (and suggest
18911924
// that they borrow the RHS mutably).
1892-
fn find_place_to_suggest_ampmut<'cx, 'gcx, 'tcx>(
1925+
//
1926+
// This implementation attempts to emulate AST-borrowck prioritization
1927+
// by trying (3.), then (2.) and finally falling back on (1.).
1928+
1929+
fn try_suggest_ampmut_rhs<'cx, 'gcx, 'tcx>(
18931930
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
18941931
mir: &Mir<'tcx>,
18951932
local: Local,
1896-
) -> (Span, String) {
1897-
// This implementation attempts to emulate AST-borrowck prioritization
1898-
// by trying (3.), then (2.) and finally falling back on (1.).
1933+
) -> Option<(Span, String)> {
18991934
let locations = mir.find_assignments(local);
19001935
if locations.len() > 0 {
19011936
let assignment_rhs_span = mir.source_info(locations[0]).span;
19021937
let snippet = tcx.sess.codemap().span_to_snippet(assignment_rhs_span);
19031938
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 ".
19081939
if src.starts_with('&') {
19091940
let borrowed_expr = src[1..].to_string();
1910-
return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
1941+
return Some((assignment_rhs_span, format!("&mut {}", borrowed_expr)));
19111942
}
19121943
}
19131944
}
1945+
None
1946+
}
19141947

1915-
let local_decl = &mir.local_decls[local];
1916-
let highlight_span = match local_decl.is_user_variable {
1948+
fn suggest_ampmut_type<'tcx>(
1949+
local_decl: &mir::LocalDecl<'tcx>,
1950+
opt_ty_info: Option<Span>,
1951+
) -> (Span, String) {
1952+
let highlight_span = match opt_ty_info {
19171953
// if this is a variable binding with an explicit type,
19181954
// 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"),
1955+
Some(ty_span) => ty_span,
19251956

19261957
// otherwise, just highlight the span associated with
19271958
// the (MIR) LocalDecl.
1928-
_ => local_decl.source_info.span,
1959+
None => local_decl.source_info.span,
19291960
};
19301961

19311962
let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
19321963
assert_eq!(ty_mut.mutbl, hir::MutImmutable);
1933-
return (highlight_span, format!("&mut {}", ty_mut.ty));
1964+
(highlight_span, format!("&mut {}", ty_mut.ty))
1965+
}
1966+
1967+
fn suggest_ref_mut<'cx, 'gcx, 'tcx>(
1968+
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
1969+
local_decl: &mir::LocalDecl<'tcx>,
1970+
) -> (Span, String) {
1971+
let hi_span = local_decl.source_info.span;
1972+
let hi_src = tcx.sess.codemap().span_to_snippet(hi_span).unwrap();
1973+
assert!(hi_src.starts_with("ref"));
1974+
assert!(hi_src["ref".len()..].starts_with(Pattern_White_Space));
1975+
let suggestion = format!("ref mut{}", &hi_src["ref".len()..]);
1976+
(hi_span, suggestion)
19341977
}
19351978
}
19361979

src/librustc_mir/lib.rs

Lines changed: 2 additions & 0 deletions
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/test/ui/did_you_mean/issue-38147-1.nll.stderr

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 3 deletions
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/suggestions/suggest-ref-mut.stderr

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
error[E0594]: cannot assign to `*foo` which is behind a `&` reference
22
--> $DIR/suggest-ref-mut.rs:17:5
33
|
4+
LL | let ref foo = 16;
5+
| ------- help: consider changing this to be a mutable reference: `ref mut foo`
6+
...
47
LL | *foo = 32;
5-
| ^^^^^^^^^ cannot assign
8+
| ^^^^^^^^^ `foo` is a `&` reference, so the data it refers to cannot be written
69

710
error[E0594]: cannot assign to `*bar` which is behind a `&` reference
811
--> $DIR/suggest-ref-mut.rs:22:9
912
|
13+
LL | if let Some(ref bar) = Some(16) {
14+
| ------- help: consider changing this to be a mutable reference: `ref mut bar`
15+
...
1016
LL | *bar = 32;
11-
| ^^^^^^^^^ cannot assign
17+
| ^^^^^^^^^ `bar` is a `&` reference, so the data it refers to cannot be written
1218

1319
error[E0594]: cannot assign to `*quo` which is behind a `&` reference
1420
--> $DIR/suggest-ref-mut.rs:26:22
1521
|
1622
LL | ref quo => { *quo = 32; },
17-
| ^^^^^^^^^ cannot assign
23+
| ------- ^^^^^^^^^ `quo` is a `&` reference, so the data it refers to cannot be written
24+
| |
25+
| help: consider changing this to be a mutable reference: `ref mut quo`
1826

1927
error: aborting due to 3 previous errors
2028

0 commit comments

Comments
 (0)