Skip to content

Commit 68c2f5b

Browse files
committed
Auto merge of #115308 - chenyukang:yukang-fix-62387-iter-mut, r=davidtwco
suggest iter_mut() where trying to modify elements from .iter() Fixes #115259 Fixes #62387
2 parents 55e5c9d + 3988ff2 commit 68c2f5b

10 files changed

+274
-1
lines changed

Diff for: compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+41-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use hir::ExprKind;
12
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
23
use rustc_hir as hir;
34
use rustc_hir::intravisit::Visitor;
45
use rustc_hir::Node;
56
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
6-
use rustc_middle::ty::{self, Ty, TyCtxt};
7+
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
78
use rustc_middle::{
89
hir::place::PlaceBase,
910
mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
@@ -491,6 +492,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
491492
),
492493
);
493494

495+
self.suggest_using_iter_mut(&mut err);
494496
self.suggest_make_local_mut(&mut err, local, name);
495497
}
496498
_ => {
@@ -953,6 +955,44 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
953955
}
954956
}
955957

958+
fn suggest_using_iter_mut(&self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>) {
959+
let source = self.body.source;
960+
let hir = self.infcx.tcx.hir();
961+
if let InstanceDef::Item(def_id) = source.instance
962+
&& let Some(Node::Expr(hir::Expr { hir_id, kind, ..})) = hir.get_if_local(def_id)
963+
&& let ExprKind::Closure(closure) = kind && closure.movability == None
964+
&& let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) {
965+
let mut cur_expr = expr;
966+
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
967+
if path_segment.ident.name == sym::iter {
968+
// check `_ty` has `iter_mut` method
969+
let res = self
970+
.infcx
971+
.tcx
972+
.typeck(path_segment.hir_id.owner.def_id)
973+
.type_dependent_def_id(cur_expr.hir_id)
974+
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
975+
.map(|def_id| self.infcx.tcx.associated_items(def_id))
976+
.map(|assoc_items| {
977+
assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
978+
});
979+
980+
if let Some(mut res) = res && res.peek().is_some() {
981+
err.span_suggestion_verbose(
982+
path_segment.ident.span,
983+
"you may want to use `iter_mut` here",
984+
"iter_mut",
985+
Applicability::MaybeIncorrect,
986+
);
987+
}
988+
break;
989+
} else {
990+
cur_expr = recv;
991+
}
992+
}
993+
}
994+
}
995+
956996
fn suggest_make_local_mut(
957997
&self,
958998
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-rustfix
2+
#![allow(unused_mut)]
3+
#![allow(dead_code)]
4+
5+
pub trait Layer {
6+
fn process(&mut self) -> u32;
7+
}
8+
9+
pub struct State {
10+
layers: Vec<Box<dyn Layer>>,
11+
}
12+
13+
impl State {
14+
pub fn process(&mut self) -> u32 {
15+
self.layers.iter_mut().fold(0, |result, mut layer| result + layer.process())
16+
//~^ ERROR cannot borrow `**layer` as mutable, as it is behind a `&` reference
17+
}
18+
}
19+
20+
fn main() {}

Diff for: tests/ui/borrowck/issue-115259-suggest-iter-mut.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-rustfix
2+
#![allow(unused_mut)]
3+
#![allow(dead_code)]
4+
5+
pub trait Layer {
6+
fn process(&mut self) -> u32;
7+
}
8+
9+
pub struct State {
10+
layers: Vec<Box<dyn Layer>>,
11+
}
12+
13+
impl State {
14+
pub fn process(&mut self) -> u32 {
15+
self.layers.iter().fold(0, |result, mut layer| result + layer.process())
16+
//~^ ERROR cannot borrow `**layer` as mutable, as it is behind a `&` reference
17+
}
18+
}
19+
20+
fn main() {}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0596]: cannot borrow `**layer` as mutable, as it is behind a `&` reference
2+
--> $DIR/issue-115259-suggest-iter-mut.rs:15:65
3+
|
4+
LL | self.layers.iter().fold(0, |result, mut layer| result + layer.process())
5+
| --------- ^^^^^ `layer` is a `&` reference, so the data it refers to cannot be borrowed as mutable
6+
| |
7+
| consider changing this binding's type to be: `&mut Box<dyn Layer>`
8+
|
9+
help: you may want to use `iter_mut` here
10+
|
11+
LL | self.layers.iter_mut().fold(0, |result, mut layer| result + layer.process())
12+
| ~~~~~~~~
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0596`.
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// run-rustfix
2+
#![allow(unused_mut)]
3+
#![allow(dead_code)]
4+
use std::path::PathBuf;
5+
6+
#[derive(Clone)]
7+
struct Container {
8+
things: Vec<PathBuf>,
9+
}
10+
11+
impl Container {
12+
fn things(&mut self) -> &[PathBuf] {
13+
&self.things
14+
}
15+
}
16+
17+
// contains containers
18+
struct ContainerContainer {
19+
contained: Vec<Container>,
20+
}
21+
22+
impl ContainerContainer {
23+
fn contained(&self) -> &[Container] {
24+
&self.contained
25+
}
26+
27+
fn all_the_things(&mut self) -> &[PathBuf] {
28+
let mut vec = self.contained.clone();
29+
let _a =
30+
vec.iter_mut().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
31+
//~^ ERROR cannot borrow `*container` as mutable, as it is behind a `&` reference
32+
unimplemented!();
33+
}
34+
}
35+
36+
fn main() {}

Diff for: tests/ui/borrowck/issue-62387-suggest-iter-mut-2.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// run-rustfix
2+
#![allow(unused_mut)]
3+
#![allow(dead_code)]
4+
use std::path::PathBuf;
5+
6+
#[derive(Clone)]
7+
struct Container {
8+
things: Vec<PathBuf>,
9+
}
10+
11+
impl Container {
12+
fn things(&mut self) -> &[PathBuf] {
13+
&self.things
14+
}
15+
}
16+
17+
// contains containers
18+
struct ContainerContainer {
19+
contained: Vec<Container>,
20+
}
21+
22+
impl ContainerContainer {
23+
fn contained(&self) -> &[Container] {
24+
&self.contained
25+
}
26+
27+
fn all_the_things(&mut self) -> &[PathBuf] {
28+
let mut vec = self.contained.clone();
29+
let _a =
30+
vec.iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
31+
//~^ ERROR cannot borrow `*container` as mutable, as it is behind a `&` reference
32+
unimplemented!();
33+
}
34+
}
35+
36+
fn main() {}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0596]: cannot borrow `*container` as mutable, as it is behind a `&` reference
2+
--> $DIR/issue-62387-suggest-iter-mut-2.rs:30:45
3+
|
4+
LL | vec.iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
5+
| --------- ^^^^^^^^^ `container` is a `&` reference, so the data it refers to cannot be borrowed as mutable
6+
| |
7+
| consider changing this binding's type to be: `&mut Container`
8+
|
9+
help: you may want to use `iter_mut` here
10+
|
11+
LL | vec.iter_mut().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
12+
| ~~~~~~~~
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0596`.

