@@ -6,7 +6,7 @@ use if_chain::if_chain;
6
6
use matches:: matches;
7
7
use rustc:: mir:: {
8
8
self , traversal,
9
- visit:: { MutatingUseContext , PlaceContext , Visitor as _} ,
9
+ visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor as _} ,
10
10
} ;
11
11
use rustc:: ty:: { self , fold:: TypeVisitor , Ty } ;
12
12
use rustc_data_structures:: { fx:: FxHashMap , transitive_relation:: TransitiveRelation } ;
@@ -110,7 +110,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
110
110
continue ;
111
111
}
112
112
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) ) ;
114
115
115
116
let from_borrow = match_def_path ( cx, fn_def_id, & paths:: CLONE_TRAIT_METHOD )
116
117
|| match_def_path ( cx, fn_def_id, & paths:: TO_OWNED_METHOD )
@@ -132,16 +133,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
132
133
statement_index : bbdata. statements . len ( ) ,
133
134
} ;
134
135
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 {
137
138
// `res = clone(arg)` can be turned into `res = move arg;`
138
139
// if `arg` is the only borrow of `cloned` at this point.
139
140
140
141
if cannot_move_out || !possible_borrower. only_borrowers ( & [ arg] , cloned, loc) {
141
142
continue ;
142
143
}
143
144
144
- cloned
145
+ ( cloned, clone_ret )
145
146
} else {
146
147
// `arg` is a reference as it is `.deref()`ed in the previous block.
147
148
// Look into the predecessor block and find out the source of deref.
@@ -153,15 +154,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
153
154
let pred_terminator = mir[ ps[ 0 ] ] . terminator ( ) ;
154
155
155
156
// 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) ) =
158
159
is_call_with_ref_arg( cx, mir, & pred_terminator. kind) ;
159
- if res. local == cloned;
160
+ if res == cloned;
160
161
if match_def_path( cx, pred_fn_def_id, & paths:: DEREF_TRAIT_METHOD ) ;
161
162
if match_type( cx, pred_arg_ty, & paths:: PATH_BUF )
162
163
|| match_type( cx, pred_arg_ty, & paths:: OS_STRING ) ;
163
164
then {
164
- pred_arg
165
+ ( pred_arg, res )
165
166
} else {
166
167
continue ;
167
168
}
@@ -188,25 +189,32 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
188
189
continue ;
189
190
}
190
191
191
- local
192
+ ( local, deref_clone_ret )
192
193
} ;
193
194
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
+ // 1. `local` cannot be moved out if it is used later.
196
+ // 2. If `ret_local` is not consumed, we can remove this `clone` call anyway.
197
+ let ( used, consumed) = traversal:: ReversePostorder :: new ( & mir, bb) . skip ( 1 ) . fold (
198
+ ( false , false ) ,
199
+ |( used, consumed) , ( tbb, tdata) | {
200
+ // Short-circuit
201
+ if ( used && consumed) ||
202
+ // Give up on loops
203
+ tdata. terminator ( ) . successors ( ) . any ( |s| * s == bb)
204
+ {
205
+ return ( true , true ) ;
206
+ }
200
207
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
- } ) ;
208
+ let mut vis = LocalUseVisitor {
209
+ used : ( local, false ) ,
210
+ consumed : ( ret_local, false ) ,
211
+ } ;
212
+ vis. visit_basic_block_data ( tbb, tdata) ;
213
+ ( used || vis. used . 1 , consumed || vis. consumed . 1 )
214
+ } ,
215
+ ) ;
208
216
209
- if !used_later {
217
+ if !used || !consumed {
210
218
let span = terminator. source_info . span ;
211
219
let scope = terminator. source_info . scope ;
212
220
let node = mir. source_scopes [ scope]
@@ -240,10 +248,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
240
248
String :: new( ) ,
241
249
app,
242
250
) ;
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
- ) ;
251
+ if used {
252
+ db. span_note(
253
+ span,
254
+ "cloned value is not consumed" ,
255
+ ) ;
256
+ } else {
257
+ db. span_note(
258
+ span. with_hi( span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) ) ) ,
259
+ "this value is dropped without further use" ,
260
+ ) ;
261
+ }
247
262
} ) ;
248
263
} else {
249
264
span_lint_hir( cx, REDUNDANT_CLONE , node, span, "redundant clone" ) ;
@@ -259,7 +274,7 @@ fn is_call_with_ref_arg<'tcx>(
259
274
cx : & LateContext < ' _ , ' tcx > ,
260
275
mir : & ' tcx mir:: Body < ' tcx > ,
261
276
kind : & ' tcx mir:: TerminatorKind < ' tcx > ,
262
- ) -> Option < ( def_id:: DefId , mir:: Local , Ty < ' tcx > , Option < & ' tcx mir:: Place < ' tcx > > ) > {
277
+ ) -> Option < ( def_id:: DefId , mir:: Local , Ty < ' tcx > , mir:: Local ) > {
263
278
if_chain ! {
264
279
if let mir:: TerminatorKind :: Call { func, args, destination, .. } = kind;
265
280
if args. len( ) == 1 ;
@@ -268,7 +283,7 @@ fn is_call_with_ref_arg<'tcx>(
268
283
if let ( inner_ty, 1 ) = walk_ptrs_ty_depth( args[ 0 ] . ty( & * mir, cx. tcx) ) ;
269
284
if !is_copy( cx, inner_ty) ;
270
285
then {
271
- Some ( ( def_id, * local, inner_ty, destination. as_ref( ) . map( |( dest, _) | dest) ) )
286
+ Some ( ( def_id, * local, inner_ty, destination. as_ref( ) . map( |( dest, _) | dest) ? . as_local ( ) ? ) )
272
287
} else {
273
288
None
274
289
}
@@ -337,20 +352,15 @@ fn base_local_and_movability<'tcx>(
337
352
}
338
353
339
354
struct LocalUseVisitor {
340
- local : mir:: Local ,
341
- used_other_than_drop : bool ,
355
+ used : ( mir:: Local , bool ) ,
356
+ consumed : ( mir :: Local , bool ) ,
342
357
}
343
358
344
359
impl < ' tcx > mir:: visit:: Visitor < ' tcx > for LocalUseVisitor {
345
360
fn visit_basic_block_data ( & mut self , block : mir:: BasicBlock , data : & mir:: BasicBlockData < ' tcx > ) {
346
361
let statements = & data. statements ;
347
362
for ( statement_index, statement) in statements. iter ( ) . enumerate ( ) {
348
363
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
- }
354
364
}
355
365
356
366
self . visit_terminator (
@@ -363,13 +373,14 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
363
373
}
364
374
365
375
fn visit_local ( & mut self , local : & mir:: Local , ctx : PlaceContext , _: mir:: Location ) {
366
- match ctx {
367
- PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( _) => return ,
368
- _ => { } ,
376
+ if * local == self . used . 0
377
+ && !matches ! ( ctx, PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( _) )
378
+ {
379
+ self . used . 1 = true ;
369
380
}
370
381
371
- if * local == self . local {
372
- self . used_other_than_drop = true ;
382
+ if * local == self . consumed . 0 && matches ! ( ctx , PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Move ) ) {
383
+ self . consumed . 1 = true ;
373
384
}
374
385
}
375
386
}
0 commit comments