Skip to content

Commit 5a28d8f

Browse files
committed
Auto merge of rust-lang#12650 - cocodery:issue/12098, r=xFrednet
fix false positive in Issue/12098 because lack of consideration of mutable caller fixes [Issue#12098](rust-lang/rust-clippy#12098) In issue#12098, the former code doesn't consider the caller for clone is mutable, and suggests to delete clone function. In this change, we first get the inner caller requests for clone, and if it's immutable, the following code will suggest deleting clone. If it's mutable, the loop will check whether a borrow check violation exists, if exists, the lint should not execute, and the function will directly return; otherwise, the following code will handle this. changelog: [`clippy::unnecessary_to_owned`]: fix false positive
2 parents 9abaf91 + a8c35cb commit 5a28d8f

File tree

4 files changed

+117
-4
lines changed

4 files changed

+117
-4
lines changed

clippy_lints/src/methods/unnecessary_iter_cloned.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ 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::visitors::for_each_expr;
7+
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
8+
use core::ops::ControlFlow;
79
use rustc_errors::Applicability;
810
use rustc_hir::def_id::DefId;
9-
use rustc_hir::{Expr, ExprKind};
11+
use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind};
1012
use rustc_lint::LateContext;
1113
use rustc_span::{sym, Symbol};
1214

@@ -40,6 +42,53 @@ pub fn check_for_loop_iter(
4042
&& !clone_or_copy_needed
4143
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
4244
{
45+
// Issue 12098
46+
// https://github.com/rust-lang/rust-clippy/issues/12098
47+
// if the assignee have `mut borrow` conflict with the iteratee
48+
// the lint should not execute, former didn't consider the mut case
49+
50+
// check whether `expr` is mutable
51+
fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
52+
if let Some(hir_id) = path_to_local(expr)
53+
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
54+
{
55+
matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
56+
} else {
57+
true
58+
}
59+
}
60+
61+
fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
62+
let mut change = false;
63+
if let ExprKind::Block(block, ..) = body.kind {
64+
for_each_expr(block, |e| {
65+
match e.kind {
66+
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
67+
change |= !can_mut_borrow_both(cx, caller, assignee);
68+
},
69+
_ => {},
70+
}
71+
// the return value has no effect but the function need one return value
72+
ControlFlow::<()>::Continue(())
73+
});
74+
}
75+
change
76+
}
77+
78+
if let ExprKind::Call(_, [child, ..]) = expr.kind {
79+
// filter first layer of iterator
80+
let mut child = child;
81+
// get inner real caller requests for clone
82+
while let ExprKind::MethodCall(_, caller, _, _) = child.kind {
83+
child = caller;
84+
}
85+
if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) {
86+
// skip lint
87+
return true;
88+
}
89+
};
90+
91+
// the lint should not be executed if no violation happens
4392
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
4493
&& maybe_iter_method_name.ident.name == sym::iter
4594
&& 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)