@@ -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,35 @@ 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
+ 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
+ }
200
210
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
+ ) ;
208
219
209
- if !used_later {
220
+ if !used || !consumed_or_mutated {
210
221
let span = terminator. source_info . span ;
211
222
let scope = terminator. source_info . scope ;
212
223
let node = mir. source_scopes [ scope]
@@ -240,10 +251,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
240
251
String :: new( ) ,
241
252
app,
242
253
) ;
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
+ }
247
265
} ) ;
248
266
} else {
249
267
span_lint_hir( cx, REDUNDANT_CLONE , node, span, "redundant clone" ) ;
@@ -259,7 +277,7 @@ fn is_call_with_ref_arg<'tcx>(
259
277
cx : & LateContext < ' _ , ' tcx > ,
260
278
mir : & ' tcx mir:: Body < ' tcx > ,
261
279
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 ) > {
263
281
if_chain ! {
264
282
if let mir:: TerminatorKind :: Call { func, args, destination, .. } = kind;
265
283
if args. len( ) == 1 ;
@@ -268,7 +286,7 @@ fn is_call_with_ref_arg<'tcx>(
268
286
if let ( inner_ty, 1 ) = walk_ptrs_ty_depth( args[ 0 ] . ty( & * mir, cx. tcx) ) ;
269
287
if !is_copy( cx, inner_ty) ;
270
288
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 ( ) ? ) )
272
290
} else {
273
291
None
274
292
}
@@ -337,20 +355,15 @@ fn base_local_and_movability<'tcx>(
337
355
}
338
356
339
357
struct LocalUseVisitor {
340
- local : mir:: Local ,
341
- used_other_than_drop : bool ,
358
+ used : ( mir:: Local , bool ) ,
359
+ consumed_or_mutated : ( mir :: Local , bool ) ,
342
360
}
343
361
344
362
impl < ' tcx > mir:: visit:: Visitor < ' tcx > for LocalUseVisitor {
345
363
fn visit_basic_block_data ( & mut self , block : mir:: BasicBlock , data : & mir:: BasicBlockData < ' tcx > ) {
346
364
let statements = & data. statements ;
347
365
for ( statement_index, statement) in statements. iter ( ) . enumerate ( ) {
348
366
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
367
}
355
368
356
369
self . visit_terminator (
@@ -362,14 +375,23 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
362
375
) ;
363
376
}
364
377
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 ;
369
385
}
370
386
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
+ }
373
395
}
374
396
}
375
397
}
0 commit comments