@@ -29,6 +29,15 @@ use crate::utils::{
2929use if_chain:: if_chain;
3030use std:: convert:: TryFrom ;
3131
32+ macro_rules! unwrap_or_continue {
33+ ( $x: expr) => {
34+ match $x {
35+ Some ( x) => x,
36+ None => continue ,
37+ }
38+ } ;
39+ }
40+
3241/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
3342/// value that is going to be dropped without further use.
3443///
@@ -87,40 +96,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
8796 let def_id = cx. tcx . hir . body_owner_def_id ( body. id ( ) ) ;
8897 let mir = cx. tcx . optimized_mir ( def_id) ;
8998
90- // Looks for `call(x: &T)` where `T: !Copy`
91- let call = |kind : & mir:: TerminatorKind < ' tcx > | -> Option < ( def_id:: DefId , mir:: Local , ty:: Ty < ' tcx > ) > {
92- if_chain ! {
93- if let TerminatorKind :: Call { func, args, .. } = kind;
94- if args. len( ) == 1 ;
95- if let mir:: Operand :: Move ( mir:: Place :: Local ( local) ) = & args[ 0 ] ;
96- if let ty:: FnDef ( def_id, _) = func. ty( & * mir, cx. tcx) . sty;
97- if let ( inner_ty, 1 ) = walk_ptrs_ty_depth( args[ 0 ] . ty( & * mir, cx. tcx) ) ;
98- if !is_copy( cx, inner_ty) ;
99- then {
100- Some ( ( def_id, * local, inner_ty) )
101- } else {
102- None
103- }
104- }
105- } ;
106-
10799 for ( bb, bbdata) in mir. basic_blocks ( ) . iter_enumerated ( ) {
108- let terminator = if let Some ( terminator) = & bbdata. terminator {
109- terminator
110- } else {
111- continue ;
112- } ;
100+ let terminator = unwrap_or_continue ! ( & bbdata. terminator) ;
113101
114102 // Give up on loops
115103 if terminator. successors ( ) . any ( |s| * s == bb) {
116104 continue ;
117105 }
118106
119- let ( fn_def_id, arg, arg_ty) = if let Some ( t) = call ( & terminator. kind ) {
120- t
121- } else {
122- continue ;
123- } ;
107+ let ( fn_def_id, arg, arg_ty, _) = unwrap_or_continue ! ( is_call_with_ref_arg( cx, mir, & terminator. kind) ) ;
124108
125109 let from_borrow = match_def_path ( cx. tcx , fn_def_id, & paths:: CLONE_TRAIT_METHOD )
126110 || match_def_path ( cx. tcx , fn_def_id, & paths:: TO_OWNED_METHOD )
@@ -135,43 +119,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
135119 continue ;
136120 }
137121
138- // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); }
139- let cloned = if let Some ( referent) = bbdata
140- . statements
141- . iter ( )
142- . rev ( )
143- . filter_map ( |stmt| {
144- if let mir:: StatementKind :: Assign ( mir:: Place :: Local ( local) , v) = & stmt. kind {
145- if * local == arg {
146- if from_deref {
147- // `r` is already a reference.
148- if let mir:: Rvalue :: Use ( mir:: Operand :: Copy ( mir:: Place :: Local ( r) ) ) = * * v {
149- return Some ( r) ;
150- }
151- } else if let mir:: Rvalue :: Ref ( _, _, mir:: Place :: Local ( r) ) = * * v {
152- return Some ( r) ;
153- }
154- }
155- }
156-
157- None
158- } )
159- . next ( )
160- {
161- referent
162- } else {
163- continue ;
164- } ;
122+ // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
123+ // In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
124+ // block.
125+ let cloned = unwrap_or_continue ! ( find_stmt_assigns_to( arg, from_borrow, bbdata. statements. iter( ) . rev( ) ) ) ;
165126
166127 // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
167128 let referent = if from_deref {
168129 let ps = mir. predecessors_for ( bb) ;
130+ if ps. len ( ) != 1 {
131+ continue ;
132+ }
133+ let pred_terminator = unwrap_or_continue ! ( & mir[ ps[ 0 ] ] . terminator) ;
134+
169135 let pred_arg = if_chain ! {
170- if ps. len( ) == 1 ;
171- if let Some ( pred_terminator) = & mir[ ps[ 0 ] ] . terminator;
172- if let mir:: TerminatorKind :: Call { destination: Some ( ( res, _) ) , .. } = & pred_terminator. kind;
136+ if let Some ( ( pred_fn_def_id, pred_arg, pred_arg_ty, Some ( res) ) ) =
137+ is_call_with_ref_arg( cx, mir, & pred_terminator. kind) ;
173138 if * res == mir:: Place :: Local ( cloned) ;
174- if let Some ( ( pred_fn_def_id, pred_arg, pred_arg_ty) ) = call( & pred_terminator. kind) ;
175139 if match_def_path( cx. tcx, pred_fn_def_id, & paths:: DEREF_TRAIT_METHOD ) ;
176140 if match_type( cx, pred_arg_ty, & paths:: PATH_BUF )
177141 || match_type( cx, pred_arg_ty, & paths:: OS_STRING ) ;
@@ -182,27 +146,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
182146 }
183147 } ;
184148
185- if let Some ( referent) = mir[ ps[ 0 ] ]
186- . statements
187- . iter ( )
188- . rev ( )
189- . filter_map ( |stmt| {
190- if let mir:: StatementKind :: Assign ( mir:: Place :: Local ( l) , v) = & stmt. kind {
191- if * l == pred_arg {
192- if let mir:: Rvalue :: Ref ( _, _, mir:: Place :: Local ( referent) ) = * * v {
193- return Some ( referent) ;
194- }
195- }
196- }
197-
198- None
199- } )
200- . next ( )
201- {
202- referent
203- } else {
204- continue ;
205- }
149+ unwrap_or_continue ! ( find_stmt_assigns_to( pred_arg, true , mir[ ps[ 0 ] ] . statements. iter( ) . rev( ) ) )
206150 } else {
207151 cloned
208152 } ;
@@ -261,6 +205,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
261205 }
262206}
263207
208+ /// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
209+ fn is_call_with_ref_arg < ' tcx > (
210+ cx : & LateContext < ' _ , ' tcx > ,
211+ mir : & ' tcx mir:: Mir < ' tcx > ,
212+ kind : & ' tcx mir:: TerminatorKind < ' tcx > ,
213+ ) -> Option < ( def_id:: DefId , mir:: Local , ty:: Ty < ' tcx > , Option < & ' tcx mir:: Place < ' tcx > > ) > {
214+ if_chain ! {
215+ if let TerminatorKind :: Call { func, args, destination, .. } = kind;
216+ if args. len( ) == 1 ;
217+ if let mir:: Operand :: Move ( mir:: Place :: Local ( local) ) = & args[ 0 ] ;
218+ if let ty:: FnDef ( def_id, _) = func. ty( & * mir, cx. tcx) . sty;
219+ if let ( inner_ty, 1 ) = walk_ptrs_ty_depth( args[ 0 ] . ty( & * mir, cx. tcx) ) ;
220+ if !is_copy( cx, inner_ty) ;
221+ then {
222+ Some ( ( def_id, * local, inner_ty, destination. as_ref( ) . map( |( dest, _) | dest) ) )
223+ } else {
224+ None
225+ }
226+ }
227+ }
228+
229+ /// Finds the first `to = (&)from`, and returns `Some(from)`.
230+ fn find_stmt_assigns_to < ' a , ' tcx : ' a > (
231+ to : mir:: Local ,
232+ by_ref : bool ,
233+ mut stmts : impl Iterator < Item = & ' a mir:: Statement < ' tcx > > ,
234+ ) -> Option < mir:: Local > {
235+ stmts. find_map ( |stmt| {
236+ if let mir:: StatementKind :: Assign ( mir:: Place :: Local ( local) , v) = & stmt. kind {
237+ if * local == to {
238+ if by_ref {
239+ if let mir:: Rvalue :: Ref ( _, _, mir:: Place :: Local ( r) ) = * * v {
240+ return Some ( r) ;
241+ }
242+ } else if let mir:: Rvalue :: Use ( mir:: Operand :: Copy ( mir:: Place :: Local ( r) ) ) = * * v {
243+ return Some ( r) ;
244+ }
245+ }
246+ }
247+
248+ None
249+ } )
250+ }
251+
264252struct LocalUseVisitor {
265253 local : mir:: Local ,
266254 used_other_than_drop : bool ,
0 commit comments