1
- use rustc_hash:: FxHashSet ;
2
1
use rustpython_parser:: ast:: { self , Comprehension , Expr , ExprContext , Ranged , Stmt } ;
3
2
4
3
use ruff_diagnostics:: { Diagnostic , Violation } ;
5
4
use ruff_macros:: { derive_message_formats, violation} ;
6
- use ruff_python_ast:: helpers:: collect_arg_names ;
5
+ use ruff_python_ast:: helpers:: includes_arg_name ;
7
6
use ruff_python_ast:: types:: Node ;
8
7
use ruff_python_ast:: visitor;
9
8
use ruff_python_ast:: visitor:: Visitor ;
@@ -58,19 +57,17 @@ impl Violation for FunctionUsesLoopVariable {
58
57
59
58
#[ derive( Default ) ]
60
59
struct LoadedNamesVisitor < ' a > {
61
- // Tuple of: name, defining expression, and defining range.
62
- loaded : Vec < ( & ' a str , & ' a Expr ) > ,
63
- // Tuple of: name, defining expression, and defining range.
64
- stored : Vec < ( & ' a str , & ' a Expr ) > ,
60
+ loaded : Vec < & ' a ast:: ExprName > ,
61
+ stored : Vec < & ' a ast:: ExprName > ,
65
62
}
66
63
67
64
/// `Visitor` to collect all used identifiers in a statement.
68
65
impl < ' a > Visitor < ' a > for LoadedNamesVisitor < ' a > {
69
66
fn visit_expr ( & mut self , expr : & ' a Expr ) {
70
67
match expr {
71
- Expr :: Name ( ast :: ExprName { id , ctx , range : _ } ) => match ctx {
72
- ExprContext :: Load => self . loaded . push ( ( id , expr ) ) ,
73
- ExprContext :: Store => self . stored . push ( ( id , expr ) ) ,
68
+ Expr :: Name ( name ) => match & name . ctx {
69
+ ExprContext :: Load => self . loaded . push ( name ) ,
70
+ ExprContext :: Store => self . stored . push ( name ) ,
74
71
ExprContext :: Del => { }
75
72
} ,
76
73
_ => visitor:: walk_expr ( self , expr) ,
@@ -80,7 +77,7 @@ impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> {
80
77
81
78
#[ derive( Default ) ]
82
79
struct SuspiciousVariablesVisitor < ' a > {
83
- names : Vec < ( & ' a str , & ' a Expr ) > ,
80
+ names : Vec < & ' a ast :: ExprName > ,
84
81
safe_functions : Vec < & ' a Expr > ,
85
82
}
86
83
@@ -95,17 +92,20 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
95
92
let mut visitor = LoadedNamesVisitor :: default ( ) ;
96
93
visitor. visit_body ( body) ;
97
94
98
- // Collect all argument names.
99
- let mut arg_names = collect_arg_names ( args) ;
100
- arg_names. extend ( visitor. stored . iter ( ) . map ( |( id, ..) | id) ) ;
101
-
102
95
// Treat any non-arguments as "suspicious".
103
- self . names . extend (
104
- visitor
105
- . loaded
106
- . into_iter ( )
107
- . filter ( |( id, ..) | !arg_names. contains ( id) ) ,
108
- ) ;
96
+ self . names
97
+ . extend ( visitor. loaded . into_iter ( ) . filter ( |loaded| {
98
+ if visitor. stored . iter ( ) . any ( |stored| stored. id == loaded. id ) {
99
+ return false ;
100
+ }
101
+
102
+ if includes_arg_name ( & loaded. id , args) {
103
+ return false ;
104
+ }
105
+
106
+ true
107
+ } ) ) ;
108
+
109
109
return ;
110
110
}
111
111
Stmt :: Return ( ast:: StmtReturn {
@@ -132,10 +132,9 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
132
132
} ) => {
133
133
match func. as_ref ( ) {
134
134
Expr :: Name ( ast:: ExprName { id, .. } ) => {
135
- let id = id. as_str ( ) ;
136
- if id == "filter" || id == "reduce" || id == "map" {
135
+ if matches ! ( id. as_str( ) , "filter" | "reduce" | "map" ) {
137
136
for arg in args {
138
- if matches ! ( arg, Expr :: Lambda ( _ ) ) {
137
+ if arg. is_lambda_expr ( ) {
139
138
self . safe_functions . push ( arg) ;
140
139
}
141
140
}
@@ -159,7 +158,7 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
159
158
160
159
for keyword in keywords {
161
160
if keyword. arg . as_ref ( ) . map_or ( false , |arg| arg == "key" )
162
- && matches ! ( keyword. value, Expr :: Lambda ( _ ) )
161
+ && keyword. value . is_lambda_expr ( )
163
162
{
164
163
self . safe_functions . push ( & keyword. value ) ;
165
164
}
@@ -175,17 +174,19 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
175
174
let mut visitor = LoadedNamesVisitor :: default ( ) ;
176
175
visitor. visit_expr ( body) ;
177
176
178
- // Collect all argument names.
179
- let mut arg_names = collect_arg_names ( args) ;
180
- arg_names. extend ( visitor. stored . iter ( ) . map ( |( id, ..) | id) ) ;
181
-
182
177
// Treat any non-arguments as "suspicious".
183
- self . names . extend (
184
- visitor
185
- . loaded
186
- . iter ( )
187
- . filter ( |( id, ..) | !arg_names. contains ( id) ) ,
188
- ) ;
178
+ self . names
179
+ . extend ( visitor. loaded . into_iter ( ) . filter ( |loaded| {
180
+ if visitor. stored . iter ( ) . any ( |stored| stored. id == loaded. id ) {
181
+ return false ;
182
+ }
183
+
184
+ if includes_arg_name ( & loaded. id , args) {
185
+ return false ;
186
+ }
187
+
188
+ true
189
+ } ) ) ;
189
190
190
191
return ;
191
192
}
@@ -198,15 +199,15 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
198
199
199
200
#[ derive( Default ) ]
200
201
struct NamesFromAssignmentsVisitor < ' a > {
201
- names : FxHashSet < & ' a str > ,
202
+ names : Vec < & ' a str > ,
202
203
}
203
204
204
205
/// `Visitor` to collect all names used in an assignment expression.
205
206
impl < ' a > Visitor < ' a > for NamesFromAssignmentsVisitor < ' a > {
206
207
fn visit_expr ( & mut self , expr : & ' a Expr ) {
207
208
match expr {
208
209
Expr :: Name ( ast:: ExprName { id, .. } ) => {
209
- self . names . insert ( id. as_str ( ) ) ;
210
+ self . names . push ( id. as_str ( ) ) ;
210
211
}
211
212
Expr :: Starred ( ast:: ExprStarred { value, .. } ) => {
212
213
self . visit_expr ( value) ;
@@ -223,7 +224,7 @@ impl<'a> Visitor<'a> for NamesFromAssignmentsVisitor<'a> {
223
224
224
225
#[ derive( Default ) ]
225
226
struct AssignedNamesVisitor < ' a > {
226
- names : FxHashSet < & ' a str > ,
227
+ names : Vec < & ' a str > ,
227
228
}
228
229
229
230
/// `Visitor` to collect all used identifiers in a statement.
@@ -257,7 +258,7 @@ impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
257
258
}
258
259
259
260
fn visit_expr ( & mut self , expr : & ' a Expr ) {
260
- if matches ! ( expr, Expr :: Lambda ( _ ) ) {
261
+ if expr. is_lambda_expr ( ) {
261
262
// Don't recurse.
262
263
return ;
263
264
}
@@ -300,15 +301,15 @@ pub(crate) fn function_uses_loop_variable<'a>(checker: &mut Checker<'a>, node: &
300
301
301
302
// If a variable was used in a function or lambda body, and assigned in the
302
303
// loop, flag it.
303
- for ( name, expr ) in suspicious_variables {
304
- if reassigned_in_loop. contains ( name) {
305
- if !checker. flake8_bugbear_seen . contains ( & expr ) {
306
- checker. flake8_bugbear_seen . push ( expr ) ;
304
+ for name in suspicious_variables {
305
+ if reassigned_in_loop. contains ( & name. id . as_str ( ) ) {
306
+ if !checker. flake8_bugbear_seen . contains ( & name ) {
307
+ checker. flake8_bugbear_seen . push ( name ) ;
307
308
checker. diagnostics . push ( Diagnostic :: new (
308
309
FunctionUsesLoopVariable {
309
- name : name. to_string ( ) ,
310
+ name : name. id . to_string ( ) ,
310
311
} ,
311
- expr . range ( ) ,
312
+ name . range ( ) ,
312
313
) ) ;
313
314
}
314
315
}
0 commit comments