Skip to content

Commit e669d97

Browse files
committed
Auto merge of rust-lang#12706 - pacak:less-aggressive-needless-borrows, r=dswij
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 copy values or values created in place 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 rust-lang/rust-clippy#12454 changelog: [`needless_borrows_for_generic_args`]: avoid moving variables
2 parents a9b5c8c + 79a14de commit e669d97

4 files changed

+88
-61
lines changed

clippy_lints/src/needless_borrows_for_generic_args.rs

Lines changed: 5 additions & 40 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,9 @@ 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.
159-
#[expect(clippy::too_many_arguments)]
156+
/// * The borrowed value is Copy itself OR not a variable (created by a function call)
160157
fn needless_borrow_count<'tcx>(
161158
cx: &LateContext<'tcx>,
162-
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
163159
fn_id: DefId,
164160
callee_args: &'tcx List<GenericArg<'tcx>>,
165161
arg_index: usize,
@@ -234,9 +230,9 @@ fn needless_borrow_count<'tcx>(
234230

235231
let referent_ty = cx.typeck_results().expr_ty(referent);
236232

237-
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))
233+
if (!is_copy(cx, referent_ty) && !referent_ty.is_ref())
234+
&& let ExprKind::AddrOf(_, _, inner) = reference.kind
235+
&& !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
240236
{
241237
return false;
242238
}
@@ -339,37 +335,6 @@ fn is_mixed_projection_predicate<'tcx>(
339335
}
340336
}
341337

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-
373338
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
374339
// projected type that is a type parameter. Returns `false` if replacing the types would have an
375340
// effect on the function signature beyond substituting `new_ty` for `param_ty`.

tests/ui/needless_borrows_for_generic_args.fixed

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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);
@@ -158,13 +158,13 @@ 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>) {}
165165

166166
let x = String::new();
167-
f(x);
167+
f(&x);
168168
}
169169
{
170170
fn f(_: impl AsRef<str>) {}
@@ -299,4 +299,38 @@ 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+
}
327+
{
328+
fn f(_: impl AsRef<str>) {}
329+
let x = String::new();
330+
f(&x); // Don't lint, not a copy, makes it unavailable later
331+
f(String::new()); // Lint, makes no difference
332+
let y = "".to_owned();
333+
f(&y); // Don't lint
334+
f("".to_owned()); // Lint
335+
}
302336
}

tests/ui/needless_borrows_for_generic_args.rs

Lines changed: 35 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,38 @@ 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+
}
327+
{
328+
fn f(_: impl AsRef<str>) {}
329+
let x = String::new();
330+
f(&x); // Don't lint, not a copy, makes it unavailable later
331+
f(&String::new()); // Lint, makes no difference
332+
let y = "".to_owned();
333+
f(&y); // Don't lint
334+
f(&"".to_owned()); // Lint
335+
}
302336
}

tests/ui/needless_borrows_for_generic_args.stderr

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,22 @@ LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
5050
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
5151

5252
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
53+
--> tests/ui/needless_borrows_for_generic_args.rs:247:13
6054
|
61-
LL | let _ = std::fs::write("x", &loc);
62-
| ^^^^ help: change this to: `loc`
55+
LL | foo(&a);
56+
| ^^ help: change this to: `a`
6357

6458
error: the borrowed expression implements the required traits
65-
--> tests/ui/needless_borrows_for_generic_args.rs:167:11
59+
--> tests/ui/needless_borrows_for_generic_args.rs:331:11
6660
|
67-
LL | f(&x);
68-
| ^^ help: change this to: `x`
61+
LL | f(&String::new()); // Lint, makes no difference
62+
| ^^^^^^^^^^^^^^ help: change this to: `String::new()`
6963

7064
error: the borrowed expression implements the required traits
71-
--> tests/ui/needless_borrows_for_generic_args.rs:247:13
65+
--> tests/ui/needless_borrows_for_generic_args.rs:334:11
7266
|
73-
LL | foo(&a);
74-
| ^^ help: change this to: `a`
67+
LL | f(&"".to_owned()); // Lint
68+
| ^^^^^^^^^^^^^^ help: change this to: `"".to_owned()`
7569

76-
error: aborting due to 12 previous errors
70+
error: aborting due to 11 previous errors
7771

0 commit comments

Comments
 (0)