Skip to content

Commit

Permalink
Auto merge of rust-lang#115308 - chenyukang:yukang-fix-62387-iter-mut…
Browse files Browse the repository at this point in the history
…, r=davidtwco

suggest iter_mut() where trying to modify elements from .iter()

Fixes rust-lang#115259
Fixes rust-lang#62387
  • Loading branch information
bors committed Sep 11, 2023
2 parents 55e5c9d + 3988ff2 commit 68c2f5b
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 1 deletion.
42 changes: 41 additions & 1 deletion compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use hir::ExprKind;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_hir::Node;
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
use rustc_middle::{
hir::place::PlaceBase,
mir::{self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location},
Expand Down Expand Up @@ -491,6 +492,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
),
);

self.suggest_using_iter_mut(&mut err);
self.suggest_make_local_mut(&mut err, local, name);
}
_ => {
Expand Down Expand Up @@ -953,6 +955,44 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
}
}

fn suggest_using_iter_mut(&self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>) {
let source = self.body.source;
let hir = self.infcx.tcx.hir();
if let InstanceDef::Item(def_id) = source.instance
&& let Some(Node::Expr(hir::Expr { hir_id, kind, ..})) = hir.get_if_local(def_id)
&& let ExprKind::Closure(closure) = kind && closure.movability == None
&& let Some(Node::Expr(expr)) = hir.find_parent(*hir_id) {
let mut cur_expr = expr;
while let ExprKind::MethodCall(path_segment, recv, _, _) = cur_expr.kind {
if path_segment.ident.name == sym::iter {
// check `_ty` has `iter_mut` method
let res = self
.infcx
.tcx
.typeck(path_segment.hir_id.owner.def_id)
.type_dependent_def_id(cur_expr.hir_id)
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
.map(|def_id| self.infcx.tcx.associated_items(def_id))
.map(|assoc_items| {
assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable()
});

if let Some(mut res) = res && res.peek().is_some() {
err.span_suggestion_verbose(
path_segment.ident.span,
"you may want to use `iter_mut` here",
"iter_mut",
Applicability::MaybeIncorrect,
);
}
break;
} else {
cur_expr = recv;
}
}
}
}

