@@ -12,7 +12,7 @@ use crate::rustc::hir::{def_id, Body, FnDecl};
12
12
use crate :: rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
13
13
use crate :: rustc:: mir:: {
14
14
self , traversal,
15
- visit:: { MutatingUseContext , NonUseContext , PlaceContext , Visitor } ,
15
+ visit:: { MutatingUseContext , PlaceContext , Visitor } ,
16
16
TerminatorKind ,
17
17
} ;
18
18
use crate :: rustc:: ty;
@@ -23,10 +23,11 @@ use crate::syntax::{
23
23
source_map:: { BytePos , Span } ,
24
24
} ;
25
25
use crate :: utils:: {
26
- in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node, span_lint_node_and_then ,
27
- walk_ptrs_ty_depth,
26
+ has_drop , in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node,
27
+ span_lint_node_and_then , walk_ptrs_ty_depth,
28
28
} ;
29
29
use if_chain:: if_chain;
30
+ use matches:: matches;
30
31
use std:: convert:: TryFrom ;
31
32
32
33
macro_rules! unwrap_or_continue {
@@ -126,7 +127,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
126
127
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
127
128
// In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
128
129
// block.
129
- let cloned = unwrap_or_continue ! ( find_stmt_assigns_to( arg, from_borrow, bbdata. statements. iter( ) . rev( ) ) ) ;
130
+ let ( cloned, cannot_move_out) = unwrap_or_continue ! ( find_stmt_assigns_to(
131
+ cx,
132
+ mir,
133
+ arg,
134
+ from_borrow,
135
+ bbdata. statements. iter( )
136
+ ) ) ;
137
+
138
+ if from_borrow && cannot_move_out {
139
+ continue ;
140
+ }
130
141
131
142
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
132
143
let referent = if from_deref {
@@ -150,7 +161,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
150
161
}
151
162
} ;
152
163
153
- unwrap_or_continue ! ( find_stmt_assigns_to( pred_arg, true , mir[ ps[ 0 ] ] . statements. iter( ) . rev( ) ) )
164
+ let ( local, cannot_move_out) = unwrap_or_continue ! ( find_stmt_assigns_to(
165
+ cx,
166
+ mir,
167
+ pred_arg,
168
+ true ,
169
+ mir[ ps[ 0 ] ] . statements. iter( )
170
+ ) ) ;
171
+ if cannot_move_out {
172
+ continue ;
173
+ }
174
+ local
154
175
} else {
155
176
cloned
156
177
} ;
@@ -227,27 +248,69 @@ fn is_call_with_ref_arg<'tcx>(
227
248
}
228
249
}
229
250
230
- /// Finds the first `to = (&)from`, and returns `Some(from)`.
251
+ type CannotMoveOut = bool ;
252
+
253
+ /// Finds the first `to = (&)from`, and returns
254
+ /// ``Some((from, [`true` if `from` cannot be moved out]))``.
231
255
fn find_stmt_assigns_to < ' a , ' tcx : ' a > (
256
+ cx : & LateContext < ' _ , ' tcx > ,
257
+ mir : & mir:: Mir < ' tcx > ,
232
258
to : mir:: Local ,
233
259
by_ref : bool ,
234
- mut stmts : impl Iterator < Item = & ' a mir:: Statement < ' tcx > > ,
235
- ) -> Option < mir:: Local > {
236
- stmts. find_map ( |stmt| {
237
- if let mir:: StatementKind :: Assign ( mir:: Place :: Local ( local) , v) = & stmt. kind {
238
- if * local == to {
239
- if by_ref {
240
- if let mir:: Rvalue :: Ref ( _, _, mir:: Place :: Local ( r) ) = * * v {
241
- return Some ( r) ;
242
- }
243
- } else if let mir:: Rvalue :: Use ( mir:: Operand :: Copy ( mir:: Place :: Local ( r) ) ) = * * v {
244
- return Some ( r) ;
260
+ stmts : impl DoubleEndedIterator < Item = & ' a mir:: Statement < ' tcx > > ,
261
+ ) -> Option < ( mir:: Local , CannotMoveOut ) > {
262
+ stmts
263
+ . rev ( )
264
+ . find_map ( |stmt| {
265
+ if let mir:: StatementKind :: Assign ( mir:: Place :: Local ( local) , v) = & stmt. kind {
266
+ if * local == to {
267
+ return Some ( v) ;
245
268
}
246
269
}
247
- }
248
270
249
- None
250
- } )
271
+ None
272
+ } )
273
+ . and_then ( |v| {
274
+ if by_ref {
275
+ if let mir:: Rvalue :: Ref ( _, _, ref place) = * * v {
276
+ return base_local_and_movability ( cx, mir, place) ;
277
+ }
278
+ } else if let mir:: Rvalue :: Use ( mir:: Operand :: Copy ( ref place) ) = * * v {
279
+ return base_local_and_movability ( cx, mir, place) ;
280
+ }
281
+ None
282
+ } )
283
+ }
284
+
285
+ /// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
286
+ /// if it is already a `Local`.
287
+ ///
288
+ /// Also reports whether given `place` cannot be moved out.
289
+ fn base_local_and_movability < ' tcx > (
290
+ cx : & LateContext < ' _ , ' tcx > ,
291
+ mir : & mir:: Mir < ' tcx > ,
292
+ mut place : & mir:: Place < ' tcx > ,
293
+ ) -> Option < ( mir:: Local , CannotMoveOut ) > {
294
+ use rustc:: mir:: Place :: * ;
295
+
296
+ // Dereference. You cannot move things out from a borrowed value.
297
+ let mut deref = false ;
298
+ // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
299
+ let mut field = false ;
300
+
301
+ loop {
302
+ match place {
303
+ Local ( local) => return Some ( ( * local, deref || field) ) ,
304
+ Projection ( proj) => {
305
+ place = & proj. base ;
306
+ deref = deref || matches ! ( proj. elem, mir:: ProjectionElem :: Deref ) ;
307
+ if !field && matches ! ( proj. elem, mir:: ProjectionElem :: Field ( ..) ) {
308
+ field = has_drop ( cx, place. ty ( & mir. local_decls , cx. tcx ) . to_ty ( cx. tcx ) ) ;
309
+ }
310
+ } ,
311
+ _ => return None ,
312
+ }
313
+ }
251
314
}
252
315
253
316
struct LocalUseVisitor {
@@ -279,9 +342,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
279
342
280
343
fn visit_local ( & mut self , local : & mir:: Local , ctx : PlaceContext < ' tcx > , _: mir:: Location ) {
281
344
match ctx {
282
- PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( NonUseContext :: StorageDead ) => {
283
- return ;
284
- } ,
345
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) | PlaceContext :: NonUse ( _) => return ,
285
346
_ => { } ,
286
347
}
287
348
0 commit comments