Diff for: tests/ui/borrowck/issue-62387-suggest-iter-mut.fixed

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-rustfix
2+
#![allow(unused_mut)]
3+
#![allow(dead_code)]
4+
5+
#[derive(Debug)]
6+
struct A {
7+
a: i32,
8+
}
9+
10+
impl A {
11+
fn double(&mut self) {
12+
self.a += self.a
13+
}
14+
}
15+
16+
fn baz() {
17+
let mut v = [A { a: 4 }];
18+
v.iter_mut().for_each(|a| a.double());
19+
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
20+
println!("{:?}", v);
21+
}
22+
23+
fn bar() {
24+
let mut v = [A { a: 4 }];
25+
v.iter_mut().rev().rev().for_each(|a| a.double());
26+
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
27+
println!("{:?}", v);
28+
}
29+
30+
fn main() {}

Diff for: tests/ui/borrowck/issue-62387-suggest-iter-mut.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-rustfix
2+
#![allow(unused_mut)]
3+
#![allow(dead_code)]
4+
5+
#[derive(Debug)]
6+
struct A {
7+
a: i32,
8+
}
9+
10+
impl A {
11+
fn double(&mut self) {
12+
self.a += self.a
13+
}
14+
}
15+
16+
fn baz() {
17+
let mut v = [A { a: 4 }];
18+
v.iter().for_each(|a| a.double());
19+
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
20+
println!("{:?}", v);
21+
}
22+
23+
fn bar() {
24+
let mut v = [A { a: 4 }];
25+
v.iter().rev().rev().for_each(|a| a.double());
26+
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
27+
println!("{:?}", v);
28+
}
29+
30+
fn main() {}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
2+
--> $DIR/issue-62387-suggest-iter-mut.rs:18:27
3+
|
4+
LL | v.iter().for_each(|a| a.double());
5+
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
6+
| |
7+
| consider changing this binding's type to be: `&mut A`
8+
|
9+
help: you may want to use `iter_mut` here
10+
|
11+
LL | v.iter_mut().for_each(|a| a.double());
12+
| ~~~~~~~~
13+
14+
error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
15+
--> $DIR/issue-62387-suggest-iter-mut.rs:25:39
16+
|
17+
LL | v.iter().rev().rev().for_each(|a| a.double());
18+
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
19+
| |
20+
| consider changing this binding's type to be: `&mut A`
21+
|
22+
help: you may want to use `iter_mut` here
23+
|
24+
LL | v.iter_mut().rev().rev().for_each(|a| a.double());
25+
| ~~~~~~~~
26+
27+
error: aborting due to 2 previous errors
28+
29+
For more information about this error, try `rustc --explain E0596`.

0 commit comments

Comments
 (0)