Skip to content

Commit 54451bd

Browse files
committed
needless_borrow uses dropped_without_further_use
1 parent 8f0535b commit 54451bd

File tree

5 files changed

+94
-11
lines changed

5 files changed

+94
-11
lines changed

clippy_lints/src/dereference.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
2+
use clippy_utils::mir::{dropped_without_further_use, enclosing_mir, expr_local, local_assignments};
23
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
34
use clippy_utils::sugg::has_enclosing_paren;
45
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
@@ -18,6 +19,7 @@ use rustc_hir::{
1819
use rustc_index::bit_set::BitSet;
1920
use rustc_infer::infer::TyCtxtInferExt;
2021
use rustc_lint::{LateContext, LateLintPass};
22+
use rustc_middle::mir::{Rvalue, StatementKind};
2123
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
2224
use rustc_middle::ty::{
2325
self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
@@ -1082,10 +1084,10 @@ fn needless_borrow_impl_arg_position<'tcx>(
10821084
// elements are modified each time `check_referent` is called.
10831085
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
10841086

1085-
let mut check_referent = |referent| {
1087+
let mut check_reference_and_referent = |reference, referent| {
10861088
let referent_ty = cx.typeck_results().expr_ty(referent);
10871089

1088-
if !is_copy(cx, referent_ty) {
1090+
if !(is_copy(cx, referent_ty) || referent_dropped_without_further_use(cx.tcx, reference)) {
10891091
return false;
10901092
}
10911093

@@ -1127,7 +1129,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
11271129

11281130
let mut needless_borrow = false;
11291131
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
1130-
if !check_referent(referent) {
1132+
if !check_reference_and_referent(expr, referent) {
11311133
break;
11321134
}
11331135
expr = referent;
@@ -1155,6 +1157,20 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
11551157
})
11561158
}
11571159

1160+
fn referent_dropped_without_further_use(tcx: TyCtxt<'_>, reference: &Expr<'_>) -> bool {
1161+
let mir = enclosing_mir(tcx, reference.hir_id);
1162+
if let Some(local) = expr_local(tcx, reference)
1163+
&& let [location] = *local_assignments(mir, local).as_slice()
1164+
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) =
1165+
mir.basic_blocks[location.block].statements[location.statement_index].kind
1166+
&& !place.has_deref()
1167+
{
1168+
dropped_without_further_use(mir, place.local, location).unwrap_or(false)
1169+
} else {
1170+
false
1171+
}
1172+
}
1173+
11581174
// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
11591175
// projected type that is a type parameter. Returns `false` if replacing the types would have an
11601176
// effect on the function signature beyond substituting `new_ty` for `param_ty`.

clippy_utils/src/mir.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use rustc_hir::{Expr, HirId};
12
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
2-
use rustc_middle::mir::{traversal, Body, Local, Location, Place};
3+
use rustc_middle::mir::{traversal, Body, Local, Location, Place, StatementKind};
4+
use rustc_middle::ty::TyCtxt;
35

46
#[derive(Clone, Default)]
57
pub struct LocalUsage {
@@ -69,3 +71,52 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
6971
}
7072
}
7173
}
74+
75+
/// Convenience wrapper around `visit_local_usage`.
76+
pub fn dropped_without_further_use(mir: &Body<'_>, local: Local, location: Location) -> Option<bool> {
77+
visit_local_usage(&[local], mir, location).map(|mut vec| {
78+
let LocalUsage {
79+
local_use_loc,
80+
local_consume_or_mutate_loc,
81+
} = vec.remove(0);
82+
local_use_loc.is_none() && local_consume_or_mutate_loc.is_none()
83+
})
84+
}
85+
86+
/// Returns the `mir::Body` containing the node associated with `hir_id`.
87+
#[allow(clippy::module_name_repetitions)]
88+
pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> {
89+
let body_owner_hir_id = tcx.hir().enclosing_body_owner(hir_id);
90+
let body_id = tcx.hir().body_owned_by(body_owner_hir_id);
91+
let body_owner_local_def_id = tcx.hir().body_owner_def_id(body_id);
92+
tcx.optimized_mir(body_owner_local_def_id.to_def_id())
93+
}
94+
95+
/// Tries to determine the `Local` corresponding to `expr`, if any.
96+
/// This function is expensive and should be used sparingly.
97+
pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
98+
let mir = enclosing_mir(tcx, expr.hir_id);
99+
mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
100+
if local_decl.source_info.span == expr.span {
101+
Some(local)
102+
} else {
103+
None
104+
}
105+
})
106+
}
107+
108+
/// Returns a vector of `mir::Location` where `local` is assigned. Each statement referred to has
109+
/// kind `StatementKind::Assign`.
110+
pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec<Location> {
111+
let mut locations = Vec::new();
112+
for (block, data) in mir.basic_blocks.iter_enumerated() {
113+
for (statement_index, statement) in data.statements.iter().enumerate() {
114+
if let StatementKind::Assign(box (place, _)) = statement.kind {
115+
if place.as_local() == Some(local) {
116+
locations.push(Location { block, statement_index });
117+
}
118+
}
119+
}
120+
}
121+
locations
122+
}

