@@ -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 ) ]
370391struct 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`.
423447struct 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`.
562587struct 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
569595impl 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