Skip to content

Commit add771f

Browse files
committed
needless_borrow uses dropped_without_further_use
1 parent f0f6f1c commit add771f

File tree

5 files changed

+92
-9
lines changed

5 files changed

+92
-9
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};
@@ -15,6 +16,7 @@ use rustc_hir::{
1516
use rustc_index::bit_set::BitSet;
1617
use rustc_infer::infer::TyCtxtInferExt;
1718
use rustc_lint::{LateContext, LateLintPass};
19+
use rustc_middle::mir::{Rvalue, StatementKind};
1820
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
1921
use rustc_middle::ty::{
2022
self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
@@ -1061,10 +1063,10 @@ fn needless_borrow_impl_arg_position<'tcx>(
10611063
// elements are modified each time `check_referent` is called.
10621064
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
10631065

1064-
let mut check_referent = |referent| {
1066+
let mut check_reference_and_referent = |reference, referent| {
10651067
let referent_ty = cx.typeck_results().expr_ty(referent);
10661068

1067-
if !is_copy(cx, referent_ty) {
1069+
if !(is_copy(cx, referent_ty) || referent_dropped_without_further_use(cx.tcx, reference)) {
10681070
return false;
10691071
}
10701072

@@ -1106,7 +1108,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
11061108

11071109
let mut needless_borrow = false;
11081110
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
1109-
if !check_referent(referent) {
1111+
if !check_reference_and_referent(expr, referent) {
11101112
break;
11111113
}
11121114
expr = referent;
@@ -1134,6 +1136,20 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
11341136
})
11351137
}
11361138

1139+
fn referent_dropped_without_further_use(tcx: TyCtxt<'_>, reference: &Expr<'_>) -> bool {
1140+
let mir = enclosing_mir(tcx, reference.hir_id);
1141+
if let Some(local) = expr_local(tcx, reference)
1142+
&& let [location] = *local_assignments(mir, local).as_slice()
1143+
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) =
1144+
mir.basic_blocks()[location.block].statements[location.statement_index].kind
1145+
&& !place.has_deref()
1146+
{
1147+
dropped_without_further_use(mir, place.local, location).unwrap_or(false)
1148+
} else {
1149+
false
1150+
}
1151+
}
1152+
11371153
// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
11381154
// projected type that is a type parameter. Returns `false` if replacing the types would have an
11391155
// 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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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)