Skip to content

Commit 2eac00c

Browse files
[perflint] fix invalid hoist in perf401 (#14369)
Co-authored-by: Micha Reiser <[email protected]>
1 parent 033ecf5 commit 2eac00c

File tree

6 files changed

+863
-203
lines changed

6 files changed

+863
-203
lines changed

crates/ruff_linter/resources/test/fixtures/perflint/PERF401.py

+122-10
Original file line numberDiff line numberDiff line change
@@ -74,49 +74,57 @@ def f():
7474
result.append(i) # Ok
7575

7676

77-
def f():
77+
async def f():
7878
items = [1, 2, 3, 4]
7979
result = []
8080
async for i in items:
8181
if i % 2:
8282
result.append(i) # PERF401
8383

8484

85-
def f():
85+
async def f():
8686
items = [1, 2, 3, 4]
8787
result = []
8888
async for i in items:
8989
result.append(i) # PERF401
9090

9191

92+
async def f():
93+
items = [1, 2, 3, 4]
94+
result = [1, 2]
95+
async for i in items:
96+
result.append(i) # PERF401
97+
98+
9299
def f():
93-
result, _ = [1,2,3,4], ...
100+
result, _ = [1, 2, 3, 4], ...
94101
for i in range(10):
95-
result.append(i*2) # PERF401
102+
result.append(i * 2) # PERF401
96103

97104

98105
def f():
99106
result = []
100107
if True:
101108
for i in range(10): # single-line comment 1 should be protected
102109
# single-line comment 2 should be protected
103-
if i % 2: # single-line comment 3 should be protected
104-
result.append(i) # PERF401
110+
if i % 2: # single-line comment 3 should be protected
111+
result.append(i) # PERF401
105112

106113

107114
def f():
108-
result = [] # comment after assignment should be protected
115+
result = [] # comment after assignment should be protected
109116
for i in range(10): # single-line comment 1 should be protected
110117
# single-line comment 2 should be protected
111-
if i % 2: # single-line comment 3 should be protected
112-
result.append(i) # PERF401
118+
if i % 2: # single-line comment 3 should be protected
119+
result.append(i) # PERF401
113120

114121

115122
def f():
116123
result = []
117124
for i in range(10):
118125
"""block comment stops the fix"""
119-
result.append(i*2) # Ok
126+
result.append(i * 2) # Ok
127+
120128

121129
def f(param):
122130
# PERF401
@@ -125,3 +133,107 @@ def f(param):
125133
new_layers = []
126134
for value in param:
127135
new_layers.append(value * 3)
136+
137+
138+
def f():
139+
result = []
140+
var = 1
141+
for _ in range(10):
142+
result.append(var + 1) # PERF401
143+
144+
145+
def f():
146+
# make sure that `tmp` is not deleted
147+
tmp = 1; result = [] # commment should be protected
148+
for i in range(10):
149+
result.append(i + 1) # PERF401
150+
151+
152+
def f():
153+
# make sure that `tmp` is not deleted
154+
result = []; tmp = 1 # commment should be protected
155+
for i in range(10):
156+
result.append(i + 1) # PERF401
157+
158+
159+
def f():
160+
result = [] # comment should be protected
161+
for i in range(10):
162+
result.append(i * 2) # PERF401
163+
164+
165+
def f():
166+
result = []
167+
result.append(1)
168+
for i in range(10):
169+
result.append(i * 2) # PERF401
170+
171+
172+
def f():
173+
result = []
174+
result += [1]
175+
for i in range(10):
176+
result.append(i * 2) # PERF401
177+
178+
179+
def f():
180+
result = []
181+
for val in range(5):
182+
result.append(val * 2) # Ok
183+
print(val)
184+
185+
186+
def f():
187+
result = []
188+
for val in range(5):
189+
result.append(val * 2) # PERF401
190+
val = 1
191+
print(val)
192+
193+
194+
def f():
195+
i = [1, 2, 3]
196+
result = []
197+
for i in i:
198+
result.append(i + 1) # PERF401
199+
200+
201+
def f():
202+
result = []
203+
for i in range( # Comment 1 should not be duplicated
204+
(
205+
2 # Comment 2
206+
+ 1
207+
)
208+
): # Comment 3
209+
if i % 2: # Comment 4
210+
result.append(
211+
(
212+
i + 1,
213+
# Comment 5
214+
2,
215+
)
216+
) # PERF401
217+
218+
219+
def f():
220+
result: list[int] = []
221+
for i in range(10):
222+
result.append(i * 2) # PERF401
223+
224+
225+
def f():
226+
a, b = [1, 2, 3], [4, 5, 6]
227+
result = []
228+
for i in a, b:
229+
result.append(i[0] + i[1]) # PERF401
230+
return result
231+
232+
233+
def f():
234+
values = [1, 2, 3]
235+
result = []
236+
for a in values:
237+
print(a)
238+
for a in values:
239+
result.append(a + 1) # PERF401

crates/ruff_linter/src/checkers/ast/analyze/deferred_for_loops.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub(crate) fn deferred_for_loops(checker: &mut Checker) {
1414
let Stmt::For(stmt_for) = checker.semantic.current_statement() else {
1515
unreachable!("Expected Stmt::For");
1616
};
17-
1817
if checker.enabled(Rule::UnusedLoopControlVariable) {
1918
flake8_bugbear::rules::unused_loop_control_variable(checker, stmt_for);
2019
}
@@ -36,6 +35,9 @@ pub(crate) fn deferred_for_loops(checker: &mut Checker) {
3635
if checker.enabled(Rule::DictIndexMissingItems) {
3736
pylint::rules::dict_index_missing_items(checker, stmt_for);
3837
}
38+
if checker.enabled(Rule::ManualListComprehension) {
39+
perflint::rules::manual_list_comprehension(checker, stmt_for);
40+
}
3941
}
4042
}
4143
}

crates/ruff_linter/src/checkers/ast/analyze/statement.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
13661366
Rule::UnnecessaryEnumerate,
13671367
Rule::UnusedLoopControlVariable,
13681368
Rule::YieldInForLoop,
1369+
Rule::ManualListComprehension,
13691370
]) {
13701371
checker.analyze.for_loops.push(checker.semantic.snapshot());
13711372
}
@@ -1390,9 +1391,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
13901391
if checker.enabled(Rule::DictIterMissingItems) {
13911392
pylint::rules::dict_iter_missing_items(checker, target, iter);
13921393
}
1393-
if checker.enabled(Rule::ManualListComprehension) {
1394-
perflint::rules::manual_list_comprehension(checker, for_stmt);
1395-
}
13961394
if checker.enabled(Rule::ManualListCopy) {
13971395
perflint::rules::manual_list_copy(checker, for_stmt);
13981396
}

0 commit comments

Comments
 (0)