Skip to content

Commit 786330e

Browse files
authored
Fix ptr_arg suggests changes when it's actually better not to bother (rust-lang#15105)
No longer suggests `&[i32]` or `&mut [i32]` instead of `&Vec<i32>` or `&mut Vec<i32>` (also: `Path` and `PathBuf`, etc.) for the parameter type when the parameter name starts with an underscore (or, if that does not start with one, then a local `let` binding in the function body, pointing to the same value, does) – (Fixes rust-lang/rust-clippy#13489, fixes rust-lang/rust-clippy#13728) ~changelog: fix false positive: [`ptr_arg`] no longer triggers with underscore binding to `&mut` argument~ changelog: fix false positive: [`ptr_arg`] no longer triggers with underscore binding to `&T` or `&mut T` argument *Edit:* This change has been extended to all references, not just mutable ones. See [discussion below](#issuecomment-3006386877).
2 parents 041a0f6 + 75c330b commit 786330e

File tree

3 files changed

+179
-42
lines changed

3 files changed

+179
-42
lines changed

clippy_lints/src/ptr.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,13 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
584584
Some((Node::Stmt(_), _)) => (),
585585
Some((Node::LetStmt(l), _)) => {
586586
// Only trace simple bindings. e.g `let x = y;`
587-
if let PatKind::Binding(BindingMode::NONE, id, _, None) = l.pat.kind {
587+
if let PatKind::Binding(BindingMode::NONE, id, ident, None) = l.pat.kind
588+
// Let's not lint for the current parameter. The user may still intend to mutate
589+
// (or, if not mutate, then perhaps call a method that's not otherwise available
590+
// for) the referenced value behind the parameter through this local let binding
591+
// with the underscore being only temporary.
592+
&& !ident.name.as_str().starts_with('_')
593+
{
588594
self.bindings.insert(id, args_idx);
589595
} else {
590596
set_skip_flag();
@@ -650,7 +656,14 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
650656
.filter_map(|(i, arg)| {
651657
let param = &body.params[arg.idx];
652658
match param.pat.kind {
653-
PatKind::Binding(BindingMode::NONE, id, _, None) if !is_lint_allowed(cx, PTR_ARG, param.hir_id) => {
659+
PatKind::Binding(BindingMode::NONE, id, ident, None)
660+
if !is_lint_allowed(cx, PTR_ARG, param.hir_id)
661+
// Let's not lint for the current parameter. The user may still intend to mutate
662+
// (or, if not mutate, then perhaps call a method that's not otherwise available
663+
// for) the referenced value behind the parameter with the underscore being only
664+
// temporary.
665+
&& !ident.name.as_str().starts_with('_') =>
666+
{
654667
Some((id, i))
655668
},
656669
_ => {

tests/ui/ptr_arg.rs

Lines changed: 114 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ fn test_cow_with_ref(c: &Cow<[i32]>) {}
123123
//~^ ptr_arg
124124

125125
fn test_cow(c: Cow<[i32]>) {
126-
let _c = c;
126+
let d = c;
127127
}
128128

129129
trait Foo2 {
@@ -141,36 +141,36 @@ mod issue_5644 {
141141
use std::path::PathBuf;
142142

143143
fn allowed(
144-
#[allow(clippy::ptr_arg)] _v: &Vec<u32>,
145-
#[allow(clippy::ptr_arg)] _s: &String,
146-
#[allow(clippy::ptr_arg)] _p: &PathBuf,
147-
#[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
148-
#[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
144+
#[allow(clippy::ptr_arg)] v: &Vec<u32>,
145+
#[allow(clippy::ptr_arg)] s: &String,
146+
#[allow(clippy::ptr_arg)] p: &PathBuf,
147+
#[allow(clippy::ptr_arg)] c: &Cow<[i32]>,
148+
#[expect(clippy::ptr_arg)] expect: &Cow<[i32]>,
149149
) {
150150
}
151151

152-
fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
152+
fn some_allowed(#[allow(clippy::ptr_arg)] v: &Vec<u32>, s: &String) {}
153153
//~^ ptr_arg
154154

155155
struct S;
156156
impl S {
157157
fn allowed(
158-
#[allow(clippy::ptr_arg)] _v: &Vec<u32>,
159-
#[allow(clippy::ptr_arg)] _s: &String,
160-
#[allow(clippy::ptr_arg)] _p: &PathBuf,
161-
#[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
162-
#[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
158+
#[allow(clippy::ptr_arg)] v: &Vec<u32>,
159+
#[allow(clippy::ptr_arg)] s: &String,
160+
#[allow(clippy::ptr_arg)] p: &PathBuf,
161+
#[allow(clippy::ptr_arg)] c: &Cow<[i32]>,
162+
#[expect(clippy::ptr_arg)] expect: &Cow<[i32]>,
163163
) {
164164
}
165165
}
166166

167167
trait T {
168168
fn allowed(
169-
#[allow(clippy::ptr_arg)] _v: &Vec<u32>,
170-
#[allow(clippy::ptr_arg)] _s: &String,
171-
#[allow(clippy::ptr_arg)] _p: &PathBuf,
172-
#[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
173-
#[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
169+
#[allow(clippy::ptr_arg)] v: &Vec<u32>,
170+
#[allow(clippy::ptr_arg)] s: &String,
171+
#[allow(clippy::ptr_arg)] p: &PathBuf,
172+
#[allow(clippy::ptr_arg)] c: &Cow<[i32]>,
173+
#[expect(clippy::ptr_arg)] expect: &Cow<[i32]>,
174174
) {
175175
}
176176
}
@@ -182,22 +182,22 @@ mod issue6509 {
182182
fn foo_vec(vec: &Vec<u8>) {
183183
//~^ ptr_arg
184184

185-
let _ = vec.clone().pop();
186-
let _ = vec.clone().clone();
185+
let a = vec.clone().pop();
186+
let b = vec.clone().clone();
187187
}
188188

189189
fn foo_path(path: &PathBuf) {
190190
//~^ ptr_arg
191191

192-
let _ = path.clone().pop();
193-
let _ = path.clone().clone();
192+
let c = path.clone().pop();
193+
let d = path.clone().clone();
194194
}
195195

196-
fn foo_str(str: &PathBuf) {
196+
fn foo_str(str: &String) {
197197
//~^ ptr_arg
198198

199-
let _ = str.clone().pop();
200-
let _ = str.clone().clone();
199+
let e = str.clone().pop();
200+
let f = str.clone().clone();
201201
}
202202
}
203203

@@ -340,8 +340,8 @@ mod issue_13308 {
340340
ToOwned::clone_into(source, destination);
341341
}
342342

343-
fn h1(_: &<String as Deref>::Target) {}
344-
fn h2<T: Deref>(_: T, _: &T::Target) {}
343+
fn h1(x: &<String as Deref>::Target) {}
344+
fn h2<T: Deref>(x: T, y: &T::Target) {}
345345

346346
// Other cases that are still ok to lint and ideally shouldn't regress
347347
fn good(v1: &String, v2: &String) {
@@ -352,3 +352,91 @@ mod issue_13308 {
352352
h2(String::new(), v2);
353353
}
354354
}
355+
356+
mod issue_13489_and_13728 {
357+
// This is a no-lint from now on.
358+
fn foo(_x: &Vec<i32>) {
359+
todo!();
360+
}
361+
362+
// But this still gives us a lint.
363+
fn foo_used(x: &Vec<i32>) {
364+
//~^ ptr_arg
365+
366+
todo!();
367+
}
368+
369+
// This is also a no-lint from now on.
370+
fn foo_local(x: &Vec<i32>) {
371+
let _y = x;
372+
373+
todo!();
374+
}
375+
376+
// But this still gives us a lint.
377+
fn foo_local_used(x: &Vec<i32>) {
378+
//~^ ptr_arg
379+
380+
let y = x;
381+
382+
todo!();
383+
}
384+
385+
// This only lints once from now on.
386+
fn foofoo(_x: &Vec<i32>, y: &String) {
387+
//~^ ptr_arg
388+
389+
todo!();
390+
}
391+
392+
// And this is also a no-lint from now on.
393+
fn foofoo_local(_x: &Vec<i32>, y: &String) {
394+
let _z = y;
395+
396+
todo!();
397+
}
398+
}
399+
400+
mod issue_13489_and_13728_mut {
401+
// This is a no-lint from now on.
402+
fn bar(_x: &mut Vec<u32>) {
403+
todo!()
404+
}
405+
406+
// But this still gives us a lint.
407+
fn bar_used(x: &mut Vec<u32>) {
408+
//~^ ptr_arg
409+
410+
todo!()
411+
}
412+
413+
// This is also a no-lint from now on.
414+
fn bar_local(x: &mut Vec<u32>) {
415+
let _y = x;
416+
417+
todo!()
418+
}
419+
420+
// But this still gives us a lint.
421+
fn bar_local_used(x: &mut Vec<u32>) {
422+
//~^ ptr_arg
423+
424+
let y = x;
425+
426+
todo!()
427+
}
428+
429+
// This only lints once from now on.
430+
fn barbar(_x: &mut Vec<u32>, y: &mut String) {
431+
//~^ ptr_arg
432+
433+
todo!()
434+
}
435+
436+
// And this is also a no-lint from now on.
437+
fn barbar_local(_x: &mut Vec<u32>, y: &mut String) {
438+
let _z = y;
439+
440+
todo!()
441+
}
442+
}

tests/ui/ptr_arg.stderr

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
127127
| ^^^^^^^^^^^ help: change this to: `&[i32]`
128128

129129
error: writing `&String` instead of `&str` involves a new object where a slice will do
130-
--> tests/ui/ptr_arg.rs:152:66
130+
--> tests/ui/ptr_arg.rs:152:64
131131
|
132-
LL | fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
133-
| ^^^^^^^ help: change this to: `&str`
132+
LL | fn some_allowed(#[allow(clippy::ptr_arg)] v: &Vec<u32>, s: &String) {}
133+
| ^^^^^^^ help: change this to: `&str`
134134

135135
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
136136
--> tests/ui/ptr_arg.rs:182:21
@@ -143,8 +143,8 @@ help: change this to
143143
LL ~ fn foo_vec(vec: &[u8]) {
144144
LL |
145145
LL |
146-
LL ~ let _ = vec.to_owned().pop();
147-
LL ~ let _ = vec.to_owned().clone();
146+
LL ~ let a = vec.to_owned().pop();
147+
LL ~ let b = vec.to_owned().clone();
148148
|
149149

150150
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
@@ -158,23 +158,23 @@ help: change this to
158158
LL ~ fn foo_path(path: &Path) {
159159
LL |
160160
LL |
161-
LL ~ let _ = path.to_path_buf().pop();
162-
LL ~ let _ = path.to_path_buf().clone();
161+
LL ~ let c = path.to_path_buf().pop();
162+
LL ~ let d = path.to_path_buf().clone();
163163
|
164164

165-
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
165+
error: writing `&String` instead of `&str` involves a new object where a slice will do
166166
--> tests/ui/ptr_arg.rs:196:21
167167
|
168-
LL | fn foo_str(str: &PathBuf) {
169-
| ^^^^^^^^
168+
LL | fn foo_str(str: &String) {
169+
| ^^^^^^^
170170
|
171171
help: change this to
172172
|
173-
LL ~ fn foo_str(str: &Path) {
173+
LL ~ fn foo_str(str: &str) {
174174
LL |
175175
LL |
176-
LL ~ let _ = str.to_path_buf().pop();
177-
LL ~ let _ = str.to_path_buf().clone();
176+
LL ~ let e = str.to_owned().pop();
177+
LL ~ let f = str.to_owned().clone();
178178
|
179179

180180
error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
@@ -231,6 +231,42 @@ error: writing `&String` instead of `&str` involves a new object where a slice w
231231
LL | fn good(v1: &String, v2: &String) {
232232
| ^^^^^^^ help: change this to: `&str`
233233

234+
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
235+
--> tests/ui/ptr_arg.rs:363:20
236+
|
237+
LL | fn foo_used(x: &Vec<i32>) {
238+
| ^^^^^^^^^ help: change this to: `&[i32]`
239+
240+
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
241+
--> tests/ui/ptr_arg.rs:377:26
242+
|
243+
LL | fn foo_local_used(x: &Vec<i32>) {
244+
| ^^^^^^^^^ help: change this to: `&[i32]`
245+
246+
error: writing `&String` instead of `&str` involves a new object where a slice will do
247+
--> tests/ui/ptr_arg.rs:386:33
248+
|
249+
LL | fn foofoo(_x: &Vec<i32>, y: &String) {
250+
| ^^^^^^^ help: change this to: `&str`
251+
252+
error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
253+
--> tests/ui/ptr_arg.rs:407:20
254+
|
255+
LL | fn bar_used(x: &mut Vec<u32>) {
256+
| ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
257+
258+
error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
259+
--> tests/ui/ptr_arg.rs:421:26
260+
|
261+
LL | fn bar_local_used(x: &mut Vec<u32>) {
262+
| ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
263+
264+
error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
265+
--> tests/ui/ptr_arg.rs:430:37
266+
|
267+
LL | fn barbar(_x: &mut Vec<u32>, y: &mut String) {
268+
| ^^^^^^^^^^^ help: change this to: `&mut str`
269+
234270
error: lifetime flowing from input to output with different syntax can be confusing
235271
--> tests/ui/ptr_arg.rs:314:36
236272
|
@@ -247,5 +283,5 @@ help: one option is to consistently use `'a`
247283
LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &'a str {
248284
| ++
249285

250-
error: aborting due to 27 previous errors
286+
error: aborting due to 33 previous errors
251287

0 commit comments

Comments
 (0)