@@ -7,25 +7,25 @@ mod for_single_element_loop;
7
7
mod infinite_loop;
8
8
mod manual_flatten;
9
9
mod needless_collect;
10
+ mod same_item_push;
10
11
mod utils;
11
12
12
13
use crate :: utils:: sugg:: Sugg ;
13
14
use crate :: utils:: usage:: mutated_variables;
14
15
use crate :: utils:: {
15
16
get_enclosing_block, get_parent_expr, get_trait_def_id, higher, implements_trait, is_in_panic_handler,
16
17
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
17
- path_to_local, path_to_local_id, paths, snippet, snippet_with_applicability, snippet_with_macro_callsite ,
18
- span_lint , span_lint_and_help , span_lint_and_sugg, sugg,
18
+ path_to_local, path_to_local_id, paths, snippet, snippet_with_applicability, span_lint , span_lint_and_help ,
19
+ span_lint_and_sugg, sugg,
19
20
} ;
20
21
use if_chain:: if_chain;
21
22
use rustc_ast:: ast;
22
23
use rustc_data_structures:: fx:: FxHashMap ;
23
24
use rustc_errors:: Applicability ;
24
- use rustc_hir:: def:: { DefKind , Res } ;
25
25
use rustc_hir:: intravisit:: { walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap , Visitor } ;
26
26
use rustc_hir:: {
27
- BinOpKind , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , HirId , InlineAsmOperand , LoopSource , MatchSource ,
28
- Mutability , Node , Pat , PatKind , Stmt , StmtKind ,
27
+ BinOpKind , Block , BorrowKind , Expr , ExprKind , HirId , InlineAsmOperand , LoopSource , MatchSource , Mutability , Node ,
28
+ Pat , PatKind , Stmt , StmtKind ,
29
29
} ;
30
30
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
31
31
use rustc_middle:: hir:: map:: Map ;
@@ -866,7 +866,7 @@ fn check_for_loop<'tcx>(
866
866
for_loop_over_map_kv:: check_for_loop_over_map_kv ( cx, pat, arg, body, expr) ;
867
867
for_mut_range_bound:: check_for_mut_range_bound ( cx, arg, body) ;
868
868
for_single_element_loop:: check_for_single_element_loop ( cx, pat, arg, body, expr) ;
869
- detect_same_item_push ( cx, pat, arg, body, expr) ;
869
+ same_item_push :: detect_same_item_push ( cx, pat, arg, body, expr) ;
870
870
manual_flatten:: check_manual_flatten ( cx, pat, arg, body, span) ;
871
871
}
872
872
@@ -1307,165 +1307,6 @@ fn detect_manual_memcpy<'tcx>(
1307
1307
false
1308
1308
}
1309
1309
1310
- // Scans the body of the for loop and determines whether lint should be given
1311
- struct SameItemPushVisitor < ' a , ' tcx > {
1312
- should_lint : bool ,
1313
- // this field holds the last vec push operation visited, which should be the only push seen
1314
- vec_push : Option < ( & ' tcx Expr < ' tcx > , & ' tcx Expr < ' tcx > ) > ,
1315
- cx : & ' a LateContext < ' tcx > ,
1316
- }
1317
-
1318
- impl < ' a , ' tcx > Visitor < ' tcx > for SameItemPushVisitor < ' a , ' tcx > {
1319
- type Map = Map < ' tcx > ;
1320
-
1321
- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' _ > ) {
1322
- match & expr. kind {
1323
- // Non-determinism may occur ... don't give a lint
1324
- ExprKind :: Loop ( ..) | ExprKind :: Match ( ..) => self . should_lint = false ,
1325
- ExprKind :: Block ( block, _) => self . visit_block ( block) ,
1326
- _ => { } ,
1327
- }
1328
- }
1329
-
1330
- fn visit_block ( & mut self , b : & ' tcx Block < ' _ > ) {
1331
- for stmt in b. stmts . iter ( ) {
1332
- self . visit_stmt ( stmt) ;
1333
- }
1334
- }
1335
-
1336
- fn visit_stmt ( & mut self , s : & ' tcx Stmt < ' _ > ) {
1337
- let vec_push_option = get_vec_push ( self . cx , s) ;
1338
- if vec_push_option. is_none ( ) {
1339
- // Current statement is not a push so visit inside
1340
- match & s. kind {
1341
- StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => self . visit_expr ( & expr) ,
1342
- _ => { } ,
1343
- }
1344
- } else {
1345
- // Current statement is a push ...check whether another
1346
- // push had been previously done
1347
- if self . vec_push . is_none ( ) {
1348
- self . vec_push = vec_push_option;
1349
- } else {
1350
- // There are multiple pushes ... don't lint
1351
- self . should_lint = false ;
1352
- }
1353
- }
1354
- }
1355
-
1356
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
1357
- NestedVisitorMap :: None
1358
- }
1359
- }
1360
-
1361
- // Given some statement, determine if that statement is a push on a Vec. If it is, return
1362
- // the Vec being pushed into and the item being pushed
1363
- fn get_vec_push < ' tcx > ( cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < ' _ > ) -> Option < ( & ' tcx Expr < ' tcx > , & ' tcx Expr < ' tcx > ) > {
1364
- if_chain ! {
1365
- // Extract method being called
1366
- if let StmtKind :: Semi ( semi_stmt) = & stmt. kind;
1367
- if let ExprKind :: MethodCall ( path, _, args, _) = & semi_stmt. kind;
1368
- // Figure out the parameters for the method call
1369
- if let Some ( self_expr) = args. get( 0 ) ;
1370
- if let Some ( pushed_item) = args. get( 1 ) ;
1371
- // Check that the method being called is push() on a Vec
1372
- if is_type_diagnostic_item( cx, cx. typeck_results( ) . expr_ty( self_expr) , sym:: vec_type) ;
1373
- if path. ident. name. as_str( ) == "push" ;
1374
- then {
1375
- return Some ( ( self_expr, pushed_item) )
1376
- }
1377
- }
1378
- None
1379
- }
1380
-
1381
- /// Detects for loop pushing the same item into a Vec
1382
- fn detect_same_item_push < ' tcx > (
1383
- cx : & LateContext < ' tcx > ,
1384
- pat : & ' tcx Pat < ' _ > ,
1385
- _: & ' tcx Expr < ' _ > ,
1386
- body : & ' tcx Expr < ' _ > ,
1387
- _: & ' tcx Expr < ' _ > ,
1388
- ) {
1389
- fn emit_lint ( cx : & LateContext < ' _ > , vec : & Expr < ' _ > , pushed_item : & Expr < ' _ > ) {
1390
- let vec_str = snippet_with_macro_callsite ( cx, vec. span , "" ) ;
1391
- let item_str = snippet_with_macro_callsite ( cx, pushed_item. span , "" ) ;
1392
-
1393
- span_lint_and_help (
1394
- cx,
1395
- SAME_ITEM_PUSH ,
1396
- vec. span ,
1397
- "it looks like the same item is being pushed into this Vec" ,
1398
- None ,
1399
- & format ! (
1400
- "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})" ,
1401
- item_str, vec_str, item_str
1402
- ) ,
1403
- )
1404
- }
1405
-
1406
- if !matches ! ( pat. kind, PatKind :: Wild ) {
1407
- return ;
1408
- }
1409
-
1410
- // Determine whether it is safe to lint the body
1411
- let mut same_item_push_visitor = SameItemPushVisitor {
1412
- should_lint : true ,
1413
- vec_push : None ,
1414
- cx,
1415
- } ;
1416
- walk_expr ( & mut same_item_push_visitor, body) ;
1417
- if same_item_push_visitor. should_lint {
1418
- if let Some ( ( vec, pushed_item) ) = same_item_push_visitor. vec_push {
1419
- let vec_ty = cx. typeck_results ( ) . expr_ty ( vec) ;
1420
- let ty = vec_ty. walk ( ) . nth ( 1 ) . unwrap ( ) . expect_ty ( ) ;
1421
- if cx
1422
- . tcx
1423
- . lang_items ( )
1424
- . clone_trait ( )
1425
- . map_or ( false , |id| implements_trait ( cx, ty, id, & [ ] ) )
1426
- {
1427
- // Make sure that the push does not involve possibly mutating values
1428
- match pushed_item. kind {
1429
- ExprKind :: Path ( ref qpath) => {
1430
- match cx. qpath_res ( qpath, pushed_item. hir_id ) {
1431
- // immutable bindings that are initialized with literal or constant
1432
- Res :: Local ( hir_id) => {
1433
- if_chain ! {
1434
- let node = cx. tcx. hir( ) . get( hir_id) ;
1435
- if let Node :: Binding ( pat) = node;
1436
- if let PatKind :: Binding ( bind_ann, ..) = pat. kind;
1437
- if !matches!( bind_ann, BindingAnnotation :: RefMut | BindingAnnotation :: Mutable ) ;
1438
- let parent_node = cx. tcx. hir( ) . get_parent_node( hir_id) ;
1439
- if let Some ( Node :: Local ( parent_let_expr) ) = cx. tcx. hir( ) . find( parent_node) ;
1440
- if let Some ( init) = parent_let_expr. init;
1441
- then {
1442
- match init. kind {
1443
- // immutable bindings that are initialized with literal
1444
- ExprKind :: Lit ( ..) => emit_lint( cx, vec, pushed_item) ,
1445
- // immutable bindings that are initialized with constant
1446
- ExprKind :: Path ( ref path) => {
1447
- if let Res :: Def ( DefKind :: Const , ..) = cx. qpath_res( path, init. hir_id) {
1448
- emit_lint( cx, vec, pushed_item) ;
1449
- }
1450
- }
1451
- _ => { } ,
1452
- }
1453
- }
1454
- }
1455
- } ,
1456
- // constant
1457
- Res :: Def ( DefKind :: Const , ..) => emit_lint ( cx, vec, pushed_item) ,
1458
- _ => { } ,
1459
- }
1460
- } ,
1461
- ExprKind :: Lit ( ..) => emit_lint ( cx, vec, pushed_item) ,
1462
- _ => { } ,
1463
- }
1464
- }
1465
- }
1466
- }
1467
- }
1468
-
1469
1310
fn is_used_inside < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , container : & ' tcx Expr < ' _ > ) -> bool {
1470
1311
let def_id = match path_to_local ( expr) {
1471
1312
Some ( id) => id,
0 commit comments