Skip to content

Commit 7e4c1ae

Browse files
committed
Auto merge of #12843 - mdm:fix-unnecessary-to-owned-println-interaction, r=y21
Fix `unnecessary_to_owned` interaction with macro expansion fixes #12821 In the case of an unnecessary `.iter().cloned()`, the lint `unnecessary_to_owned` might suggest to remove the `&` from references without checking if such references are inside a macro expansion. This can lead to unexpected behavior or even broken code if the lint suggestion is applied blindly. See issue #12821 for an example. This PR checks if such references are inside macro expansions and skips this part of the lint suggestion in these cases. changelog: [`unnecessary_to_owned`]: Don't suggest to remove `&` inside macro expansion
2 parents 20f0f13 + 4a64180 commit 7e4c1ae

6 files changed

+117
-19
lines changed

clippy_lints/src/methods/unnecessary_iter_cloned.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn check_for_loop_iter(
3838
) -> bool {
3939
if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent))
4040
&& let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent)
41-
&& let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body)
41+
&& let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body)
4242
&& !clone_or_copy_needed
4343
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
4444
{
@@ -123,14 +123,12 @@ pub fn check_for_loop_iter(
123123
Applicability::MachineApplicable
124124
};
125125
diag.span_suggestion(expr.span, "use", snippet, applicability);
126-
for addr_of_expr in addr_of_exprs {
127-
match addr_of_expr.kind {
128-
ExprKind::AddrOf(_, _, referent) => {
129-
let span = addr_of_expr.span.with_hi(referent.span.lo());
130-
diag.span_suggestion(span, "remove this `&`", "", applicability);
131-
},
132-
_ => unreachable!(),
133-
}
126+
if !references_to_binding.is_empty() {
127+
diag.multipart_suggestion(
128+
"remove any references to the binding",
129+
references_to_binding,
130+
applicability,
131+
);
134132
}
135133
},
136134
);

clippy_lints/src/methods/utils.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_lint::LateContext;
99
use rustc_middle::hir::nested_filter;
1010
use rustc_middle::ty::{self, Ty};
1111
use rustc_span::symbol::sym;
12+
use rustc_span::Span;
1213

1314
pub(super) fn derefs_to_slice<'tcx>(
1415
cx: &LateContext<'tcx>,
@@ -96,15 +97,15 @@ pub(super) fn clone_or_copy_needed<'tcx>(
9697
cx: &LateContext<'tcx>,
9798
pat: &Pat<'tcx>,
9899
body: &'tcx Expr<'tcx>,
99-
) -> (bool, Vec<&'tcx Expr<'tcx>>) {
100+
) -> (bool, Vec<(Span, String)>) {
100101
let mut visitor = CloneOrCopyVisitor {
101102
cx,
102103
binding_hir_ids: pat_bindings(pat),
103104
clone_or_copy_needed: false,
104-
addr_of_exprs: Vec::new(),
105+
references_to_binding: Vec::new(),
105106
};
106107
visitor.visit_expr(body);
107-
(visitor.clone_or_copy_needed, visitor.addr_of_exprs)
108+
(visitor.clone_or_copy_needed, visitor.references_to_binding)
108109
}
109110

110111
/// Returns a vector of all `HirId`s bound by the pattern.
@@ -127,7 +128,7 @@ struct CloneOrCopyVisitor<'cx, 'tcx> {
127128
cx: &'cx LateContext<'tcx>,
128129
binding_hir_ids: Vec<HirId>,
129130
clone_or_copy_needed: bool,
130-
addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
131+
references_to_binding: Vec<(Span, String)>,
131132
}
132133

