@@ -6,7 +6,7 @@ use if_chain::if_chain;
66use matches:: matches;
77use rustc:: mir:: {
88 self , traversal,
9- visit:: { MutatingUseContext , PlaceContext , Visitor as _} ,
9+ visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor as _} ,
1010} ;
1111use rustc:: ty:: { self , fold:: TypeVisitor , Ty } ;
1212use rustc_data_structures:: { fx:: FxHashMap , transitive_relation:: TransitiveRelation } ;
@@ -110,7 +110,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
110110 continue ;
111111 }
112112
113- let ( fn_def_id, arg, arg_ty, _) = unwrap_or_continue ! ( is_call_with_ref_arg( cx, mir, & terminator. kind) ) ;
113+ let ( fn_def_id, arg, arg_ty, clone_ret) =
114+ unwrap_or_continue ! ( is_call_with_ref_arg( cx, mir, & terminator. kind) ) ;
114115
115116 let from_borrow = match_def_path ( cx, fn_def_id, & paths:: CLONE_TRAIT_METHOD )
116117 || match_def_path ( cx, fn_def_id, & paths:: TO_OWNED_METHOD )
@@ -132,16 +133,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
132133 statement_index : bbdata. statements . len ( ) ,
133134 } ;
134135
135- // Cloned local
136- let local = if from_borrow {
136+ // `Local` to be cloned, and a local of `clone` call's destination
137+ let ( local, ret_local ) = if from_borrow {
137138 // `res = clone(arg)` can be turned into `res = move arg;`
138139 // if `arg` is the only borrow of `cloned` at this point.
139140
140141 if cannot_move_out || !possible_borrower. only_borrowers ( & [ arg] , cloned, loc) {
141142 continue ;
142143 }
143144
144- cloned
145+ ( cloned, clone_ret )
145146 } else {
146147 // `arg` is a reference as it is `.deref()`ed in the previous block.
147148 // Look into the predecessor block and find out the source of deref.
@@ -153,15 +154,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
153154 let pred_terminator = mir[ ps[ 0 ] ] . terminator ( ) ;
154155
155156 // receiver of the `deref()` call
156- let pred_arg = if_chain ! {
157- if let Some ( ( pred_fn_def_id, pred_arg, pred_arg_ty, Some ( res) ) ) =
157+ let ( pred_arg, deref_clone_ret ) = if_chain ! {
158+ if let Some ( ( pred_fn_def_id, pred_arg, pred_arg_ty, res) ) =
158159 is_call_with_ref_arg( cx, mir, & pred_terminator. kind) ;
159- if res. local == cloned;
160+ if res == cloned;
160161 if match_def_path( cx, pred_fn_def_id, & paths:: DEREF_TRAIT_METHOD ) ;
161162 if match_type( cx, pred_arg_ty, & paths:: PATH_BUF )
162163 || match_type( cx, pred_arg_ty, & paths:: OS_STRING ) ;
163164 then {
164- pred_arg
165+ ( pred_arg, res )
165166 } else {
166167 continue ;
167168 }
@@ -188,25 +189,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
188189 continue ;
189190 }
190191
191- local
192+ ( local, deref_clone_ret )
192193 } ;
193194
194- // `local` cannot be moved out if it is used later
195- let used_later = traversal:: ReversePostorder :: new ( & mir, bb) . skip ( 1 ) . any ( |( tbb, tdata) | {
196- // Give up on loops
197- if tdata. terminator ( ) . successors ( ) . any ( |s| * s == bb) {
198- return true ;
199- }
195+ let is_temp = mir_read_only. local_kind ( ret_local) == mir:: LocalKind :: Temp ;
196+
197+ // 1. `local` can be moved out if it is not used later.
198+ // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone`
199+ // call anyway.
200+ let ( used, consumed_or_mutated) = traversal:: ReversePostorder :: new ( & mir, bb) . skip ( 1 ) . fold (
201+ ( false , !is_temp) ,
202+ |( used, consumed) , ( tbb, tdata) | {
203+ // Short-circuit
204+ if ( used && consumed) ||
205+ // Give up on loops
206+ tdata. terminator ( ) . successors ( ) . any ( |s| * s == bb)
207+ {
208+ return ( true , true ) ;
209+ }
200210
201- let mut vis = LocalUseVisitor {
202- local,
203- used_other_than_drop : false ,
204- } ;
205- vis. visit_basic_block_data ( tbb, tdata) ;
206- vis. used_other_than_drop
207- } ) ;
211+ let mut vis = LocalUseVisitor {
212+ used : ( local, false ) ,
213+ consumed_or_mutated : ( ret_local, false ) ,
214+ } ;
215+ vis. visit_basic_block_data ( tbb, tdata) ;
216+ ( used || vis. used . 1 , consumed || vis. consumed_or_mutated . 1 )
217+ } ,
218+ ) ;
208219
209- if !used_later {
220+ if !used || !consumed_or_mutated {
210221 let span = terminator. source_info . span ;
211222 let scope = terminator. source_info . scope ;
212223 let node = mir. source_scopes [ scope]
@@ -240,10 +251,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
240251 String :: new( ) ,
241252 app,
242253 ) ;
243- db. span_note(
244- span. with_hi( span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) ) ) ,
245- "this value is dropped without further use" ,
246- ) ;
254+ if used {
255+ db. span_note(
256+ span,
257+ "cloned value is neither consumed nor mutated" ,
258+ ) ;
259+ } else {
260+ db. span_note(
261+ span. with_hi( span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) ) ) ,
262+ "this value is dropped without further use" ,
263+ ) ;
264+ }
247265 } ) ;
248266 } else {
249267 span_lint_hir( cx, REDUNDANT_CLONE , node, span, "redundant clone" ) ;
@@ -259,7 +277,7 @@ fn is_call_with_ref_arg<'tcx>(
259277 cx : & LateContext < ' _ , ' tcx > ,
260278 mir : & ' tcx mir:: Body < ' tcx > ,
261279 kind : & ' tcx mir:: TerminatorKind < ' tcx > ,
262- ) -> Option < ( def_id:: DefId , mir:: Local , Ty < ' tcx > , Option < & ' tcx mir:: Place < ' tcx > > ) > {
280+ ) -> Option < ( def_id:: DefId , mir:: Local , Ty < ' tcx > , mir:: Local ) > {
263281 if_chain ! {
264282 if let mir:: TerminatorKind :: Call { func, args, destination, .. } = kind;
265283 if args. len( ) == 1 ;
@@ -268,7 +286,7 @@ fn is_call_with_ref_arg<'tcx>(
268286 if let ( inner_ty, 1 ) = walk_ptrs_ty_depth( args[ 0 ] . ty( & * mir, cx. tcx) ) ;
269287 if !is_copy( cx, inner_ty) ;
270288 then {
271- Some ( ( def_id, * local, inner_ty, destination. as_ref( ) . map( |( dest, _) | dest) ) )
289+ Some ( ( def_id, * local, inner_ty, destination. as_ref( ) . map( |( dest, _) | dest) ? . as_local ( ) ? ) )
272290 } else {
273291 None
274292 }
@@ -337,20 +355,15 @@ fn base_local_and_movability<'tcx>(
337355}
338356
339357struct LocalUseVisitor {
340- local : mir:: Local ,
341- used_other_than_drop : bool ,
358+ used : ( mir:: Local , bool ) ,
359+ consumed_or_mutated : ( mir :: Local , bool ) ,
342360}
343361
344362impl < ' tcx > mir:: visit:: Visitor < ' tcx > for LocalUseVisitor {
345363 fn visit_basic_block_data ( & mut self , block : mir:: BasicBlock , data : & mir:: BasicBlockData < ' tcx > ) {
346364 let statements = & data. statements ;
347365 for ( statement_index, statement) in statements. iter ( ) . enumerate ( ) {
348366 self . visit_statement ( statement, mir:: Location { block, statement_index } ) ;
349-
350- // Once flagged, skip remaining statements
351- if self . used_other_than_drop {
352- return ;
353- }
354367 }
355368
356369 self . visit_terminator (
@@ -362,14 +375,23 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
362375 ) ;
363376 }
364377
365- fn visit_local ( & mut self , local : & mir:: Local , ctx : PlaceContext , _: mir:: Location ) {
366- match ctx {
367- PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( _) => return ,
368- _ => { } ,
378+ fn visit_place ( & mut self , place : & mir:: Place < ' tcx > , ctx : PlaceContext , _: mir:: Location ) {
379+ let local = place. local ;
380+
381+ if local == self . used . 0
382+ && !matches ! ( ctx, PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( _) )
383+ {
384+ self . used . 1 = true ;
369385 }
370386
371- if * local == self . local {
372- self . used_other_than_drop = true ;
387+ if local == self . consumed_or_mutated . 0 {
388+ match ctx {
389+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Move )
390+ | PlaceContext :: MutatingUse ( MutatingUseContext :: Borrow ) => {
391+ self . consumed_or_mutated . 1 = true ;
392+ } ,
393+ _ => { } ,
394+ }
373395 }
374396 }
375397}
0 commit comments