Skip to content

Commit ebe333c

Browse files
committed
less aggressive needless_borrows_for_generic_args
Current implementation looks for significant drops, that can change the behavior, but that's not enough - value might not have a Drop itself but one of its children might have it. A good example is passing a reference to `PathBuf` to `std::fs::File::open`. There's no benefits to pass `PathBuf` by value, but since clippy can't see `Drop` on `Vec` several layers down it complains forcing pass by value and making it impossible to use the same name later. New implementation only looks at variables and only those that are copy, so existing variable will never be moved but things that take a string reference created and value is created inplace `&"".to_owned()` will make it to suggest to use `"".to_owned()` still. Fixes #12454
1 parent 7fcaa60 commit ebe333c

File tree

4 files changed

+61
-69
lines changed

4 files changed

+61
-69
lines changed

clippy_lints/src/needless_borrows_for_generic_args.rs

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_config::msrvs::{self, Msrv};
22
use clippy_utils::diagnostics::span_lint_and_then;
3-
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
3+
use clippy_utils::mir::PossibleBorrowerMap;
44
use clippy_utils::source::snippet_with_context;
55
use clippy_utils::ty::{implements_trait, is_copy};
66
use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
@@ -11,7 +11,6 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
1111
use rustc_index::bit_set::BitSet;
1212
use rustc_infer::infer::TyCtxtInferExt;
1313
use rustc_lint::{LateContext, LateLintPass};
14-
use rustc_middle::mir::{Rvalue, StatementKind};
1514
use rustc_middle::ty::{
1615
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty,
1716
};
@@ -106,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
106105
}
107106
&& let count = needless_borrow_count(
108107
cx,
109-
&mut self.possible_borrowers,
110108
fn_id,
111109
cx.typeck_results().node_args(hir_id),
112110
i,
@@ -155,11 +153,10 @@ fn path_has_args(p: &QPath<'_>) -> bool {
155153
/// The following constraints will be checked:
156154
/// * The borrowed expression meets all the generic type's constraints.
157155
/// * The generic type appears only once in the functions signature.
158-
/// * The borrowed value will not be moved if it is used later in the function.
156+
/// * The borrowed value is Copy itself OR not a variable (created by a function call)
159157
#[expect(clippy::too_many_arguments)]
160158
fn needless_borrow_count<'tcx>(
161159
cx: &LateContext<'tcx>,
162-
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
163160
fn_id: DefId,
164161
callee_args: &'tcx List<GenericArg<'tcx>>,
165162
arg_index: usize,
@@ -235,8 +232,8 @@ fn needless_borrow_count<'tcx>(
235232
let referent_ty = cx.typeck_results().expr_ty(referent);
236233

237234
if !is_copy(cx, referent_ty)
238-
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
239-
|| !referent_used_exactly_once(cx, possible_borrowers, reference))
235+
&& let ExprKind::AddrOf(_, _, inner) = reference.kind
236+
&& matches!(inner.kind, ExprKind::Path(_))
240237
{
241238
return false;
242239
}
@@ -339,37 +336,6 @@ fn is_mixed_projection_predicate<'tcx>(
339336
}
340337
}
341338

342-
fn referent_used_exactly_once<'tcx>(
343-
cx: &LateContext<'tcx>,
344-
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
345-
reference: &Expr<'tcx>,
346-
) -> bool {
347-
if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
348-
&& let Some(local) = expr_local(cx.tcx, reference)
349-
&& let [location] = *local_assignments(mir, local).as_slice()
350-
&& let block_data = &mir.basic_blocks[location.block]
351-
&& let Some(statement) = block_data.statements.get(location.statement_index)
352-
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
353-
&& !place.is_indirect_first_projection()
354-
{
355-
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
356-
if possible_borrowers
357-
.last()
358-
.map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
359-
{
360-
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
361-
}
362-
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
363-
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
364-
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
365-
// itself. See the comment in that method for an explanation as to why.
366-
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
367-
&& used_exactly_once(mir, place.local).unwrap_or(false)
368-
} else {
369-
false
370-
}
371-
}
372-
373339
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
374340
// projected type that is a type parameter. Returns `false` if replacing the types would have an
375341
// effect on the function signature beyond substituting `new_ty` for `param_ty`.

tests/ui/needless_borrows_for_generic_args.fixed

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use core::ops::Deref;
99
use std::any::Any;
1010
use std::ffi::OsStr;
1111
use std::fmt::{Debug, Display};
12-
use std::path::Path;
12+
use std::path::{Path, PathBuf};
1313
use std::process::Command;
1414

