Skip to content

Commit 414add3

Browse files
committed
1 parent 75ea471 commit 414add3

12 files changed

+139
-52
lines changed

clippy_lints/src/dereference.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
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};
2+
use clippy_utils::mir::{
3+
dropped_without_further_use, enclosing_mir, expr_local, local_assignments, PossibleBorrowerMap,
4+
};
35
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
46
use clippy_utils::sugg::has_enclosing_paren;
57
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
@@ -1059,14 +1061,19 @@ fn needless_borrow_impl_arg_position<'tcx>(
10591061
return Position::Other(precedence);
10601062
}
10611063

1064+
let mut possible_borrower = None;
1065+
10621066
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
10631067
// elements are modified each time `check_referent` is called.
10641068
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
10651069

10661070
let mut check_reference_and_referent = |reference, referent| {
10671071
let referent_ty = cx.typeck_results().expr_ty(referent);
10681072

1069-
if !(is_copy(cx, referent_ty) || referent_dropped_without_further_use(cx.tcx, reference)) {
1073+
if !is_copy(cx, referent_ty)
1074+
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
1075+
|| !referent_dropped_without_further_use(cx, &mut possible_borrower, reference))
1076+
{
10701077
return false;
10711078
}
10721079

@@ -1136,15 +1143,24 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
11361143
})
11371144
}
11381145

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)
1146+
fn referent_dropped_without_further_use<'a, 'tcx>(
1147+
cx: &'a LateContext<'tcx>,
1148+
possible_borrower: &mut Option<PossibleBorrowerMap<'a, 'tcx>>,
1149+
reference: &Expr<'tcx>,
1150+
) -> bool {
1151+
let mir = enclosing_mir(cx.tcx, reference.hir_id);
1152+
if let Some(local) = expr_local(cx.tcx, reference)
11421153
&& let [location] = *local_assignments(mir, local).as_slice()
11431154
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) =
11441155
mir.basic_blocks()[location.block].statements[location.statement_index].kind
11451156
&& !place.has_deref()
11461157
{
1147-
dropped_without_further_use(mir, place.local, location).unwrap_or(false)
1158+
let possible_borrower = possible_borrower.get_or_insert_with(|| PossibleBorrowerMap::new(cx, mir));
1159+
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
1160+
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
1161+
// itself. See the comment in that method for an explanation as to why.
1162+
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
1163+
&& dropped_without_further_use(mir, place.local, location).unwrap_or(false)
11481164
} else {
11491165
false
11501166
}

clippy_lints/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ extern crate rustc_infer;
3737
extern crate rustc_lexer;
3838
extern crate rustc_lint;
3939
extern crate rustc_middle;
40-
extern crate rustc_mir_dataflow;
4140
extern crate rustc_parse;
4241
extern crate rustc_parse_format;
4342
extern crate rustc_session;

clippy_lints/src/redundant_clone/mod.rs renamed to clippy_lints/src/redundant_clone.rs

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
2-
use clippy_utils::mir::{visit_local_usage, LocalUsage};
2+
use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
33
use clippy_utils::source::snippet_opt;
44
use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
55
use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
@@ -8,24 +8,12 @@ use rustc_errors::Applicability;
88
use rustc_hir::intravisit::FnKind;
99
use rustc_hir::{def_id, Body, FnDecl, HirId};
1010
use rustc_lint::{LateContext, LateLintPass};
11-
use rustc_middle::mir::{self, visit::Visitor as _};
11+
use rustc_middle::mir;
1212
use rustc_middle::ty::{self, Ty};
13-
use rustc_mir_dataflow::Analysis;
1413
use rustc_session::{declare_lint_pass, declare_tool_lint};
1514
use rustc_span::source_map::{BytePos, Span};
1615
use rustc_span::sym;
1716