fn suggest_make_local_mut(
&self,
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
Expand Down
20 changes: 20 additions & 0 deletions tests/ui/borrowck/issue-115259-suggest-iter-mut.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// run-rustfix
#![allow(unused_mut)]
#![allow(dead_code)]

pub trait Layer {
fn process(&mut self) -> u32;
}

pub struct State {
layers: Vec<Box<dyn Layer>>,
}

impl State {
pub fn process(&mut self) -> u32 {
self.layers.iter_mut().fold(0, |result, mut layer| result + layer.process())
//~^ ERROR cannot borrow `**layer` as mutable, as it is behind a `&` reference
}
}

fn main() {}
20 changes: 20 additions & 0 deletions tests/ui/borrowck/issue-115259-suggest-iter-mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// run-rustfix
#![allow(unused_mut)]
#![allow(dead_code)]

pub trait Layer {
fn process(&mut self) -> u32;
}

pub struct State {
layers: Vec<Box<dyn Layer>>,
}

impl State {
pub fn process(&mut self) -> u32 {
self.layers.iter().fold(0, |result, mut layer| result + layer.process())
//~^ ERROR cannot borrow `**layer` as mutable, as it is behind a `&` reference
}
}

fn main() {}
16 changes: 16 additions & 0 deletions tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0596]: cannot borrow `**layer` as mutable, as it is behind a `&` reference
--> $DIR/issue-115259-suggest-iter-mut.rs:15:65
|
LL | self.layers.iter().fold(0, |result, mut layer| result + layer.process())
| --------- ^^^^^ `layer` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut Box<dyn Layer>`
|
help: you may want to use `iter_mut` here
|
LL | self.layers.iter_mut().fold(0, |result, mut layer| result + layer.process())
| ~~~~~~~~

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
36 changes: 36 additions & 0 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut-2.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// run-rustfix
#![allow(unused_mut)]
#![allow(dead_code)]
use std::path::PathBuf;

#[derive(Clone)]
struct Container {
things: Vec<PathBuf>,
}

impl Container {
fn things(&mut self) -> &[PathBuf] {
&self.things
}
}

// contains containers
struct ContainerContainer {
contained: Vec<Container>,
}

impl ContainerContainer {
fn contained(&self) -> &[Container] {
&self.contained
}

fn all_the_things(&mut self) -> &[PathBuf] {
let mut vec = self.contained.clone();
let _a =
vec.iter_mut().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
//~^ ERROR cannot borrow `*container` as mutable, as it is behind a `&` reference
unimplemented!();
}
}

fn main() {}
36 changes: 36 additions & 0 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// run-rustfix
#![allow(unused_mut)]
#![allow(dead_code)]
use std::path::PathBuf;

#[derive(Clone)]
struct Container {
things: Vec<PathBuf>,
}

impl Container {
fn things(&mut self) -> &[PathBuf] {
&self.things
}
}

// contains containers
struct ContainerContainer {
contained: Vec<Container>,
}

impl ContainerContainer {
fn contained(&self) -> &[Container] {
&self.contained
}

fn all_the_things(&mut self) -> &[PathBuf] {
let mut vec = self.contained.clone();
let _a =
vec.iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
//~^ ERROR cannot borrow `*container` as mutable, as it is behind a `&` reference
unimplemented!();
}
}

fn main() {}
16 changes: 16 additions & 0 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0596]: cannot borrow `*container` as mutable, as it is behind a `&` reference
--> $DIR/issue-62387-suggest-iter-mut-2.rs:30:45
|
LL | vec.iter().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
| --------- ^^^^^^^^^ `container` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut Container`
|
help: you may want to use `iter_mut` here
|
LL | vec.iter_mut().flat_map(|container| container.things()).cloned().collect::<Vec<PathBuf>>();
| ~~~~~~~~

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
30 changes: 30 additions & 0 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// run-rustfix
#![allow(unused_mut)]
#![allow(dead_code)]

#[derive(Debug)]
struct A {
a: i32,
}

impl A {
fn double(&mut self) {
self.a += self.a
}
}

fn baz() {
let mut v = [A { a: 4 }];
v.iter_mut().for_each(|a| a.double());
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
println!("{:?}", v);
}

fn bar() {
let mut v = [A { a: 4 }];
v.iter_mut().rev().rev().for_each(|a| a.double());
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
println!("{:?}", v);
}

fn main() {}
30 changes: 30 additions & 0 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// run-rustfix
#![allow(unused_mut)]
#![allow(dead_code)]

#[derive(Debug)]
struct A {
a: i32,
}

impl A {
fn double(&mut self) {
self.a += self.a
}
}

fn baz() {
let mut v = [A { a: 4 }];
v.iter().for_each(|a| a.double());
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
println!("{:?}", v);
}

fn bar() {
let mut v = [A { a: 4 }];
v.iter().rev().rev().for_each(|a| a.double());
//~^ ERROR cannot borrow `*a` as mutable, as it is behind a `&` reference
println!("{:?}", v);
}

fn main() {}
29 changes: 29 additions & 0 deletions tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
--> $DIR/issue-62387-suggest-iter-mut.rs:18:27
|
LL | v.iter().for_each(|a| a.double());
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut A`
|
help: you may want to use `iter_mut` here
|
LL | v.iter_mut().for_each(|a| a.double());
| ~~~~~~~~

error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
--> $DIR/issue-62387-suggest-iter-mut.rs:25:39
|
LL | v.iter().rev().rev().for_each(|a| a.double());
| - ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| consider changing this binding's type to be: `&mut A`
|
help: you may want to use `iter_mut` here
|
LL | v.iter_mut().rev().rev().for_each(|a| a.double());
| ~~~~~~~~

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0596`.

0 comments on commit 68c2f5b

Please sign in to comment.