Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`.
Loading