Skip to content

Commit a7d6406

Browse files
committed
1 parent d3ebdf4 commit a7d6406

File tree

12 files changed

+139
-52
lines changed

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};
@@ -1062,14 +1064,19 @@ fn needless_borrow_impl_arg_position<'tcx>(
10621064
return Position::Other(precedence);
10631065
}
10641066

1067+
let mut possible_borrower = None;
1068+
10651069
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
10661070
// elements are modified each time `check_referent` is called.
10671071
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
10681072

10691073
let mut check_reference_and_referent = |reference, referent| {
10701074
let referent_ty = cx.typeck_results().expr_ty(referent);
10711075

1072-
if !(is_copy(cx, referent_ty) || referent_dropped_without_further_use(cx.tcx, reference)) {
1076+
if !is_copy(cx, referent_ty)
1077+
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
1078+
|| !referent_dropped_without_further_use(cx, &mut possible_borrower, reference))
1079+
{
10731080
return false;
10741081
}
10751082

@@ -1139,15 +1146,24 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
11391146
})
11401147
}
11411148

1142-
fn referent_dropped_without_further_use(tcx: TyCtxt<'_>, reference: &Expr<'_>) -> bool {
1143-
let mir = enclosing_mir(tcx, reference.hir_id);
1144-
if let Some(local) = expr_local(tcx, reference)
1149+
fn referent_dropped_without_further_use<'a, 'tcx>(
1150+
cx: &'a LateContext<'tcx>,
1151+
possible_borrower: &mut Option<PossibleBorrowerMap<'a, 'tcx>>,
1152+
reference: &Expr<'tcx>,
1153+
) -> bool {
1154+
let mir = enclosing_mir(cx.tcx, reference.hir_id);
1155+
if let Some(local) = expr_local(cx.tcx, reference)
11451156
&& let [location] = *local_assignments(mir, local).as_slice()
11461157
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) =
11471158
mir.basic_blocks[location.block].statements[location.statement_index].kind
11481159
&& !place.has_deref()
11491160
{
1150-
dropped_without_further_use(mir, place.local, location).unwrap_or(false)
1161+
let possible_borrower = possible_borrower.get_or_insert_with(|| PossibleBorrowerMap::new(cx, mir));
1162+
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
1163+
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
1164+
// itself. See the comment in that method for an explanation as to why.
1165+
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
1166+
&& dropped_without_further_use(mir, place.local, location).unwrap_or(false)
11511167
} else {
11521168
false
11531169
}

clippy_lints/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ extern crate rustc_infer;
3838
extern crate rustc_lexer;
3939
extern crate rustc_lint;
4040
extern crate rustc_middle;
41-
extern crate rustc_mir_dataflow;
4241
extern crate rustc_parse;
4342
extern crate rustc_parse_format;
4443
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
@@ -24,10 +24,12 @@ extern crate rustc_attr;
2424
extern crate rustc_data_structures;
2525
extern crate rustc_errors;
2626
extern crate rustc_hir;
27+
extern crate rustc_index;
2728
extern crate rustc_infer;
2829
extern crate rustc_lexer;
2930
extern crate rustc_lint;
3031
extern crate rustc_middle;
32+
extern crate rustc_mir_dataflow;
3133
extern crate rustc_parse_format;
3234
extern crate rustc_session;
3335
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+
}

0 commit comments

Comments
 (0)