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

This commit is contained in:
yukang 2023-08-28 16:57:29 +08:00
parent a0c28cd9dc
commit 3988ff25bc
10 changed files with 274 additions and 1 deletions

View file

@ -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},
@ -491,6 +492,7 @@ pub(crate) fn report_mutability_error(
),
);
self.suggest_using_iter_mut(&mut err);
self.suggest_make_local_mut(&mut err, local, name);
}
_ => {
@ -953,6 +955,44 @@ fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &st
}
}
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>,

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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() {}

View file

@ -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`.