@@ -1118,18 +1118,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> {
11181118 fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
11191119 // Recursively explore an expression until a ExprKind::Path is found
11201120 match & expr. kind {
1121- ExprKind :: Box ( expr) => self . visit_expr ( expr) ,
1122- ExprKind :: Array ( expr_list) => {
1123- for expr in expr_list {
1124- self . visit_expr ( expr)
1125- }
1126- } ,
1127- ExprKind :: MethodCall ( _, _, expr_list) => {
1128- for expr in expr_list {
1129- self . visit_expr ( expr)
1130- }
1131- } ,
1132- ExprKind :: Tup ( expr_list) => {
1121+ ExprKind :: Array ( expr_list) | ExprKind :: MethodCall ( _, _, expr_list) | ExprKind :: Tup ( expr_list) => {
11331122 for expr in expr_list {
11341123 self . visit_expr ( expr)
11351124 }
@@ -1138,11 +1127,12 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> {
11381127 self . visit_expr ( lhs_expr) ;
11391128 self . visit_expr ( rhs_expr) ;
11401129 } ,
1141- ExprKind :: Unary ( _, expr) => self . visit_expr ( expr) ,
1142- ExprKind :: Cast ( expr, _) => self . visit_expr ( expr) ,
1143- ExprKind :: Type ( expr, _) => self . visit_expr ( expr) ,
1144- ExprKind :: AddrOf ( _, expr) => self . visit_expr ( expr) ,
1145- ExprKind :: Struct ( _, _, Some ( expr) ) => self . visit_expr ( expr) ,
1130+ ExprKind :: Box ( expr)
1131+ | ExprKind :: Unary ( _, expr)
1132+ | ExprKind :: Cast ( expr, _)
1133+ | ExprKind :: Type ( expr, _)
1134+ | ExprKind :: AddrOf ( _, expr)
1135+ | ExprKind :: Struct ( _, _, Some ( expr) ) => self . visit_expr ( expr) ,
11461136 _ => {
11471137 // Exploration cannot continue ... calculate the hir_id of the current
11481138 // expr assuming it is a Path
@@ -1173,6 +1163,55 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> {
11731163 }
11741164}
11751165
1166+ // Scans the body of the for loop and determines whether lint should be given
1167+ struct SameItemPushVisitor < ' a , ' tcx > {
1168+ should_lint : bool ,
1169+ // this field holds the last vec push operation visited, which should be the only push seen
1170+ vec_push : Option < ( & ' tcx Expr , & ' tcx Expr ) > ,
1171+ cx : & ' a LateContext < ' a , ' tcx > ,
1172+ }
1173+
1174+ impl < ' a , ' tcx > Visitor < ' tcx > for SameItemPushVisitor < ' a , ' tcx > {
1175+ fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
1176+ match & expr. kind {
1177+ // Non-determinism may occur ... don't give a lint
1178+ ExprKind :: Loop ( _, _, _) | ExprKind :: Match ( _, _, _) => self . should_lint = false ,
1179+ ExprKind :: Block ( block, _) => self . visit_block ( block) ,
1180+ _ => { } ,
1181+ }
1182+ }
1183+
1184+ fn visit_block ( & mut self , b : & ' tcx Block ) {
1185+ for stmt in b. stmts . iter ( ) {
1186+ self . visit_stmt ( stmt) ;
1187+ }
1188+ }
1189+
1190+ fn visit_stmt ( & mut self , s : & ' tcx Stmt ) {
1191+ let vec_push_option = get_vec_push ( self . cx , s) ;
1192+ if vec_push_option. is_none ( ) {
1193+ // Current statement is not a push so visit inside
1194+ match & s. kind {
1195+ rustc:: hir:: StmtKind :: Expr ( expr) | rustc:: hir:: StmtKind :: Semi ( expr) => self . visit_expr ( & expr) ,
1196+ _ => { } ,
1197+ }
1198+ } else {
1199+ // Current statement is a push ...check whether another
1200+ // push had been previously done
1201+ if self . vec_push . is_none ( ) {
1202+ self . vec_push = vec_push_option;
1203+ } else {
1204+ // There are multiple pushes ... don't lint
1205+ self . should_lint = false ;
1206+ }
1207+ }
1208+ }
1209+
1210+ fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' tcx > {
1211+ NestedVisitorMap :: None
1212+ }
1213+ }
1214+
11761215// Given some statement, determine if that statement is a push on a Vec. If it is, return
11771216// the Vec being pushed into and the item being pushed
11781217fn get_vec_push < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , stmt : & ' tcx Stmt ) -> Option < ( & ' tcx Expr , & ' tcx Expr ) > {
@@ -1201,39 +1240,34 @@ fn detect_same_item_push<'a, 'tcx>(
12011240 body : & ' tcx Expr ,
12021241 _: & ' tcx Expr ,
12031242) {
1204- // Extract for loop body
1205- if let rustc:: hir:: ExprKind :: Block ( block, _) = & body. kind {
1206- // Analyze only for loops with 1 push statement
1207- let pushes: Vec < ( & Expr , & Expr ) > = block
1208- . stmts
1209- . iter ( )
1210- // Map each statement to a Vec push (if possible)
1211- . map ( |stmt| get_vec_push ( cx, stmt) )
1212- // Filter out statements that are not pushes
1213- . filter ( |stmt_option| stmt_option. is_some ( ) )
1214- // Unwrap
1215- . map ( |stmt_option| stmt_option. unwrap ( ) )
1216- . collect ( ) ;
1217- if pushes. len ( ) == 1 {
1243+ // Determine whether it is safe to lint the body
1244+ let mut same_item_push_visitor = SameItemPushVisitor {
1245+ should_lint : true ,
1246+ vec_push : None ,
1247+ cx,
1248+ } ;
1249+ intravisit:: walk_expr ( & mut same_item_push_visitor, body) ;
1250+ if same_item_push_visitor. should_lint {
1251+ if let Some ( ( vec, pushed_item) ) = same_item_push_visitor. vec_push {
12181252 // Make sure that the push does not involve possibly mutating values
1219- if !has_mutable_variables ( cx, pushes [ 0 ] . 1 ) {
1253+ if !has_mutable_variables ( cx, pushed_item ) {
12201254 // Walk through the expression being pushed and make sure that it
12211255 // does not contain the for loop pattern
12221256 let mut for_pat_visitor = ForPatternVisitor {
12231257 found_pattern : false ,
12241258 for_pattern_hir_id : pat. hir_id ,
12251259 cx,
12261260 } ;
1227- intravisit:: walk_expr ( & mut for_pat_visitor, pushes [ 0 ] . 1 ) ;
1261+ intravisit:: walk_expr ( & mut for_pat_visitor, pushed_item ) ;
12281262
12291263 if !for_pat_visitor. found_pattern {
1230- let vec_str = snippet ( cx, pushes [ 0 ] . 0 . span , "" ) ;
1231- let item_str = snippet ( cx, pushes [ 0 ] . 1 . span , "" ) ;
1264+ let vec_str = snippet ( cx, vec . span , "" ) ;
1265+ let item_str = snippet ( cx, pushed_item . span , "" ) ;
12321266
12331267 span_help_and_lint (
12341268 cx,
12351269 SAME_ITEM_PUSH ,
1236- pushes [ 0 ] . 0 . span ,
1270+ vec . span ,
12371271 "it looks like the same item is being pushed into this Vec" ,
12381272 & format ! (
12391273 "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})" ,
0 commit comments