1 use clippy_utils
::diagnostics
::{span_lint_hir, span_lint_hir_and_then}
;
2 use clippy_utils
::source
::snippet_opt
;
3 use clippy_utils
::ty
::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}
;
4 use clippy_utils
::{fn_has_unsatisfiable_preds, match_def_path, paths}
;
5 use if_chain
::if_chain
;
6 use rustc_data_structures
::fx
::FxHashMap
;
7 use rustc_errors
::Applicability
;
8 use rustc_hir
::intravisit
::FnKind
;
9 use rustc_hir
::{def_id, Body, FnDecl, HirId}
;
10 use rustc_index
::bit_set
::{BitSet, HybridBitSet}
;
11 use rustc_lint
::{LateContext, LateLintPass}
;
12 use rustc_middle
::mir
::{
14 visit
::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}
,
17 use rustc_middle
::ty
::{self, fold::TypeVisitor, Ty}
;
18 use rustc_mir_dataflow
::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor}
;
19 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
20 use rustc_span
::source_map
::{BytePos, Span}
;
22 use std
::ops
::ControlFlow
;
24 macro_rules
! unwrap_or_continue
{
33 declare_clippy_lint
! {
35 /// Checks for a redundant `clone()` (and its relatives) which clones an owned
36 /// value that is going to be dropped without further use.
38 /// ### Why is this bad?
39 /// It is not always possible for the compiler to eliminate useless
40 /// allocations and deallocations generated by redundant `clone()`s.
42 /// ### Known problems
43 /// False-negatives: analysis performed by this lint is conservative and limited.
47 /// # use std::path::Path;
48 /// # #[derive(Clone)]
51 /// # fn new() -> Self { Foo {} }
53 /// # fn call(x: Foo) {}
55 /// let x = Foo::new();
57 /// call(x.clone()); // this can just pass `x`
60 /// ["lorem", "ipsum"].join(" ").to_string();
62 /// Path::new("/a/b").join("c").to_path_buf();
64 #[clippy::version = "1.32.0"]
67 "`clone()` of an owned value that is going to be dropped immediately"
70 declare_lint_pass
!(RedundantClone
=> [REDUNDANT_CLONE
]);
72 impl<'tcx
> LateLintPass
<'tcx
> for RedundantClone
{
73 #[expect(clippy::too_many_lines)]
76 cx
: &LateContext
<'tcx
>,
83 let def_id
= cx
.tcx
.hir().body_owner_def_id(body
.id());
85 // Building MIR for `fn`s with unsatisfiable preds results in ICE.
86 if fn_has_unsatisfiable_preds(cx
, def_id
.to_def_id()) {
90 let mir
= cx
.tcx
.optimized_mir(def_id
.to_def_id());
92 let possible_origin
= {
93 let mut vis
= PossibleOriginVisitor
::new(mir
);
97 let maybe_storage_live_result
= MaybeStorageLive
98 .into_engine(cx
.tcx
, mir
)
99 .pass_name("redundant_clone")
100 .iterate_to_fixpoint()
101 .into_results_cursor(mir
);
102 let mut possible_borrower
= {
103 let mut vis
= PossibleBorrowerVisitor
::new(cx
, mir
, possible_origin
);
105 vis
.into_map(cx
, maybe_storage_live_result
)
108 for (bb
, bbdata
) in mir
.basic_blocks().iter_enumerated() {
109 let terminator
= bbdata
.terminator();
111 if terminator
.source_info
.span
.from_expansion() {
116 if terminator
.successors().any(|s
| s
== bb
) {
120 let (fn_def_id
, arg
, arg_ty
, clone_ret
) =
121 unwrap_or_continue
!(is_call_with_ref_arg(cx
, mir
, &terminator
.kind
));
123 let from_borrow
= match_def_path(cx
, fn_def_id
, &paths
::CLONE_TRAIT_METHOD
)
124 || match_def_path(cx
, fn_def_id
, &paths
::TO_OWNED_METHOD
)
125 || (match_def_path(cx
, fn_def_id
, &paths
::TO_STRING_METHOD
)
126 && is_type_diagnostic_item(cx
, arg_ty
, sym
::String
));
128 let from_deref
= !from_borrow
129 && (match_def_path(cx
, fn_def_id
, &paths
::PATH_TO_PATH_BUF
)
130 || match_def_path(cx
, fn_def_id
, &paths
::OS_STR_TO_OS_STRING
));
132 if !from_borrow
&& !from_deref
{
136 if let ty
::Adt(def
, _
) = arg_ty
.kind() {
137 if def
.is_manually_drop() {
142 // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }`
143 let (cloned
, cannot_move_out
) = unwrap_or_continue
!(find_stmt_assigns_to(cx
, mir
, arg
, from_borrow
, bb
));
145 let loc
= mir
::Location
{
147 statement_index
: bbdata
.statements
.len(),
150 // `Local` to be cloned, and a local of `clone` call's destination
151 let (local
, ret_local
) = if from_borrow
{
152 // `res = clone(arg)` can be turned into `res = move arg;`
153 // if `arg` is the only borrow of `cloned` at this point.
155 if cannot_move_out
|| !possible_borrower
.only_borrowers(&[arg
], cloned
, loc
) {
161 // `arg` is a reference as it is `.deref()`ed in the previous block.
162 // Look into the predecessor block and find out the source of deref.
164 let ps
= &mir
.predecessors()[bb
];
168 let pred_terminator
= mir
[ps
[0]].terminator();
170 // receiver of the `deref()` call
171 let (pred_arg
, deref_clone_ret
) = if_chain
! {
172 if let Some((pred_fn_def_id
, pred_arg
, pred_arg_ty
, res
)) =
173 is_call_with_ref_arg(cx
, mir
, &pred_terminator
.kind
);
175 if cx
.tcx
.is_diagnostic_item(sym
::deref_method
, pred_fn_def_id
);
176 if is_type_diagnostic_item(cx
, pred_arg_ty
, sym
::PathBuf
)
177 || is_type_diagnostic_item(cx
, pred_arg_ty
, sym
::OsString
);
185 let (local
, cannot_move_out
) =
186 unwrap_or_continue
!(find_stmt_assigns_to(cx
, mir
, pred_arg
, true, ps
[0]));
187 let loc
= mir
::Location
{
189 statement_index
: mir
.basic_blocks()[bb
].statements
.len(),
192 // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
193 // at the last statement:
196 // pred_arg = &local;
197 // cloned = deref(pred_arg);
199 // StorageDead(pred_arg);
200 // res = to_path_buf(cloned);
202 if cannot_move_out
|| !possible_borrower
.only_borrowers(&[arg
, cloned
], local
, loc
) {
206 (local
, deref_clone_ret
)
209 let clone_usage
= if local
== ret_local
{
212 cloned_consume_or_mutate_loc
: None
,
213 clone_consumed_or_mutated
: true,
216 let clone_usage
= visit_clone_usage(local
, ret_local
, mir
, bb
);
217 if clone_usage
.cloned_used
&& clone_usage
.clone_consumed_or_mutated
{
218 // cloned value is used, and the clone is modified or moved
220 } else if let Some(loc
) = clone_usage
.cloned_consume_or_mutate_loc
{
221 // cloned value is mutated, and the clone is alive.
222 if possible_borrower
.local_is_alive_at(ret_local
, loc
) {
229 let span
= terminator
.source_info
.span
;
230 let scope
= terminator
.source_info
.scope
;
231 let node
= mir
.source_scopes
[scope
]
234 .assert_crate_local()
238 if let Some(snip
) = snippet_opt(cx
, span
);
239 if let Some(dot
) = snip
.rfind('
.'
);
241 let sugg_span
= span
.with_lo(
242 span
.lo() + BytePos(u32::try_from(dot
).unwrap())
244 let mut app
= Applicability
::MaybeIncorrect
;
246 let call_snip
= &snip
[dot
+ 1..];
247 // Machine applicable when `call_snip` looks like `foobar()`
248 if let Some(call_snip
) = call_snip
.strip_suffix("()").map(str::trim
) {
249 if call_snip
.as_bytes().iter().all(|b
| b
.is_ascii_alphabetic() || *b
== b'_'
) {
250 app
= Applicability
::MachineApplicable
;
254 span_lint_hir_and_then(cx
, REDUNDANT_CLONE
, node
, sugg_span
, "redundant clone", |diag
| {
255 diag
.span_suggestion(
261 if clone_usage
.cloned_used
{
264 "cloned value is neither consumed nor mutated",
268 span
.with_hi(span
.lo() + BytePos(u32::try_from(dot
).unwrap())),
269 "this value is dropped without further use",
274 span_lint_hir(cx
, REDUNDANT_CLONE
, node
, span
, "redundant clone");
281 /// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
282 fn is_call_with_ref_arg
<'tcx
>(
283 cx
: &LateContext
<'tcx
>,
284 mir
: &'tcx mir
::Body
<'tcx
>,
285 kind
: &'tcx mir
::TerminatorKind
<'tcx
>,
286 ) -> Option
<(def_id
::DefId
, mir
::Local
, Ty
<'tcx
>, mir
::Local
)> {
288 if let mir
::TerminatorKind
::Call { func, args, destination, .. }
= kind
;
290 if let mir
::Operand
::Move(mir
::Place { local, .. }
) = &args
[0];
291 if let ty
::FnDef(def_id
, _
) = *func
.ty(mir
, cx
.tcx
).kind();
292 if let (inner_ty
, 1) = walk_ptrs_ty_depth(args
[0].ty(mir
, cx
.tcx
));
293 if !is_copy(cx
, inner_ty
);
295 Some((def_id
, *local
, inner_ty
, destination
.as_local()?
))
302 type CannotMoveOut
= bool
;
304 /// Finds the first `to = (&)from`, and returns
305 /// ``Some((from, whether `from` cannot be moved out))``.
306 fn find_stmt_assigns_to
<'tcx
>(
307 cx
: &LateContext
<'tcx
>,
308 mir
: &mir
::Body
<'tcx
>,
309 to_local
: mir
::Local
,
312 ) -> Option
<(mir
::Local
, CannotMoveOut
)> {
313 let rvalue
= mir
.basic_blocks()[bb
].statements
.iter().rev().find_map(|stmt
| {
314 if let mir
::StatementKind
::Assign(box (mir
::Place { local, .. }
, v
)) = &stmt
.kind
{
315 return if *local
== to_local { Some(v) }
else { None }
;
321 match (by_ref
, rvalue
) {
322 (true, mir
::Rvalue
::Ref(_
, _
, place
)) | (false, mir
::Rvalue
::Use(mir
::Operand
::Copy(place
))) => {
323 Some(base_local_and_movability(cx
, mir
, *place
))
325 (false, mir
::Rvalue
::Ref(_
, _
, place
)) => {
326 if let [mir
::ProjectionElem
::Deref
] = place
.as_ref().projection
{
327 Some(base_local_and_movability(cx
, mir
, *place
))
336 /// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
337 /// if it is already a `Local`.
339 /// Also reports whether given `place` cannot be moved out.
340 fn base_local_and_movability
<'tcx
>(
341 cx
: &LateContext
<'tcx
>,
342 mir
: &mir
::Body
<'tcx
>,
343 place
: mir
::Place
<'tcx
>,
344 ) -> (mir
::Local
, CannotMoveOut
) {
345 use rustc_middle
::mir
::PlaceRef
;
347 // Dereference. You cannot move things out from a borrowed value.
348 let mut deref
= false;
349 // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
350 let mut field
= false;
351 // If projection is a slice index then clone can be removed only if the
352 // underlying type implements Copy
353 let mut slice
= false;
355 let PlaceRef { local, mut projection }
= place
.as_ref();
356 while let [base @
.., elem
] = projection
{
358 deref
|= matches
!(elem
, mir
::ProjectionElem
::Deref
);
359 field
|= matches
!(elem
, mir
::ProjectionElem
::Field(..))
360 && has_drop(cx
, mir
::Place
::ty_from(local
, projection
, &mir
.local_decls
, cx
.tcx
).ty
);
361 slice
|= matches
!(elem
, mir
::ProjectionElem
::Index(..))
362 && !is_copy(cx
, mir
::Place
::ty_from(local
, projection
, &mir
.local_decls
, cx
.tcx
).ty
);
365 (local
, deref
|| field
|| slice
)
370 /// Whether the cloned value is used after the clone.
372 /// The first location where the cloned value is consumed or mutated, if any.
373 cloned_consume_or_mutate_loc
: Option
<mir
::Location
>,
374 /// Whether the clone value is mutated.
375 clone_consumed_or_mutated
: bool
,
377 fn visit_clone_usage(cloned
: mir
::Local
, clone
: mir
::Local
, mir
: &mir
::Body
<'_
>, bb
: mir
::BasicBlock
) -> CloneUsage
{
383 impl<'tcx
> mir
::visit
::Visitor
<'tcx
> for V
{
384 fn visit_basic_block_data(&mut self, block
: mir
::BasicBlock
, data
: &mir
::BasicBlockData
<'tcx
>) {
385 let statements
= &data
.statements
;
386 for (statement_index
, statement
) in statements
.iter().enumerate() {
387 self.visit_statement(statement
, mir
::Location { block, statement_index }
);
390 self.visit_terminator(
394 statement_index
: statements
.len(),
399 fn visit_place(&mut self, place
: &mir
::Place
<'tcx
>, ctx
: PlaceContext
, loc
: mir
::Location
) {
400 let local
= place
.local
;
402 if local
== self.cloned
405 PlaceContext
::MutatingUse(MutatingUseContext
::Drop
) | PlaceContext
::NonUse(_
)
408 self.result
.cloned_used
= true;
409 self.result
.cloned_consume_or_mutate_loc
= self.result
.cloned_consume_or_mutate_loc
.or_else(|| {
412 PlaceContext
::NonMutatingUse(NonMutatingUseContext
::Move
)
413 | PlaceContext
::MutatingUse(MutatingUseContext
::Borrow
)
417 } else if local
== self.clone
{
419 PlaceContext
::NonMutatingUse(NonMutatingUseContext
::Move
)
420 | PlaceContext
::MutatingUse(MutatingUseContext
::Borrow
) => {
421 self.result
.clone_consumed_or_mutated
= true;
429 let init
= CloneUsage
{
431 cloned_consume_or_mutate_loc
: None
,
432 // Consider non-temporary clones consumed.
433 // TODO: Actually check for mutation of non-temporaries.
434 clone_consumed_or_mutated
: mir
.local_kind(clone
) != mir
::LocalKind
::Temp
,
436 traversal
::ReversePostorder
::new(mir
, bb
)
438 .fold(init
, |usage
, (tbb
, tdata
)| {
440 if (usage
.cloned_used
&& usage
.clone_consumed_or_mutated
) ||
442 tdata
.terminator().successors().any(|s
| s
== bb
)
446 clone_consumed_or_mutated
: true,
456 v
.visit_basic_block_data(tbb
, tdata
);
461 /// Determines liveness of each local purely based on `StorageLive`/`Dead`.
462 #[derive(Copy, Clone)]
463 struct MaybeStorageLive
;
465 impl<'tcx
> AnalysisDomain
<'tcx
> for MaybeStorageLive
{
466 type Domain
= BitSet
<mir
::Local
>;
467 const NAME
: &'
static str = "maybe_storage_live";
469 fn bottom_value(&self, body
: &mir
::Body
<'tcx
>) -> Self::Domain
{
471 BitSet
::new_empty(body
.local_decls
.len())
474 fn initialize_start_block(&self, body
: &mir
::Body
<'tcx
>, state
: &mut Self::Domain
) {
475 for arg
in body
.args_iter() {
481 impl<'tcx
> GenKillAnalysis
<'tcx
> for MaybeStorageLive
{
482 type Idx
= mir
::Local
;
484 fn statement_effect(&self, trans
: &mut impl GenKill
<Self::Idx
>, stmt
: &mir
::Statement
<'tcx
>, _
: mir
::Location
) {
486 mir
::StatementKind
::StorageLive(l
) => trans
.gen(l
),
487 mir
::StatementKind
::StorageDead(l
) => trans
.kill(l
),
492 fn terminator_effect(
494 _trans
: &mut impl GenKill
<Self::Idx
>,
495 _terminator
: &mir
::Terminator
<'tcx
>,
500 fn call_return_effect(
502 _trans
: &mut impl GenKill
<Self::Idx
>,
503 _block
: mir
::BasicBlock
,
504 _return_places
: CallReturnPlaces
<'_
, 'tcx
>,
506 // Nothing to do when a call returns successfully
510 /// Collects the possible borrowers of each local.
511 /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
512 /// possible borrowers of `a`.
513 struct PossibleBorrowerVisitor
<'a
, 'tcx
> {
514 possible_borrower
: TransitiveRelation
,
515 body
: &'a mir
::Body
<'tcx
>,
516 cx
: &'a LateContext
<'tcx
>,
517 possible_origin
: FxHashMap
<mir
::Local
, HybridBitSet
<mir
::Local
>>,
520 impl<'a
, 'tcx
> PossibleBorrowerVisitor
<'a
, 'tcx
> {
522 cx
: &'a LateContext
<'tcx
>,
523 body
: &'a mir
::Body
<'tcx
>,
524 possible_origin
: FxHashMap
<mir
::Local
, HybridBitSet
<mir
::Local
>>,
527 possible_borrower
: TransitiveRelation
::default(),
536 cx
: &LateContext
<'tcx
>,
537 maybe_live
: ResultsCursor
<'tcx
, 'tcx
, MaybeStorageLive
>,
538 ) -> PossibleBorrowerMap
<'a
, 'tcx
> {
539 let mut map
= FxHashMap
::default();
540 for row
in (1..self.body
.local_decls
.len()).map(mir
::Local
::from_usize
) {
541 if is_copy(cx
, self.body
.local_decls
[row
].ty
) {
545 let mut borrowers
= self.possible_borrower
.reachable_from(row
, self.body
.local_decls
.len());
546 borrowers
.remove(mir
::Local
::from_usize(0));
547 if !borrowers
.is_empty() {
548 map
.insert(row
, borrowers
);
552 let bs
= BitSet
::new_empty(self.body
.local_decls
.len());
553 PossibleBorrowerMap
{
556 bitset
: (bs
.clone(), bs
),
561 impl<'a
, 'tcx
> mir
::visit
::Visitor
<'tcx
> for PossibleBorrowerVisitor
<'a
, 'tcx
> {
562 fn visit_assign(&mut self, place
: &mir
::Place
<'tcx
>, rvalue
: &mir
::Rvalue
<'_
>, _location
: mir
::Location
) {
563 let lhs
= place
.local
;
565 mir
::Rvalue
::Ref(_
, _
, borrowed
) => {
566 self.possible_borrower
.add(borrowed
.local
, lhs
);
570 .visit_ty(place
.ty(&self.body
.local_decls
, self.cx
.tcx
).ty
)
575 rvalue_locals(other
, |rhs
| {
577 self.possible_borrower
.add(rhs
, lhs
);
584 fn visit_terminator(&mut self, terminator
: &mir
::Terminator
<'_
>, _loc
: mir
::Location
) {
585 if let mir
::TerminatorKind
::Call
{
587 destination
: mir
::Place { local: dest, .. }
,
592 // If the call returns something with lifetimes,
593 // let's conservatively assume the returned value contains lifetime of all the arguments.
594 // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
596 let mut immutable_borrowers
= vec
![];
597 let mut mutable_borrowers
= vec
![];
601 mir
::Operand
::Copy(p
) | mir
::Operand
::Move(p
) => {
602 if let ty
::Ref(_
, _
, Mutability
::Mut
) = self.body
.local_decls
[p
.local
].ty
.kind() {
603 mutable_borrowers
.push(p
.local
);
605 immutable_borrowers
.push(p
.local
);
608 mir
::Operand
::Constant(..) => (),
612 let mut mutable_variables
: Vec
<mir
::Local
> = mutable_borrowers
614 .filter_map(|r
| self.possible_origin
.get(r
))
615 .flat_map(HybridBitSet
::iter
)
618 if ContainsRegion
.visit_ty(self.body
.local_decls
[*dest
].ty
).is_break() {
619 mutable_variables
.push(*dest
);
622 for y
in mutable_variables
{
623 for x
in &immutable_borrowers
{
624 self.possible_borrower
.add(*x
, y
);
626 for x
in &mutable_borrowers
{
627 self.possible_borrower
.add(*x
, y
);
634 /// Collect possible borrowed for every `&mut` local.
635 /// For example, `_1 = &mut _2` generate _1: {_2,...}
636 /// Known Problems: not sure all borrowed are tracked
637 struct PossibleOriginVisitor
<'a
, 'tcx
> {
638 possible_origin
: TransitiveRelation
,
639 body
: &'a mir
::Body
<'tcx
>,
642 impl<'a
, 'tcx
> PossibleOriginVisitor
<'a
, 'tcx
> {
643 fn new(body
: &'a mir
::Body
<'tcx
>) -> Self {
645 possible_origin
: TransitiveRelation
::default(),
650 fn into_map(self, cx
: &LateContext
<'tcx
>) -> FxHashMap
<mir
::Local
, HybridBitSet
<mir
::Local
>> {
651 let mut map
= FxHashMap
::default();
652 for row
in (1..self.body
.local_decls
.len()).map(mir
::Local
::from_usize
) {
653 if is_copy(cx
, self.body
.local_decls
[row
].ty
) {
657 let mut borrowers
= self.possible_origin
.reachable_from(row
, self.body
.local_decls
.len());
658 borrowers
.remove(mir
::Local
::from_usize(0));
659 if !borrowers
.is_empty() {
660 map
.insert(row
, borrowers
);
667 impl<'a
, 'tcx
> mir
::visit
::Visitor
<'tcx
> for PossibleOriginVisitor
<'a
, 'tcx
> {
668 fn visit_assign(&mut self, place
: &mir
::Place
<'tcx
>, rvalue
: &mir
::Rvalue
<'_
>, _location
: mir
::Location
) {
669 let lhs
= place
.local
;
671 // Only consider `&mut`, which can modify origin place
672 mir
::Rvalue
::Ref(_
, rustc_middle
::mir
::BorrowKind
::Mut { .. }
, borrowed
) |
675 mir
::Rvalue
::Use(mir
::Operand
::Move(borrowed
)) |
676 // _3 = move _2 as &mut _;
677 mir
::Rvalue
::Cast(_
, mir
::Operand
::Move(borrowed
), _
)
679 self.possible_origin
.add(lhs
, borrowed
.local
);
686 struct ContainsRegion
;
688 impl TypeVisitor
<'_
> for ContainsRegion
{
691 fn visit_region(&mut self, _
: ty
::Region
<'_
>) -> ControlFlow
<Self::BreakTy
> {
696 fn rvalue_locals(rvalue
: &mir
::Rvalue
<'_
>, mut visit
: impl FnMut(mir
::Local
)) {
697 use rustc_middle
::mir
::Rvalue
::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}
;
699 let mut visit_op
= |op
: &mir
::Operand
<'_
>| match op
{
700 mir
::Operand
::Copy(p
) | mir
::Operand
::Move(p
) => visit(p
.local
),
701 mir
::Operand
::Constant(..) => (),
705 Use(op
) | Repeat(op
, _
) | Cast(_
, op
, _
) | UnaryOp(_
, op
) => visit_op(op
),
706 Aggregate(_
, ops
) => ops
.iter().for_each(visit_op
),
707 BinaryOp(_
, box (lhs
, rhs
)) | CheckedBinaryOp(_
, box (lhs
, rhs
)) => {
715 /// Result of `PossibleBorrowerVisitor`.
716 struct PossibleBorrowerMap
<'a
, 'tcx
> {
717 /// Mapping `Local -> its possible borrowers`
718 map
: FxHashMap
<mir
::Local
, HybridBitSet
<mir
::Local
>>,
719 maybe_live
: ResultsCursor
<'a
, 'tcx
, MaybeStorageLive
>,
720 // Caches to avoid allocation of `BitSet` on every query
721 bitset
: (BitSet
<mir
::Local
>, BitSet
<mir
::Local
>),
724 impl PossibleBorrowerMap
<'_
, '_
> {
725 /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
726 fn only_borrowers(&mut self, borrowers
: &[mir
::Local
], borrowed
: mir
::Local
, at
: mir
::Location
) -> bool
{
727 self.maybe_live
.seek_after_primary_effect(at
);
729 self.bitset
.0.clear();
730 let maybe_live
= &mut self.maybe_live
;
731 if let Some(bitset
) = self.map
.get(&borrowed
) {
732 for b
in bitset
.iter().filter(move |b
| maybe_live
.contains(*b
)) {
733 self.bitset
.0.insert
(b
);
739 self.bitset
.1.clear();
741 self.bitset
.1.insert
(*b
);
744 self.bitset
.0 == self.bitset
.1
747 fn local_is_alive_at(&mut self, local
: mir
::Local
, at
: mir
::Location
) -> bool
{
748 self.maybe_live
.seek_after_primary_effect(at
);
749 self.maybe_live
.contains(local
)
754 struct TransitiveRelation
{
755 relations
: FxHashMap
<mir
::Local
, Vec
<mir
::Local
>>,
757 impl TransitiveRelation
{
758 fn add(&mut self, a
: mir
::Local
, b
: mir
::Local
) {
759 self.relations
.entry(a
).or_default().push(b
);
762 fn reachable_from(&self, a
: mir
::Local
, domain_size
: usize) -> HybridBitSet
<mir
::Local
> {
763 let mut seen
= HybridBitSet
::new_empty(domain_size
);
764 let mut stack
= vec
![a
];
765 while let Some(u
) = stack
.pop() {
766 if let Some(edges
) = self.relations
.get(&u
) {