Skip to content

Commit 6ddab3e

Browse files
committed
Auto merge of #54720 - davidtwco:issue-51191, r=nikomatsakis
NLL fails to suggest "try removing `&mut` here" Fixes #51191. This PR adds ``try removing `&mut` here`` suggestions to functions where a mutable borrow is being taken of a `&mut self` or a `self: &mut Self`. This PR also enables the suggestion for adding a `mut` pattern to by-value implicit self arguments without `mut` patterns already. r? @nikomatsakis
2 parents 4cf1176 + 2be3069 commit 6ddab3e

File tree

11 files changed

+232
-30
lines changed

11 files changed

+232
-30
lines changed

src/librustc/hir/lowering.rs

+25-5
Original file line numberDiff line numberDiff line change
@@ -2044,11 +2044,31 @@ impl<'a> LoweringContext<'a> {
20442044
inputs,
20452045
output,
20462046
variadic: decl.variadic,
2047-
has_implicit_self: decl.inputs.get(0).map_or(false, |arg| match arg.ty.node {
2048-
TyKind::ImplicitSelf => true,
2049-
TyKind::Rptr(_, ref mt) => mt.ty.node.is_implicit_self(),
2050-
_ => false,
2051-
}),
2047+
implicit_self: decl.inputs.get(0).map_or(
2048+
hir::ImplicitSelfKind::None,
2049+
|arg| {
2050+
let is_mutable_pat = match arg.pat.node {
2051+
PatKind::Ident(BindingMode::ByValue(mt), _, _) |
2052+
PatKind::Ident(BindingMode::ByRef(mt), _, _) =>
2053+
mt == Mutability::Mutable,
2054+
_ => false,
2055+
};
2056+
2057+
match arg.ty.node {
2058+
TyKind::ImplicitSelf if is_mutable_pat => hir::ImplicitSelfKind::Mut,
2059+
TyKind::ImplicitSelf => hir::ImplicitSelfKind::Imm,
2060+
// Given we are only considering `ImplicitSelf` types, we needn't consider
2061+
// the case where we have a mutable pattern to a reference as that would
2062+
// no longer be an `ImplicitSelf`.
2063+
TyKind::Rptr(_, ref mt) if mt.ty.node.is_implicit_self() &&
2064+
mt.mutbl == ast::Mutability::Mutable =>
2065+
hir::ImplicitSelfKind::MutRef,
2066+
TyKind::Rptr(_, ref mt) if mt.ty.node.is_implicit_self() =>
2067+
hir::ImplicitSelfKind::ImmRef,
2068+
_ => hir::ImplicitSelfKind::None,
2069+
}
2070+
},
2071+
),
20522072
})
20532073
}
20542074

src/librustc/hir/mod.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -1782,9 +1782,34 @@ pub struct FnDecl {
17821782
pub inputs: HirVec<Ty>,
17831783
pub output: FunctionRetTy,
17841784
pub variadic: bool,
1785-
/// True if this function has an `self`, `&self` or `&mut self` receiver
1786-
/// (but not a `self: Xxx` one).
1787-
pub has_implicit_self: bool,
1785+
/// Does the function have an implicit self?
1786+
pub implicit_self: ImplicitSelfKind,
1787+
}
1788+
1789+
/// Represents what type of implicit self a function has, if any.
1790+
#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
1791+
pub enum ImplicitSelfKind {
1792+
/// Represents a `fn x(self);`.
1793+
Imm,
1794+
/// Represents a `fn x(mut self);`.
1795+
Mut,
1796+
/// Represents a `fn x(&self);`.
1797+
ImmRef,
1798+
/// Represents a `fn x(&mut self);`.
1799+
MutRef,
1800+
/// Represents when a function does not have a self argument or
1801+
/// when a function has a `self: X` argument.
1802+
None
1803+
}
1804+
1805+
impl ImplicitSelfKind {
1806+
/// Does this represent an implicit self?
1807+
pub fn has_implicit_self(&self) -> bool {
1808+
match *self {
1809+
ImplicitSelfKind::None => false,
1810+
_ => true,
1811+
}
1812+
}
17881813
}
17891814

17901815
/// Is the trait definition an auto trait?

src/librustc/ich/impls_hir.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -355,14 +355,22 @@ impl_stable_hash_for!(struct hir::FnDecl {
355355
inputs,
356356
output,
357357
variadic,
358-
has_implicit_self
358+
implicit_self
359359
});
360360

361361
impl_stable_hash_for!(enum hir::FunctionRetTy {
362362
DefaultReturn(span),
363363
Return(t)
364364
});
365365

