Skip to content

Commit 0d6a077

Browse files
committed
Add comments
1 parent 26f19f7 commit 0d6a077

File tree

1 file changed

+41
-14
lines changed

1 file changed

+41
-14
lines changed

clippy_lints/src/redundant_clone.rs

+41-14
Original file line numberDiff line numberDiff line change
@@ -127,28 +127,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
127127
continue;
128128
}
129129

130-
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
131-
// In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
132-
// block.
130+
// `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
133131
let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
134132

135133
let loc = mir::Location {
136134
block: bb,
137135
statement_index: bbdata.statements.len(),
138136
};
139137

140-
if from_borrow && (cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc)) {
141-
continue;
142-
}
138+
// Cloned local
139+
let local = if from_borrow {
140+
// `res = clone(arg)` can be turned into `res = move arg;`
141+
// if `arg` is the only borrow of `cloned` at this point.
142+
143+
if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
144+
continue;
145+
}
146+
147+
cloned
148+
} else {
149+
// `arg` is a reference as it is `.deref()`ed in the previous block.
150+
// Look into the predecessor block and find out the source of deref.
143151

144-
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
145-
let referent = if from_deref {
146152
let ps = mir.predecessors_for(bb);
147153
if ps.len() != 1 {
148154
continue;
149155
}
150156
let pred_terminator = mir[ps[0]].terminator();
151157

158+
// receiver of the `deref()` call
152159
let pred_arg = if_chain! {
153160
if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
154161
is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
@@ -169,22 +176,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
169176
block: bb,
170177
statement_index: mir.basic_blocks()[bb].statements.len(),
171178
};
179+
180+
// This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
181+
// at the last statement:
182+
//
183+
// ```
184+
// pred_arg = &local;
185+
// cloned = deref(pred_arg);
186+
// arg = &cloned;
187+
// StorageDead(pred_arg);
188+
// res = to_path_buf(cloned);
189+
// ```
172190
if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
173191
continue;
174192
}
193+
175194
local
176-
} else {
177-
cloned
178195
};
179196

197+
// `local` cannot be moved out if it is used later
180198
let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
181199
// Give up on loops
182200
if tdata.terminator().successors().any(|s| *s == bb) {
183201
return true;
184202
}
185203

186204
let mut vis = LocalUseVisitor {
187-
local: referent,
205+
local,
188206
used_other_than_drop: false,
189207
};
190208
vis.visit_basic_block_data(tbb, tdata);
@@ -207,7 +225,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
207225
span.lo() + BytePos(u32::try_from(dot).unwrap())
208226
);
209227
let mut app = Applicability::MaybeIncorrect;
228+
210229
let mut call_snip = &snip[dot + 1..];
230+
// Machine applicable when `call_snip` looks like `foobar()`
211231
if call_snip.ends_with("()") {
212232
call_snip = call_snip[..call_snip.len()-2].trim();
213233
if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
@@ -366,6 +386,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
366386
}
367387
}
368388

389+
/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
369390
#[derive(Copy, Clone)]
370391
struct MaybeStorageLive<'a, 'tcx> {
371392
body: &'a mir::Body<'tcx>,
@@ -420,6 +441,9 @@ impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
420441
const BOTTOM_VALUE: bool = false;
421442
}
422443

444+
/// Collects the possible borrowers of each local.
445+
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
446+
/// possible borrowers of `a`.
423447
struct PossibleBorrowerVisitor<'a, 'tcx> {
424448
possible_borrower: TransitiveRelation<mir::Local>,
425449
body: &'a mir::Body<'tcx>,
@@ -507,10 +531,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
507531
..
508532
} = &terminator.kind
509533
{
510-
// If the call returns something with some lifetime,
534+
// If the call returns something with lifetimes,
511535
// let's conservatively assume the returned value contains lifetime of all the arguments.
512-
let mut cr = ContainsRegion;
513-
if !cr.visit_ty(&self.body.local_decls[*dest].ty) {
536+
// For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
537+
if !ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty) {
514538
return;
515539
}
516540

@@ -559,14 +583,17 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
559583
}
560584
}
561585

586+
/// Result of `PossibleBorrowerVisitor`.
562587
struct PossibleBorrower<'a, 'tcx> {
588+
/// Mapping `Local -> its possible borrowers`
563589
map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
564590
maybe_live: DataflowResultsCursor<'a, 'tcx, MaybeStorageLive<'a, 'tcx>>,
565591
// Caches to avoid allocation of `BitSet` on every query
566592
bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
567593
}
568594

569595
impl PossibleBorrower<'_, '_> {
596+
/// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
570597
fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
571598
self.maybe_live.seek(at);
572599

0 commit comments

Comments
 (0)