@@ -199,79 +199,72 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
199199 ( local, deref_clone_ret)
200200 } ;
201201
202- let is_temp = mir. local_kind ( ret_local) == mir:: LocalKind :: Temp ;
203-
204- // 1. `local` can be moved out if it is not used later.
205- // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone`
206- // call anyway.
207- let ( used, consumed_or_mutated) = traversal:: ReversePostorder :: new ( & mir, bb) . skip ( 1 ) . fold (
208- ( false , !is_temp) ,
209- |( used, consumed) , ( tbb, tdata) | {
210- // Short-circuit
211- if ( used && consumed) ||
212- // Give up on loops
213- tdata. terminator ( ) . successors ( ) . any ( |s| * s == bb)
214- {
215- return ( true , true ) ;
202+ let clone_usage = if local == ret_local {
203+ CloneUsage {
204+ cloned_used : false ,
205+ cloned_consume_or_mutate_loc : None ,
206+ clone_consumed_or_mutated : true ,
207+ }
208+ } else {
209+ let clone_usage = visit_clone_usage ( local, ret_local, & mir, bb) ;
210+ if clone_usage. cloned_used && clone_usage. clone_consumed_or_mutated {
211+ // cloned value is used, and the clone is modified or moved
212+ continue ;
213+ } else if let Some ( loc) = clone_usage. cloned_consume_or_mutate_loc {
214+ // cloned value is mutated, and the clone is alive.
215+ if possible_borrower. is_alive_at ( ret_local, loc) {
216+ continue ;
216217 }
218+ }
219+ clone_usage
220+ } ;
217221
218- let mut vis = LocalUseVisitor {
219- used : ( local, false ) ,
220- consumed_or_mutated : ( ret_local, false ) ,
221- } ;
222- vis. visit_basic_block_data ( tbb, tdata) ;
223- ( used || vis. used . 1 , consumed || vis. consumed_or_mutated . 1 )
224- } ,
225- ) ;
226-
227- if !used || !consumed_or_mutated {
228- let span = terminator. source_info . span ;
229- let scope = terminator. source_info . scope ;
230- let node = mir. source_scopes [ scope]
231- . local_data
232- . as_ref ( )
233- . assert_crate_local ( )
234- . lint_root ;
235-
236- if_chain ! {
237- if let Some ( snip) = snippet_opt( cx, span) ;
238- if let Some ( dot) = snip. rfind( '.' ) ;
239- then {
240- let sugg_span = span. with_lo(
241- span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) )
242- ) ;
243- let mut app = Applicability :: MaybeIncorrect ;
244-
245- let call_snip = & snip[ dot + 1 ..] ;
246- // Machine applicable when `call_snip` looks like `foobar()`
247- if let Some ( call_snip) = call_snip. strip_suffix( "()" ) . map( str :: trim) {
248- if call_snip. as_bytes( ) . iter( ) . all( |b| b. is_ascii_alphabetic( ) || * b == b'_' ) {
249- app = Applicability :: MachineApplicable ;
250- }
222+ let span = terminator. source_info . span ;
223+ let scope = terminator. source_info . scope ;
224+ let node = mir. source_scopes [ scope]
225+ . local_data
226+ . as_ref ( )
227+ . assert_crate_local ( )
228+ . lint_root ;
229+
230+ if_chain ! {
231+ if let Some ( snip) = snippet_opt( cx, span) ;
232+ if let Some ( dot) = snip. rfind( '.' ) ;
233+ then {
234+ let sugg_span = span. with_lo(
235+ span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) )
236+ ) ;
237+ let mut app = Applicability :: MaybeIncorrect ;
238+
239+ let call_snip = & snip[ dot + 1 ..] ;
240+ // Machine applicable when `call_snip` looks like `foobar()`
241+ if let Some ( call_snip) = call_snip. strip_suffix( "()" ) . map( str :: trim) {
242+ if call_snip. as_bytes( ) . iter( ) . all( |b| b. is_ascii_alphabetic( ) || * b == b'_' ) {
243+ app = Applicability :: MachineApplicable ;
251244 }
245+ }
252246
253- span_lint_hir_and_then( cx, REDUNDANT_CLONE , node, sugg_span, "redundant clone" , |diag| {
254- diag. span_suggestion(
255- sugg_span,
256- "remove this" ,
257- String :: new( ) ,
258- app,
247+ span_lint_hir_and_then( cx, REDUNDANT_CLONE , node, sugg_span, "redundant clone" , |diag| {
248+ diag. span_suggestion(
249+ sugg_span,
250+ "remove this" ,
251+ String :: new( ) ,
252+ app,
253+ ) ;
254+ if clone_usage. cloned_used {
255+ diag. span_note(
256+ span,
257+ "cloned value is neither consumed nor mutated" ,
259258 ) ;
260- if used {
261- diag. span_note(
262- span,
263- "cloned value is neither consumed nor mutated" ,
264- ) ;
265- } else {
266- diag. span_note(
267- span. with_hi( span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) ) ) ,
268- "this value is dropped without further use" ,
269- ) ;
270- }
271- } ) ;
272- } else {
273- span_lint_hir( cx, REDUNDANT_CLONE , node, span, "redundant clone" ) ;
274- }
259+ } else {
260+ diag. span_note(
261+ span. with_hi( span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) ) ) ,
262+ "this value is dropped without further use" ,
263+ ) ;
264+ }
265+ } ) ;
266+ } else {
267+ span_lint_hir( cx, REDUNDANT_CLONE , node, span, "redundant clone" ) ;
275268 }
276269 }
277270 }
@@ -365,49 +358,97 @@ fn base_local_and_movability<'tcx>(
365358 ( local, deref || field || slice)
366359}
367360
368- struct LocalUseVisitor {
369- used : ( mir:: Local , bool ) ,
370- consumed_or_mutated : ( mir:: Local , bool ) ,
361+ #[ derive( Default ) ]
362+ struct CloneUsage {
363+ /// Whether the cloned value is used after the clone.
364+ cloned_used : bool ,
365+ /// The first location where the cloned value is consumed or mutated, if any.
366+ cloned_consume_or_mutate_loc : Option < mir:: Location > ,
367+ /// Whether the clone value is mutated.
368+ clone_consumed_or_mutated : bool ,
371369}
372-
373- impl < ' tcx > mir:: visit:: Visitor < ' tcx > for LocalUseVisitor {
374- fn visit_basic_block_data ( & mut self , block : mir:: BasicBlock , data : & mir:: BasicBlockData < ' tcx > ) {
375- let statements = & data. statements ;
376- for ( statement_index, statement) in statements. iter ( ) . enumerate ( ) {
377- self . visit_statement ( statement, mir:: Location { block, statement_index } ) ;
378- }
379-
380- self . visit_terminator (
381- data. terminator ( ) ,
382- mir:: Location {
383- block,
384- statement_index : statements. len ( ) ,
385- } ,
386- ) ;
370+ fn visit_clone_usage ( cloned : mir:: Local , clone : mir:: Local , mir : & mir:: Body < ' _ > , bb : mir:: BasicBlock ) -> CloneUsage {
371+ struct V {
372+ cloned : mir:: Local ,
373+ clone : mir:: Local ,
374+ result : CloneUsage ,
387375 }
376+ impl < ' tcx > mir:: visit:: Visitor < ' tcx > for V {
377+ fn visit_basic_block_data ( & mut self , block : mir:: BasicBlock , data : & mir:: BasicBlockData < ' tcx > ) {
378+ let statements = & data. statements ;
379+ for ( statement_index, statement) in statements. iter ( ) . enumerate ( ) {
380+ self . visit_statement ( statement, mir:: Location { block, statement_index } ) ;
381+ }
388382
389- fn visit_place ( & mut self , place : & mir:: Place < ' tcx > , ctx : PlaceContext , _: mir:: Location ) {
390- let local = place. local ;
391-
392- if local == self . used . 0
393- && !matches ! (
394- ctx,
395- PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( _)
396- )
397- {
398- self . used . 1 = true ;
383+ self . visit_terminator (
384+ data. terminator ( ) ,
385+ mir:: Location {
386+ block,
387+ statement_index : statements. len ( ) ,
388+ } ,
389+ ) ;
399390 }
400391
401- if local == self . consumed_or_mutated . 0 {
402- match ctx {
403- PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Move )
404- | PlaceContext :: MutatingUse ( MutatingUseContext :: Borrow ) => {
405- self . consumed_or_mutated . 1 = true ;
406- } ,
407- _ => { } ,
392+ fn visit_place ( & mut self , place : & mir:: Place < ' tcx > , ctx : PlaceContext , loc : mir:: Location ) {
393+ let local = place. local ;
394+
395+ if local == self . cloned
396+ && !matches ! (
397+ ctx,
398+ PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( _)
399+ )
400+ {
401+ self . result . cloned_used = true ;
402+ self . result . cloned_consume_or_mutate_loc = self . result . cloned_consume_or_mutate_loc . or_else ( || {
403+ matches ! (
404+ ctx,
405+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Move )
406+ | PlaceContext :: MutatingUse ( MutatingUseContext :: Borrow )
407+ )
408+ . then ( || loc)
409+ } ) ;
410+ } else if local == self . clone {
411+ match ctx {
412+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Move )
413+ | PlaceContext :: MutatingUse ( MutatingUseContext :: Borrow ) => {
414+ self . result . clone_consumed_or_mutated = true ;
415+ } ,
416+ _ => { } ,
417+ }
408418 }
409419 }
410420 }
421+
422+ let init = CloneUsage {
423+ cloned_used : false ,
424+ cloned_consume_or_mutate_loc : None ,
425+ // Consider non-temporary clones consumed.
426+ // TODO: Actually check for mutation of non-temporaries.
427+ clone_consumed_or_mutated : mir. local_kind ( clone) != mir:: LocalKind :: Temp ,
428+ } ;
429+ traversal:: ReversePostorder :: new ( & mir, bb)
430+ . skip ( 1 )
431+ . fold ( init, |usage, ( tbb, tdata) | {
432+ // Short-circuit
433+ if ( usage. cloned_used && usage. clone_consumed_or_mutated ) ||
434+ // Give up on loops
435+ tdata. terminator ( ) . successors ( ) . any ( |s| * s == bb)
436+ {
437+ return CloneUsage {
438+ cloned_used : true ,
439+ clone_consumed_or_mutated : true ,
440+ ..usage
441+ } ;
442+ }
443+
444+ let mut v = V {
445+ cloned,
446+ clone,
447+ result : usage,
448+ } ;
449+ v. visit_basic_block_data ( tbb, tdata) ;
450+ v. result
451+ } )
411452}
412453
413454/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
@@ -623,4 +664,9 @@ impl PossibleBorrowerMap<'_, '_> {
623664
624665 self . bitset . 0 == self . bitset . 1
625666 }
667+
668+ fn is_alive_at ( & mut self , local : mir:: Local , at : mir:: Location ) -> bool {
669+ self . maybe_live . seek_after_primary_effect ( at) ;
670+ self . maybe_live . contains ( local)
671+ }
626672}
0 commit comments