@@ -3,8 +3,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
3
3
use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
4
4
use clippy_utils:: sugg:: Sugg ;
5
5
use clippy_utils:: ty:: is_type_diagnostic_item;
6
- use clippy_utils:: { can_move_expr_to_closure, is_trait_method, path_to_local_id, CaptureKind } ;
6
+ use clippy_utils:: { can_move_expr_to_closure, is_trait_method, path_to_local , path_to_local_id, CaptureKind } ;
7
7
use if_chain:: if_chain;
8
+ use rustc_data_structures:: fx:: FxHashMap ;
8
9
use rustc_errors:: Applicability ;
9
10
use rustc_hir:: intravisit:: { walk_block, walk_expr, NestedVisitorMap , Visitor } ;
10
11
use rustc_hir:: { Block , Expr , ExprKind , HirId , HirIdSet , Local , Mutability , Node , PatKind , Stmt , StmtKind } ;
@@ -174,15 +175,23 @@ struct IterFunctionVisitor<'b, 'a> {
174
175
illegal_mutable_capture_ids : HirIdSet ,
175
176
current_mutably_captured_ids : HirIdSet ,
176
177
cx : & ' a LateContext < ' b > ,
177
- uses : Vec < IterFunction > ,
178
+ uses : Vec < Option < IterFunction > > ,
179
+ hir_id_uses_map : FxHashMap < HirId , usize > ,
180
+ current_statement_hir_id : Option < HirId > ,
178
181
seen_other : bool ,
179
182
target : HirId ,
180
183
}
181
184
impl < ' tcx > Visitor < ' tcx > for IterFunctionVisitor < ' _ , ' tcx > {
182
185
fn visit_block ( & mut self , block : & ' txc Block < ' tcx > ) {
183
- for elem in block. stmts . iter ( ) . filter_map ( get_expr_from_stmt) . chain ( block. expr ) {
184
- self . current_mutably_captured_ids = HirIdSet :: default ( ) ;
185
- self . visit_expr ( elem) ;
186
+ for ( expr, hir_id) in block
187
+ . stmts
188
+ . iter ( )
189
+ . filter_map ( get_expr_and_hir_id_from_stmt)
190
+ . chain ( block. expr . map ( |expr| ( expr, None ) ) )
191
+ {
192
+ self . current_statement_hir_id = hir_id;
193
+ self . current_mutably_captured_ids = get_captured_ids ( self . cx , self . cx . typeck_results ( ) . expr_ty ( expr) ) ;
194
+ self . visit_expr ( expr) ;
186
195
}
187
196
}
188
197
@@ -202,28 +211,53 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
202
211
. next ( )
203
212
. is_none ( )
204
213
{
214
+ if let Some ( hir_id) = self . current_statement_hir_id {
215
+ self . hir_id_uses_map . insert ( hir_id, self . uses . len ( ) ) ;
216
+ }
205
217
match & * method_name. ident . name . as_str ( ) {
206
- "into_iter" => self . uses . push ( IterFunction {
218
+ "into_iter" => self . uses . push ( Some ( IterFunction {
207
219
func : IterFunctionKind :: IntoIter ,
208
220
span : expr. span ,
209
- } ) ,
210
- "len" => self . uses . push ( IterFunction {
221
+ } ) ) ,
222
+ "len" => self . uses . push ( Some ( IterFunction {
211
223
func : IterFunctionKind :: Len ,
212
224
span : expr. span ,
213
- } ) ,
214
- "is_empty" => self . uses . push ( IterFunction {
225
+ } ) ) ,
226
+ "is_empty" => self . uses . push ( Some ( IterFunction {
215
227
func : IterFunctionKind :: IsEmpty ,
216
228
span : expr. span ,
217
- } ) ,
218
- "contains" => self . uses . push ( IterFunction {
229
+ } ) ) ,
230
+ "contains" => self . uses . push ( Some ( IterFunction {
219
231
func : IterFunctionKind :: Contains ( args[ 0 ] . span ) ,
220
232
span : expr. span ,
221
- } ) ,
222
- _ => self . seen_other = true ,
233
+ } ) ) ,
234
+ _ => {
235
+ self . seen_other = true ;
236
+ if let Some ( hir_id) = self . current_statement_hir_id {
237
+ self . hir_id_uses_map . remove ( & hir_id) ;
238
+ }
239
+ } ,
223
240
}
224
241
}
225
242
return ;
226
243
}
244
+
245
+ if let Some ( hir_id) = path_to_local ( recv) {
246
+ if let Some ( index) = self . hir_id_uses_map . remove ( & hir_id) {
247
+ if self
248
+ . illegal_mutable_capture_ids
249
+ . intersection ( & self . current_mutably_captured_ids )
250
+ . next ( )
251
+ . is_none ( )
252
+ {
253
+ if let Some ( hir_id) = self . current_statement_hir_id {
254
+ self . hir_id_uses_map . insert ( hir_id, index) ;
255
+ }
256
+ } else {
257
+ self . uses [ index] = None ;
258
+ }
259
+ }
260
+ }
227
261
}
228
262
// Check if the collection is used for anything else
229
263
if path_to_local_id ( expr, self . target ) {
@@ -239,11 +273,17 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
239
273
}
240
274
}
241
275
242
- fn get_expr_from_stmt < ' v > ( stmt : & ' v Stmt < ' v > ) -> Option < & ' v Expr < ' v > > {
276
+ fn get_expr_and_hir_id_from_stmt < ' v > ( stmt : & ' v Stmt < ' v > ) -> Option < ( & ' v Expr < ' v > , Option < HirId > ) > {
243
277
match stmt. kind {
244
- StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => Some ( expr) ,
278
+ StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => Some ( ( expr, None ) ) ,
245
279
StmtKind :: Item ( ..) => None ,
246
- StmtKind :: Local ( Local { init, .. } ) => * init,
280
+ StmtKind :: Local ( Local { init, pat, .. } ) => {
281
+ if let PatKind :: Binding ( _, hir_id, ..) = pat. kind {
282
+ init. map ( |init_expr| ( init_expr, Some ( hir_id) ) )
283
+ } else {
284
+ init. map ( |init_expr| ( init_expr, None ) )
285
+ }
286
+ } ,
247
287
}
248
288
}
249
289
@@ -284,9 +324,15 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
284
324
cx,
285
325
current_mutably_captured_ids : HirIdSet :: default ( ) ,
286
326
illegal_mutable_capture_ids : captured_ids,
327
+ hir_id_uses_map : FxHashMap :: default ( ) ,
328
+ current_statement_hir_id : None ,
287
329
} ;
288
330
visitor. visit_block ( block) ;
289
- if visitor. seen_other { None } else { Some ( visitor. uses ) }
331
+ if visitor. seen_other {
332
+ None
333
+ } else {
334
+ Some ( visitor. uses . into_iter ( ) . flatten ( ) . collect ( ) )
335
+ }
290
336
}
291
337
292
338
#[ allow( rustc:: usage_of_ty_tykind) ]
0 commit comments