366+
impl_stable_hash_for!(enum hir::ImplicitSelfKind {
367+
Imm,
368+
Mut,
369+
ImmRef,
370+
MutRef,
371+
None
372+
});
373+
366374
impl_stable_hash_for!(struct hir::TraitRef {
367375
// Don't hash the ref_id. It is tracked via the thing it is used to access
368376
ref_id -> _,

src/librustc/mir/mod.rs

+30-7
Original file line numberDiff line numberDiff line change
@@ -583,11 +583,27 @@ pub enum BindingForm<'tcx> {
583583
/// This is a binding for a non-`self` binding, or a `self` that has an explicit type.
584584
Var(VarBindingForm<'tcx>),
585585
/// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
586-
ImplicitSelf,
586+
ImplicitSelf(ImplicitSelfKind),
587587
/// Reference used in a guard expression to ensure immutability.
588588
RefForGuard,
589589
}
590590

591+
/// Represents what type of implicit self a function has, if any.
592+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
593+
pub enum ImplicitSelfKind {
594+
/// Represents a `fn x(self);`.
595+
Imm,
596+
/// Represents a `fn x(mut self);`.
597+
Mut,
598+
/// Represents a `fn x(&self);`.
599+
ImmRef,
600+
/// Represents a `fn x(&mut self);`.
601+
MutRef,
602+
/// Represents when a function does not have a self argument or
603+
/// when a function has a `self: X` argument.
604+
None
605+
}
606+
591607
CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
592608

593609
impl_stable_hash_for!(struct self::VarBindingForm<'tcx> {
@@ -597,6 +613,14 @@ impl_stable_hash_for!(struct self::VarBindingForm<'tcx> {
597613
pat_span
598614
});
599615

616+
impl_stable_hash_for!(enum self::ImplicitSelfKind {
617+
Imm,
618+
Mut,
619+
ImmRef,
620+
MutRef,
621+
None
622+
});
623+
600624
mod binding_form_impl {
601625
use ich::StableHashingContext;
602626
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
@@ -612,7 +636,7 @@ mod binding_form_impl {
612636

613637
match self {
614638
Var(binding) => binding.hash_stable(hcx, hasher),
615-
ImplicitSelf => (),
639+
ImplicitSelf(kind) => kind.hash_stable(hcx, hasher),
616640
RefForGuard => (),
617641
}
618642
}
@@ -775,10 +799,9 @@ impl<'tcx> LocalDecl<'tcx> {
775799
pat_span: _,
776800
}))) => true,
777801

778-
// FIXME: might be able to thread the distinction between
779-
// `self`/`mut self`/`&self`/`&mut self` into the
780-
// `BindingForm::ImplicitSelf` variant, (and then return
781-
// true here for solely the first case).
802+
Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf(ImplicitSelfKind::Imm)))
803+
=> true,
804+
782805
_ => false,
783806
}
784807
}
@@ -795,7 +818,7 @@ impl<'tcx> LocalDecl<'tcx> {
795818
pat_span: _,
796819
}))) => true,
797820

798-
Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf)) => true,
821+
Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_))) => true,
799822

800823
_ => false,
801824
}

src/librustc_borrowck/borrowck/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
12211221
if let Some(i) = arg_pos {
12221222
// The argument's `Ty`
12231223
(Some(&fn_like.decl().inputs[i]),
1224-
i == 0 && fn_like.decl().has_implicit_self)
1224+
i == 0 && fn_like.decl().implicit_self.has_implicit_self())
12251225
} else {
12261226
(None, false)
12271227
}

src/librustc_mir/borrow_check/mutability_errors.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc::mir::TerminatorKind;
1616
use rustc::ty::{self, Const, DefIdTree, TyS, TyKind, TyCtxt};
1717
use rustc_data_structures::indexed_vec::Idx;
1818
use syntax_pos::Span;
19+
use syntax_pos::symbol::keywords;
1920

2021
use dataflow::move_paths::InitLocation;
2122
use borrow_check::MirBorrowckCtxt;
@@ -217,6 +218,40 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
217218
debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
218219

