Skip to content

Commit 194b659

Browse files
committed
Check inner caller for clone and judge whether they are mutable
if immutbale -> lint delete redudant clone if mutable -> lint check whether clone is needed
1 parent 805ef35 commit 194b659

File tree

4 files changed

+125
-4
lines changed

4 files changed

+125
-4
lines changed

clippy_lints/src/methods/unnecessary_iter_cloned.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::higher::ForLoop;
44
use clippy_utils::source::snippet_opt;
55
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
6-
use clippy_utils::{fn_def_id, get_parent_expr};
6+
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
77
use rustc_errors::Applicability;
88
use rustc_hir::def_id::DefId;
9-
use rustc_hir::{Expr, ExprKind};
9+
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Node, PatKind, StmtKind};
1010
use rustc_lint::LateContext;
1111
use rustc_span::{sym, Symbol};
1212

@@ -40,6 +40,63 @@ pub fn check_for_loop_iter(
4040
&& !clone_or_copy_needed
4141
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
4242
{
43+
// Issue 12098
44+
// https://github.com/rust-lang/rust-clippy/issues/12098
45+
// if the assignee have `mut borrow` conflict with the iteratee
46+
// the lint should not execute, former didn't consider the mut case
47+
48+
// check whether `expr` is mutable
49+
fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
50+
if let Some(hir_id) = path_to_local(expr)
51+
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
52+
{
53+
matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..))
54+
} else {
55+
true
56+
}
57+
}
58+
59+
fn is_caller_or_fields_changed(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
60+
// local private `walk_blocks` function
61+
// to iterate all the expr in the block and subblocks
62+
fn walk_blocks(cx: &LateContext<'_>, block: &Block<'_>, caller: &Expr<'_>) -> bool {
63+
for stmt in block.stmts {
64+
let changed = match stmt.kind {
65+
StmtKind::Expr(e) | StmtKind::Semi(e) => match e.kind {
66+
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
67+
!can_mut_borrow_both(cx, caller, assignee)
68+
},
69+
ExprKind::Block(block, ..) => walk_blocks(cx, block, caller),
70+
_ => false,
71+
},
72+
_ => false,
73+
};
74+
if changed {
75+
return true;
76+
}
77+
}
78+
false
79+
}
80+
if let ExprKind::Block(block, ..) = body.kind {
81+
return walk_blocks(cx, block, caller);
82+
}
83+
false
84+
}
85+
86+
if let ExprKind::Call(_, [child, ..]) = expr.kind {
87+
// filter first layer of iterator
88+
let mut child = child;
89+
// get inner real caller requests for clone
90+
while let ExprKind::MethodCall(_, caller, _, _) = child.kind {
91+
child = caller;
92+
}
93+
if is_mutable(cx, child) && is_caller_or_fields_changed(cx, body, child) {
94+
// skip lint
95+
return true;
96+
}
97+
};
98+
99+
// the lint should not be executed if no violation happens
43100
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
44101
&& maybe_iter_method_name.ident.name == sym::iter
45102
&& let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator)

tests/ui/unnecessary_iter_cloned.fixed

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ fn main() {
2121
let _ = check_files_ref_mut(&[(FileType::Account, path)]);
2222
let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
2323
let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
24+
25+
check_mut_iteratee_and_modify_inner_variable();
2426
}
2527

2628
// `check_files` and its variants are based on:
@@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
138140
fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
139141
Ok(std::path::PathBuf::new())
140142
}
143+
144+
// Issue 12098
145+
// https://github.com/rust-lang/rust-clippy/issues/12098
146+
// no message emits
147+
fn check_mut_iteratee_and_modify_inner_variable() {
148+
struct Test {
149+
list: Vec<String>,
150+
mut_this: bool,
151+
}
152+
153+
impl Test {
154+
fn list(&self) -> &[String] {
155+
&self.list
156+
}
157+
}
158+
159+
let mut test = Test {
160+
list: vec![String::from("foo"), String::from("bar")],
161+
mut_this: false,
162+
};
163+
164+
for _item in test.list().to_vec() {
165+
println!("{}", _item);
166+
167+
test.mut_this = true;
168+
{
169+
test.mut_this = true;
170+
}
171+
}
172+
}

tests/ui/unnecessary_iter_cloned.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ fn main() {
2121
let _ = check_files_ref_mut(&[(FileType::Account, path)]);
2222
let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
2323
let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
24+
25+
check_mut_iteratee_and_modify_inner_variable();
2426
}
2527

2628
// `check_files` and its variants are based on:
@@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
138140
fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
139141
Ok(std::path::PathBuf::new())
140142
}
143+
144+
// Issue 12098
145+
// https://github.com/rust-lang/rust-clippy/issues/12098
146+
// no message emits
147+
fn check_mut_iteratee_and_modify_inner_variable() {
148+
struct Test {
149+
list: Vec<String>,
150+
mut_this: bool,
151+
}
152+
153+
impl Test {
154+
fn list(&self) -> &[String] {
155+
&self.list
156+
}
157+
}
158+
159+
let mut test = Test {
160+
list: vec![String::from("foo"), String::from("bar")],
161+
mut_this: false,
162+
};
163+
164+
for _item in test.list().to_vec() {
165+
println!("{}", _item);
166+
167+
test.mut_this = true;
168+
{
169+
test.mut_this = true;
170+
}
171+
}
172+
}

tests/ui/unnecessary_iter_cloned.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: unnecessary use of `copied`
2-
--> tests/ui/unnecessary_iter_cloned.rs:29:22
2+
--> tests/ui/unnecessary_iter_cloned.rs:31:22
33
|
44
LL | for (t, path) in files.iter().copied() {
55
| ^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL + let other = match get_file_path(t) {
1717
|
1818

1919
error: unnecessary use of `copied`
20-
--> tests/ui/unnecessary_iter_cloned.rs:44:22
20+
--> tests/ui/unnecessary_iter_cloned.rs:46:22
2121
|
2222
LL | for (t, path) in files.iter().copied() {
2323
| ^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)