133134
impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
@@ -142,8 +143,11 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
142143
if self.is_binding(expr) {
143144
if let Some(parent) = get_parent_expr(self.cx, expr) {
144145
match parent.kind {
145-
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
146-
self.addr_of_exprs.push(parent);
146+
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, referent) => {
147+
if !parent.span.from_expansion() {
148+
self.references_to_binding
149+
.push((parent.span.until(referent.span), String::new()));
150+
}
147151
return;
148152
},
149153
ExprKind::MethodCall(.., args, _) => {

tests/ui/unnecessary_iter_cloned.fixed

+29
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,32 @@ fn check_mut_iteratee_and_modify_inner_variable() {
170170
}
171171
}
172172
}
173+
174+
mod issue_12821 {
175+
fn foo() {
176+
let v: Vec<_> = "hello".chars().collect();
177+
for c in v.iter() {
178+
//~^ ERROR: unnecessary use of `cloned`
179+
println!("{c}"); // should not suggest to remove `&`
180+
}
181+
}
182+
183+
fn bar() {
184+
let v: Vec<_> = "hello".chars().collect();
185+
for c in v.iter() {
186+
//~^ ERROR: unnecessary use of `cloned`
187+
let ref_c = c; //~ HELP: remove any references to the binding
188+
println!("{ref_c}");
189+
}
190+
}
191+
192+
fn baz() {
193+
let v: Vec<_> = "hello".chars().enumerate().collect();
194+
for (i, c) in v.iter() {
195+
//~^ ERROR: unnecessary use of `cloned`
196+
let ref_c = c; //~ HELP: remove any references to the binding
197+
let ref_i = i;
198+
println!("{i} {ref_c}"); // should not suggest to remove `&` from `i`
199+
}
200+
}
201+
}

tests/ui/unnecessary_iter_cloned.rs

+29
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,32 @@ fn check_mut_iteratee_and_modify_inner_variable() {
170170
}
171171
}
172172
}
173+
174+
mod issue_12821 {
175+
fn foo() {
176+
let v: Vec<_> = "hello".chars().collect();
177+
for c in v.iter().cloned() {
178+
//~^ ERROR: unnecessary use of `cloned`
179+
println!("{c}"); // should not suggest to remove `&`
180+
}
181+
}
182+
183+
fn bar() {
184+
let v: Vec<_> = "hello".chars().collect();
185+
for c in v.iter().cloned() {
186+
//~^ ERROR: unnecessary use of `cloned`
187+
let ref_c = &c; //~ HELP: remove any references to the binding
188+
println!("{ref_c}");
189+
}
190+
}
191+
192+
fn baz() {
193+
let v: Vec<_> = "hello".chars().enumerate().collect();
194+
for (i, c) in v.iter().cloned() {
195+
//~^ ERROR: unnecessary use of `cloned`
196+
let ref_c = &c; //~ HELP: remove any references to the binding
197+
let ref_i = &i;
198+
println!("{i} {ref_c}"); // should not suggest to remove `&` from `i`
199+
}
200+
}
201+
}

tests/ui/unnecessary_iter_cloned.stderr

+41-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ help: use
1010
|
1111
LL | for (t, path) in files {
1212
| ~~~~~
13-
help: remove this `&`
13+
help: remove any references to the binding
1414
|
1515
LL - let other = match get_file_path(&t) {
1616
LL + let other = match get_file_path(t) {
@@ -26,11 +26,49 @@ help: use
2626
|
2727
LL | for (t, path) in files.iter() {
2828
| ~~~~~~~~~~~~
29-
help: remove this `&`
29+
help: remove any references to the binding
3030
|
3131
LL - let other = match get_file_path(&t) {
3232
LL + let other = match get_file_path(t) {
3333
|
3434

35-
error: aborting due to 2 previous errors
35+
error: unnecessary use of `cloned`
36+
--> tests/ui/unnecessary_iter_cloned.rs:177:18
37+
|
38+
LL | for c in v.iter().cloned() {
39+
| ^^^^^^^^^^^^^^^^^ help: use: `v.iter()`
40+
41+
error: unnecessary use of `cloned`
42+
--> tests/ui/unnecessary_iter_cloned.rs:185:18
43+
|
44+
LL | for c in v.iter().cloned() {
45+
| ^^^^^^^^^^^^^^^^^
46+
|
47+
help: use
48+
|
49+
LL | for c in v.iter() {
50+
| ~~~~~~~~
51+
help: remove any references to the binding
52+
|
53+
LL - let ref_c = &c;
54+
LL + let ref_c = c;
55+
|
56+
57+
error: unnecessary use of `cloned`
58+
--> tests/ui/unnecessary_iter_cloned.rs:194:23
59+
|
60+
LL | for (i, c) in v.iter().cloned() {
61+
| ^^^^^^^^^^^^^^^^^
62+
|
63+
help: use
64+
|
65+
LL | for (i, c) in v.iter() {
66+
| ~~~~~~~~
67+
help: remove any references to the binding
68+
|
69+
LL ~ let ref_c = c;
70+
LL ~ let ref_i = i;
71+
|
72+
73+
error: aborting due to 5 previous errors
3674

tests/ui/unnecessary_to_owned.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ help: use
487487
|
488488
LL | for t in file_types {
489489
| ~~~~~~~~~~
490-
help: remove this `&`
490+
help: remove any references to the binding
491491
|
492492
LL - let path = match get_file_path(&t) {
493493
LL + let path = match get_file_path(t) {

0 commit comments

Comments
 (0)