tests/ui/needless_borrow.fixed

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![feature(custom_inner_attributes, lint_reasons)]
44

55
#[warn(clippy::all, clippy::needless_borrow)]
6-
#[allow(unused_variables, clippy::unnecessary_mut_passed)]
6+
#[allow(unused_variables, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned)]
77
fn main() {
88
let a = 5;
99
let ref_a = &a;
@@ -134,6 +134,7 @@ fn main() {
134134
multiple_constraints([[""]]);
135135
multiple_constraints_normalizes_to_same(X, X);
136136
let _ = Some("").unwrap_or("");
137+
let _ = std::fs::write("x", "".to_string());
137138

138139
only_sized(&""); // Don't lint. `Sized` is only bound
139140
let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
@@ -276,8 +277,9 @@ mod copyable_iterator {
276277
fn dont_warn(mut x: Iter) {
277278
takes_iter(&mut x);
278279
}
280+
#[allow(unused_mut)]
279281
fn warn(mut x: &mut Iter) {
280-
takes_iter(&mut x)
282+
takes_iter(x)
281283
}
282284
}
283285

tests/ui/needless_borrow.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![feature(custom_inner_attributes, lint_reasons)]
44

55
#[warn(clippy::all, clippy::needless_borrow)]
6-
#[allow(unused_variables, clippy::unnecessary_mut_passed)]
6+
#[allow(unused_variables, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned)]
77
fn main() {
88
let a = 5;
99
let ref_a = &a;
@@ -134,6 +134,7 @@ fn main() {
134134
multiple_constraints(&[[""]]);
135135
multiple_constraints_normalizes_to_same(&X, X);
136136
let _ = Some("").unwrap_or(&"");
137+
let _ = std::fs::write("x", &"".to_string());
137138

138139
only_sized(&""); // Don't lint. `Sized` is only bound
139140
let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
@@ -276,6 +277,7 @@ mod copyable_iterator {
276277
fn dont_warn(mut x: Iter) {
277278
takes_iter(&mut x);
278279
}
280+
#[allow(unused_mut)]
279281
fn warn(mut x: &mut Iter) {
280282
takes_iter(&mut x)
281283
}

tests/ui/needless_borrow.stderr

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,23 +156,35 @@ error: this expression creates a reference which is immediately dereferenced by
156156
LL | let _ = Some("").unwrap_or(&"");
157157
| ^^^ help: change this to: `""`
158158

159+
error: the borrowed expression implements the required traits
160+
--> $DIR/needless_borrow.rs:137:33
161+
|
162+
LL | let _ = std::fs::write("x", &"".to_string());
163+
| ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()`
164+
159165
error: this expression borrows a value the compiler would automatically borrow
160-
--> $DIR/needless_borrow.rs:187:13
166+
--> $DIR/needless_borrow.rs:188:13
161167
|
162168
LL | (&self.f)()
163169
| ^^^^^^^^^ help: change this to: `(self.f)`
164170

165171
error: this expression borrows a value the compiler would automatically borrow
166-
--> $DIR/needless_borrow.rs:196:13
172+
--> $DIR/needless_borrow.rs:197:13
167173
|
168174
LL | (&mut self.f)()
169175
| ^^^^^^^^^^^^^ help: change this to: `(self.f)`
170176

171177
error: the borrowed expression implements the required traits
172-
--> $DIR/needless_borrow.rs:298:55
178+
--> $DIR/needless_borrow.rs:282:20
179+
|
180+
LL | takes_iter(&mut x)
181+
| ^^^^^^ help: change this to: `x`
182+
183+
error: the borrowed expression implements the required traits
184+
--> $DIR/needless_borrow.rs:300:55
173185
|
174186
LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
175187
| ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
176188

177-
error: aborting due to 29 previous errors
189+
error: aborting due to 31 previous errors
178190

0 commit comments

Comments
 (0)