@@ -1118,18 +1118,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> {
1118
1118
fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
1119
1119
// Recursively explore an expression until a ExprKind::Path is found
1120
1120
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) => {
1133
1122
for expr in expr_list {
1134
1123
self . visit_expr ( expr)
1135
1124
}
@@ -1138,11 +1127,12 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> {
1138
1127
self . visit_expr ( lhs_expr) ;
1139
1128
self . visit_expr ( rhs_expr) ;
1140
1129
} ,
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) ,
1146
1136
_ => {
1147
1137
// Exploration cannot continue ... calculate the hir_id of the current
1148
1138
// expr assuming it is a Path
@@ -1173,6 +1163,55 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> {
1173
1163
}
1174
1164
}
1175
1165
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
+
1176
1215
// Given some statement, determine if that statement is a push on a Vec. If it is, return
1177
1216
// the Vec being pushed into and the item being pushed
1178
1217
fn 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>(
1201
1240
body : & ' tcx Expr ,
1202
1241
_: & ' tcx Expr ,
1203
1242
) {
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 {
1218
1252
// 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 ) {
1220
1254
// Walk through the expression being pushed and make sure that it
1221
1255
// does not contain the for loop pattern
1222
1256
let mut for_pat_visitor = ForPatternVisitor {
1223
1257
found_pattern : false ,
1224
1258
for_pattern_hir_id : pat. hir_id ,
1225
1259
cx,
1226
1260
} ;
1227
- intravisit:: walk_expr ( & mut for_pat_visitor, pushes [ 0 ] . 1 ) ;
1261
+ intravisit:: walk_expr ( & mut for_pat_visitor, pushed_item ) ;
1228
1262
1229
1263
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 , "" ) ;
1232
1266
1233
1267
span_help_and_lint (
1234
1268
cx,
1235
1269
SAME_ITEM_PUSH ,
1236
- pushes [ 0 ] . 0 . span ,
1270
+ vec . span ,
1237
1271
"it looks like the same item is being pushed into this Vec" ,
1238
1272
& format ! (
1239
1273
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})" ,
0 commit comments