@@ -29,6 +29,15 @@ use crate::utils::{
29
29
use if_chain:: if_chain;
30
30
use std:: convert:: TryFrom ;
31
31
32
+ macro_rules! unwrap_or_continue {
33
+ ( $x: expr) => {
34
+ match $x {
35
+ Some ( x) => x,
36
+ None => continue ,
37
+ }
38
+ } ;
39
+ }
40
+
32
41
/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
33
42
/// value that is going to be dropped without further use.
34
43
///
@@ -87,40 +96,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
87
96
let def_id = cx. tcx . hir . body_owner_def_id ( body. id ( ) ) ;
88
97
let mir = cx. tcx . optimized_mir ( def_id) ;
89
98
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
-
107
99
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) ;
113
101
114
102
// Give up on loops
115
103
if terminator. successors ( ) . any ( |s| * s == bb) {
116
104
continue ;
117
105
}
118
106
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) ) ;
124
108
125
109
let from_borrow = match_def_path ( cx. tcx , fn_def_id, & paths:: CLONE_TRAIT_METHOD )
126
110
|| match_def_path ( cx. tcx , fn_def_id, & paths:: TO_OWNED_METHOD )
@@ -135,43 +119,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
135
119
continue ;
136
120
}
137
121
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( ) ) ) ;
165
126
166
127
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
167
128
let referent = if from_deref {
168
129
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
+
169
135
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) ;
173
138
if * res == mir:: Place :: Local ( cloned) ;
174
- if let Some ( ( pred_fn_def_id, pred_arg, pred_arg_ty) ) = call( & pred_terminator. kind) ;
175
139
if match_def_path( cx. tcx, pred_fn_def_id, & paths:: DEREF_TRAIT_METHOD ) ;
176
140
if match_type( cx, pred_arg_ty, & paths:: PATH_BUF )
177
141
|| match_type( cx, pred_arg_ty, & paths:: OS_STRING ) ;
@@ -182,27 +146,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
182
146
}
183
147
} ;
184
148
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( ) ) )
206
150
} else {
207
151
cloned
208
152
} ;
@@ -261,6 +205,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
261
205
}
262
206
}
263
207
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
+
264
252
struct LocalUseVisitor {
265
253
local : mir:: Local ,
266
254
used_other_than_drop : bool ,
0 commit comments