1515
fn main() {
@@ -124,7 +124,7 @@ fn main() {
124124
}
125125
#[allow(unused_mut)]
126126
fn warn(mut x: &mut Iter) {
127-
takes_iter(x)
127+
takes_iter(&mut x)
128128
}
129129
}
130130
#[clippy::msrv = "1.52.0"]
@@ -141,8 +141,8 @@ fn main() {
141141
let f = |arg| {
142142
let loc = "loc".to_owned();
143143
let _ = std::fs::write("x", &env); // Don't lint. In environment
144-
let _ = std::fs::write("x", arg);
145-
let _ = std::fs::write("x", loc);
144+
let _ = std::fs::write("x", &arg);
145+
let _ = std::fs::write("x", &loc);
146146
};
147147
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
148148
f(arg);
@@ -164,7 +164,7 @@ fn main() {
164164
fn f(_: impl AsRef<str>) {}
165165

166166
let x = String::new();
167-
f(x);
167+
f(&x);
168168
}
169169
{
170170
fn f(_: impl AsRef<str>) {}
@@ -299,4 +299,29 @@ fn main() {
299299
check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop
300300
}
301301
}
302+
{
303+
#[derive(Debug)]
304+
struct X(Vec<u8>);
305+
306+
fn f(_: impl Debug) {}
307+
308+
let x = X(vec![]);
309+
f(&x); // Don't lint, makes x unavailable later
310+
}
311+
{
312+
#[derive(Debug)]
313+
struct X;
314+
315+
impl Drop for X {
316+
fn drop(&mut self) {}
317+
}
318+
319+
fn f(_: impl Debug) {}
320+
321+
#[derive(Debug)]
322+
struct Y(X);
323+
324+
let y = Y(X);
325+
f(&y); // Don't lint. Has significant drop
326+
}
302327
}

tests/ui/needless_borrows_for_generic_args.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ fn main() {
158158
fn f(_: impl Debug) {}
159159

160160
let x = X;
161-
f(&x); // Don't lint. Has significant drop
161+
f(&x); // Don't lint, not copy, passed by a reference to a variable
162162
}
163163
{
164164
fn f(_: impl AsRef<str>) {}
@@ -299,4 +299,29 @@ fn main() {
299299
check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop
300300
}
301301
}
302+
{
303+
#[derive(Debug)]
304+
struct X(Vec<u8>);
305+
306+
fn f(_: impl Debug) {}
307+
308+
let x = X(vec![]);
309+
f(&x); // Don't lint, makes x unavailable later
310+
}
311+
{
312+
#[derive(Debug)]
313+
struct X;
314+
315+
impl Drop for X {
316+
fn drop(&mut self) {}
317+
}
318+
319+
fn f(_: impl Debug) {}
320+
321+
#[derive(Debug)]
322+
struct Y(X);
323+
324+
let y = Y(X);
325+
f(&y); // Don't lint. Not copy, passed by a reference to value
326+
}
302327
}

tests/ui/needless_borrows_for_generic_args.stderr

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,41 +37,17 @@ error: the borrowed expression implements the required traits
3737
LL | multiple_constraints_normalizes_to_same(&X, X);
3838
| ^^ help: change this to: `X`
3939

40-
error: the borrowed expression implements the required traits
41-
--> tests/ui/needless_borrows_for_generic_args.rs:127:24
42-
|
43-
LL | takes_iter(&mut x)
44-
| ^^^^^^ help: change this to: `x`
45-
4640
error: the borrowed expression implements the required traits
4741
--> tests/ui/needless_borrows_for_generic_args.rs:136:41
4842
|
4943
LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
5044
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
5145

52-
error: the borrowed expression implements the required traits
53-
--> tests/ui/needless_borrows_for_generic_args.rs:144:41
54-
|
55-
LL | let _ = std::fs::write("x", &arg);
56-
| ^^^^ help: change this to: `arg`
57-
58-
error: the borrowed expression implements the required traits
59-
--> tests/ui/needless_borrows_for_generic_args.rs:145:41
60-
|
61-
LL | let _ = std::fs::write("x", &loc);
62-
| ^^^^ help: change this to: `loc`
63-
64-
error: the borrowed expression implements the required traits
65-
--> tests/ui/needless_borrows_for_generic_args.rs:167:11
66-
|
67-
LL | f(&x);
68-
| ^^ help: change this to: `x`
69-
7046
error: the borrowed expression implements the required traits
7147
--> tests/ui/needless_borrows_for_generic_args.rs:247:13
7248
|
7349
LL | foo(&a);
7450
| ^^ help: change this to: `a`
7551

76-
error: aborting due to 12 previous errors
52+
error: aborting due to 8 previous errors
7753

0 commit comments

Comments
 (0)