18-
mod maybe_storage_live;
19-
use maybe_storage_live::MaybeStorageLive;
20-
21-
mod possible_borrower;
22-
use possible_borrower::PossibleBorrowerVisitor;
23-
24-
mod possible_origin;
25-
use possible_origin::PossibleOriginVisitor;
26-
27-
mod transitive_relation;
28-
2917
macro_rules! unwrap_or_continue {
3018
($x:expr) => {
3119
match $x {
@@ -94,21 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
9482

9583
let mir = cx.tcx.optimized_mir(def_id.to_def_id());
9684

97-
let possible_origin = {
98-
let mut vis = PossibleOriginVisitor::new(mir);
99-
vis.visit_body(mir);
100-
vis.into_map(cx)
101-
};
102-
let maybe_storage_live_result = MaybeStorageLive
103-
.into_engine(cx.tcx, mir)
104-
.pass_name("redundant_clone")
105-
.iterate_to_fixpoint()
106-
.into_results_cursor(mir);
107-
let mut possible_borrower = {
108-
let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
109-
vis.visit_body(mir);
110-
vis.into_map(cx, maybe_storage_live_result)
111-
};
85+
let mut possible_borrower = PossibleBorrowerMap::new(cx, mir);
11286

11387
for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
11488
let terminator = bbdata.terminator();

clippy_utils/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ extern crate rustc_attr;
2323
extern crate rustc_data_structures;
2424
extern crate rustc_errors;
2525
extern crate rustc_hir;
26+
extern crate rustc_index;
2627
extern crate rustc_infer;
2728
extern crate rustc_lexer;
2829
extern crate rustc_lint;
2930
extern crate rustc_middle;
31+
extern crate rustc_mir_dataflow;
3032
extern crate rustc_parse_format;
3133
extern crate rustc_session;
3234
extern crate rustc_span;

clippy_lints/src/redundant_clone/maybe_storage_live.rs renamed to clippy_utils/src/mir/maybe_storage_live.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnaly
44

55
/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
66
#[derive(Copy, Clone)]
7-
pub struct MaybeStorageLive;
7+
pub(super) struct MaybeStorageLive;
88

99
impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
1010
type Domain = BitSet<mir::Local>;

clippy_utils/src/mir.rs renamed to clippy_utils/src/mir/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC
33
use rustc_middle::mir::{traversal, Body, Local, Location, Place, StatementKind};
44
use rustc_middle::ty::TyCtxt;
55

6+
mod maybe_storage_live;
7+
8+
mod possible_borrower;
9+
pub use possible_borrower::PossibleBorrowerMap;
10+
11+
mod possible_origin;
12+
13+
mod transitive_relation;
14+
615
#[derive(Clone, Default)]
716
pub struct LocalUsage {
817
/// The first location where the local is used, if any.

clippy_lints/src/redundant_clone/possible_borrower.rs renamed to clippy_utils/src/mir/possible_borrower.rs

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
1-
use super::{maybe_storage_live::MaybeStorageLive, transitive_relation::TransitiveRelation};
2-
use clippy_utils::ty::is_copy;
1+
use super::{
2+
maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor,
3+
transitive_relation::TransitiveRelation,
4+
};
5+
use crate::ty::is_copy;
36
use rustc_data_structures::fx::FxHashMap;
47
use rustc_index::bit_set::{BitSet, HybridBitSet};
58
use rustc_lint::LateContext;
6-
use rustc_middle::mir::{self, Mutability};
9+
use rustc_middle::mir::{self, visit::Visitor as _, Mutability};
710
use rustc_middle::ty::{self, visit::TypeVisitor};
8-
use rustc_mir_dataflow::ResultsCursor;
11+
use rustc_mir_dataflow::{Analysis, ResultsCursor};
912
use std::ops::ControlFlow;
1013

1114
/// Collects the possible borrowers of each local.
1215
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
1316
/// possible borrowers of `a`.
1417
#[allow(clippy::module_name_repetitions)]
15-
pub struct PossibleBorrowerVisitor<'a, 'tcx> {
18+
struct PossibleBorrowerVisitor<'a, 'tcx> {
1619
possible_borrower: TransitiveRelation,
1720
body: &'a mir::Body<'tcx>,
1821
cx: &'a LateContext<'tcx>,
1922
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
2023
}
2124

2225
impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
23-
pub fn new(
26+
fn new(
2427
cx: &'a LateContext<'tcx>,
2528
body: &'a mir::Body<'tcx>,
2629
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
@@ -33,10 +36,10 @@ impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
3336
}
3437
}
3538

36-
pub fn into_map(
39+
fn into_map(
3740
self,
3841
cx: &LateContext<'tcx>,
39-
maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
42+
maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
4043
) -> PossibleBorrowerMap<'a, 'tcx> {
4144
let mut map = FxHashMap::default();
4245
for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
@@ -172,9 +175,37 @@ pub struct PossibleBorrowerMap<'a, 'tcx> {
172175
bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
173176
}
174177

175-
impl PossibleBorrowerMap<'_, '_> {
178+
impl<'a, 'tcx> PossibleBorrowerMap<'a, 'tcx> {
179+
pub fn new(cx: &'a LateContext<'tcx>, mir: &'a mir::Body<'tcx>) -> Self {
180+
let possible_origin = {
181+
let mut vis = PossibleOriginVisitor::new(mir);
182+
vis.visit_body(mir);
183+
vis.into_map(cx)
184+
};
185+
let maybe_storage_live_result = MaybeStorageLive
186+
.into_engine(cx.tcx, mir)
187+
.pass_name("redundant_clone")
188+
.iterate_to_fixpoint()
189+
.into_results_cursor(mir);
190+
let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
191+
vis.visit_body(mir);
192+
vis.into_map(cx, maybe_storage_live_result)
193+
}
194+
176195
/// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
177196
pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
197+
self.bounded_borrowers(borrowers, borrowers, borrowed, at)
198+
}
199+
200+
/// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below`
201+
/// but no more than `above`.
202+
pub fn bounded_borrowers(
203+
&mut self,
204+
below: &[mir::Local],
205+
above: &[mir::Local],
206+
borrowed: mir::Local,
207+
at: mir::Location,
208+
) -> bool {
178209
self.maybe_live.seek_after_primary_effect(at);
179210

180211
self.bitset.0.clear();
@@ -188,11 +219,19 @@ impl PossibleBorrowerMap<'_, '_> {
188219
}
189220

190221
self.bitset.1.clear();
191-
for b in borrowers {
222+
for b in below {
192223
self.bitset.1.insert(*b);
193224
}
194225

195-
self.bitset.0 == self.bitset.1
226+
if !self.bitset.0.superset(&self.bitset.1) {
227+
return false;
228+
}
229+
230+
for b in above {
231+
self.bitset.0.remove(*b);
232+
}
233+
234+
self.bitset.0.is_empty()
196235
}
197236

198237
pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {

clippy_lints/src/redundant_clone/possible_origin.rs renamed to clippy_utils/src/mir/possible_origin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::transitive_relation::TransitiveRelation;
2-
use clippy_utils::ty::is_copy;
2+
use crate::ty::is_copy;
33
use rustc_data_structures::fx::FxHashMap;
44
use rustc_index::bit_set::HybridBitSet;
55
use rustc_lint::LateContext;
@@ -9,7 +9,7 @@ use rustc_middle::mir;
99
/// For example, `_1 = &mut _2` generate _1: {_2,...}
1010
/// Known Problems: not sure all borrowed are tracked
1111
#[allow(clippy::module_name_repetitions)]
12-
pub struct PossibleOriginVisitor<'a, 'tcx> {
12+
pub(super) struct PossibleOriginVisitor<'a, 'tcx> {
1313
possible_origin: TransitiveRelation,
1414
body: &'a mir::Body<'tcx>,
1515
}

clippy_lints/src/redundant_clone/transitive_relation.rs renamed to clippy_utils/src/mir/transitive_relation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_index::bit_set::HybridBitSet;
33
use rustc_middle::mir;
44

55
#[derive(Default)]
6-
pub struct TransitiveRelation {
6+
pub(super) struct TransitiveRelation {
77
relations: FxHashMap<mir::Local, Vec<mir::Local>>,
88
}
99

tests/ui/needless_borrow.fixed

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,5 +311,26 @@ fn closure_test() {
311311
let _ = std::fs::write("x", arg);
312312
let _ = std::fs::write("x", loc);
313313
};
314+
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
314315
f(arg);
315316
}
317+
318+
#[allow(dead_code)]
319+
mod significant_drop {
320+
#[derive(Debug)]
321+
struct X;
322+
323+
#[derive(Debug)]
324+
struct Y;
325+
326+
impl Drop for Y {
327+
fn drop(&mut self) {}
328+
}
329+
330+
fn foo(x: X, y: Y) {
331+
debug(x);
332+
debug(&y); // Don't lint. Has significant drop
333+
}
334+
335+
fn debug(_: impl std::fmt::Debug) {}
336+
}

tests/ui/needless_borrow.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,5 +311,26 @@ fn closure_test() {
311311
let _ = std::fs::write("x", &arg);
312312
let _ = std::fs::write("x", &loc);
313313
};
314+
let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
314315
f(arg);
315316
}
317+
318+
#[allow(dead_code)]
319+
mod significant_drop {
320+
#[derive(Debug)]
321+
struct X;
322+
323+
#[derive(Debug)]
324+
struct Y;
325+
326+
impl Drop for Y {
327+
fn drop(&mut self) {}
328+
}
329+
330+
fn foo(x: X, y: Y) {
331+
debug(&x);
332+
debug(&y); // Don't lint. Has significant drop
333+
}
334+
335+
fn debug(_: impl std::fmt::Debug) {}
336+
}

tests/ui/needless_borrow.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,5 +198,11 @@ error: the borrowed expression implements the required traits
198198
LL | let _ = std::fs::write("x", &loc);
199199
| ^^^^ help: change this to: `loc`
200200

201-
error: aborting due to 33 previous errors
201+
error: the borrowed expression implements the required traits
202+
--> $DIR/needless_borrow.rs:331:15
203+
|
204+
LL | debug(&x);
205+
| ^^ help: change this to: `x`
206+
207+
error: aborting due to 34 previous errors
202208

0 commit comments

Comments
 (0)