219220
match the_place_err {
221+
// Suggest removing a `&mut` from the use of a mutable reference.
222+
Place::Local(local)
223+
if {
224+
self.mir.local_decls.get(*local).map(|local_decl| {
225+
if let ClearCrossCrate::Set(
226+
mir::BindingForm::ImplicitSelf(kind)
227+
) = local_decl.is_user_variable.as_ref().unwrap() {
228+
// Check if the user variable is a `&mut self` and we can therefore
229+
// suggest removing the `&mut`.
230+
//
231+
// Deliberately fall into this case for all implicit self types,
232+
// so that we don't fall in to the next case with them.
233+
*kind == mir::ImplicitSelfKind::MutRef
234+
} else if Some(keywords::SelfValue.name()) == local_decl.name {
235+
// Otherwise, check if the name is the self kewyord - in which case
236+
// we have an explicit self. Do the same thing in this case and check
237+
// for a `self: &mut Self` to suggest removing the `&mut`.
238+
if let ty::TyKind::Ref(
239+
_, _, hir::Mutability::MutMutable
240+
) = local_decl.ty.sty {
241+
true
242+
} else {
243+
false
244+
}
245+
} else {
246+
false
247+
}
248+
}).unwrap_or(false)
249+
} =>
250+
{
251+
err.span_label(span, format!("cannot {ACT}", ACT = act));
252+
err.span_label(span, "try removing `&mut` here");
253+
},
254+
220255
// We want to suggest users use `let mut` for local (user
221256
// variable) mutations...
222257
Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
@@ -316,7 +351,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
316351
{
317352
let local_decl = &self.mir.local_decls[*local];
318353
let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
319-
ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => {
354+
ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_)) => {
320355
Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
321356
}
322357

src/librustc_mir/build/mod.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,14 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
125125
let ty_hir_id = fn_decl.inputs[index].hir_id;
126126
let ty_span = tcx.hir.span(tcx.hir.hir_to_node_id(ty_hir_id));
127127
opt_ty_info = Some(ty_span);
128-
self_arg = if index == 0 && fn_decl.has_implicit_self {
129-
Some(ImplicitSelfBinding)
128+
self_arg = if index == 0 && fn_decl.implicit_self.has_implicit_self() {
129+
match fn_decl.implicit_self {
130+
hir::ImplicitSelfKind::Imm => Some(ImplicitSelfKind::Imm),
131+
hir::ImplicitSelfKind::Mut => Some(ImplicitSelfKind::Mut),
132+
hir::ImplicitSelfKind::ImmRef => Some(ImplicitSelfKind::ImmRef),
133+
hir::ImplicitSelfKind::MutRef => Some(ImplicitSelfKind::MutRef),
134+
_ => None,
135+
}
130136
} else {
131137
None
132138
};
@@ -508,12 +514,10 @@ fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
508514
///////////////////////////////////////////////////////////////////////////
509515
/// the main entry point for building MIR for a function
510516
511-
struct ImplicitSelfBinding;
512-
513517
struct ArgInfo<'gcx>(Ty<'gcx>,
514518
Option<Span>,
515519
Option<&'gcx hir::Pat>,
516-
Option<ImplicitSelfBinding>);
520+
Option<ImplicitSelfKind>);
517521

518522
fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
519523
fn_id: ast::NodeId,
@@ -797,8 +801,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
797801
PatternKind::Binding { mutability, var, mode: BindingMode::ByValue, .. } => {
798802
self.local_decls[local].mutability = mutability;
799803
self.local_decls[local].is_user_variable =
800-
if let Some(ImplicitSelfBinding) = self_binding {
801-
Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf))
804+
if let Some(kind) = self_binding {
805+
Some(ClearCrossCrate::Set(BindingForm::ImplicitSelf(*kind)))
802806
} else {
803807
let binding_mode = ty::BindingMode::BindByValue(mutability.into());
804808
Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {

src/test/incremental/hashes/trait_defs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ trait TraitChangeModeSelfOwnToMut: Sized {
270270
#[rustc_clean(label="Hir", cfg="cfail2")]
271271
#[rustc_clean(label="Hir", cfg="cfail3")]
272272
trait TraitChangeModeSelfOwnToMut: Sized {
273-
#[rustc_clean(label="Hir", cfg="cfail2")]
273+
#[rustc_dirty(label="Hir", cfg="cfail2")]
274274
#[rustc_clean(label="Hir", cfg="cfail3")]
275275
#[rustc_dirty(label="HirBody", cfg="cfail2")]
276276
#[rustc_clean(label="HirBody", cfg="cfail3")]

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

+8-4
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
22
--> $DIR/issue-31424.rs:17:9
33
|
44
LL | (&mut self).bar(); //~ ERROR cannot borrow
5-
| ^^^^^^^^^^^ cannot borrow as mutable
5+
| ^^^^^^^^^^^
6+
| |
7+
| cannot borrow as mutable
8+
| try removing `&mut` here
69

710
error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
811
--> $DIR/issue-31424.rs:23:9
912
|
10-
LL | fn bar(self: &mut Self) {
11-
| ---- help: consider changing this to be mutable: `mut self`
1213
LL | (&mut self).bar(); //~ ERROR cannot borrow
13-
| ^^^^^^^^^^^ cannot borrow as mutable
14+
| ^^^^^^^^^^^
15+
| |
16+
| cannot borrow as mutable
17+
| try removing `&mut` here
1418

1519
error: aborting due to 2 previous errors
1620

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

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2016 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+
struct Struct;
14+
15+
impl Struct {
16+
fn bar(self: &mut Self) {
17+
(&mut self).bar();
18+
//~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596]
19+
}
20+
21+
fn imm(self) {
22+
(&mut self).bar();
23+
//~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596]
24+
}
25+
26+
fn mtbl(mut self) {
27+
(&mut self).bar();
28+
}
29+
30+
fn immref(&self) {
31+
(&mut self).bar();
32+
//~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596]
33+
//~^^ ERROR cannot borrow data in a `&` reference as mutable [E0596]
34+
}
35+
36+
fn mtblref(&mut self) {
37+
(&mut self).bar();
38+
//~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596]
39+
}
40+
}
41+
42+
fn main () {}

0 commit comments

Comments
 (0)