Skip to content

Commit c306f85

Browse files
authored
F841: support fixing unused assignments in tuples by renaming variables (#9107)
## Summary A fairly common pattern which triggers F841 is unused variables from tuple assignments, e.g.: user, created = User.objects.get_or_create(...) ^ F841: Local variable `created` is assigned to but never used This error is currently not auto-fixable. This PR adds support for fixing the error automatically by renaming the unused variable to have a leading underscore (i.e. `_created`) **iff** the `dummy-variable-rgx` setting would match it. I considered using `renamers::Renamer` here, but because by the nature of the error there should be no references to it, that seemed like overkill. Also note that the fix might break by shadowing the new name if it is already used elsewhere in the scope. I left it as is because 1. the renamed variable matches the "unused" regex, so it should hopefully not already be used, 2. the fix is marked as unsafe so it should be reviewed manually anyways, and 3. I'm not actually sure how to check the scope for the new variable name 😅
1 parent b972455 commit c306f85

5 files changed

+128
-12
lines changed

crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,14 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
247247
Some(Fix::unsafe_edit(edit).isolate(isolation))
248248
};
249249
}
250+
} else {
251+
let name = binding.name(checker.locator());
252+
let renamed = format!("_{name}");
253+
if checker.settings.dummy_variable_rgx.is_match(&renamed) {
254+
let edit = Edit::range_replacement(renamed, binding.range());
255+
256+
return Some(Fix::unsafe_edit(edit).isolate(isolation));
257+
}
250258
}
251259
}
252260

crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_0.py.snap

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ F841_0.py:20:5: F841 [*] Local variable `foo` is assigned to but never used
5757
22 21 |
5858
23 22 | bar = (1, 2)
5959

60-
F841_0.py:21:6: F841 Local variable `a` is assigned to but never used
60+
F841_0.py:21:6: F841 [*] Local variable `a` is assigned to but never used
6161
|
6262
19 | def f():
6363
20 | foo = (1, 2)
@@ -68,7 +68,17 @@ F841_0.py:21:6: F841 Local variable `a` is assigned to but never used
6868
|
6969
= help: Remove assignment to unused variable `a`
7070

71-
F841_0.py:21:9: F841 Local variable `b` is assigned to but never used
71+
Unsafe fix
72+
18 18 |
73+
19 19 | def f():
74+
20 20 | foo = (1, 2)
75+
21 |- (a, b) = (1, 2)
76+
21 |+ (_a, b) = (1, 2)
77+
22 22 |
78+
23 23 | bar = (1, 2)
79+
24 24 | (c, d) = bar
80+
81+
F841_0.py:21:9: F841 [*] Local variable `b` is assigned to but never used
7282
|
7383
19 | def f():
7484
20 | foo = (1, 2)
@@ -79,6 +89,16 @@ F841_0.py:21:9: F841 Local variable `b` is assigned to but never used
7989
|
8090
= help: Remove assignment to unused variable `b`
8191

92+
Unsafe fix
93+
18 18 |
94+
19 19 | def f():
95+
20 20 | foo = (1, 2)
96+
21 |- (a, b) = (1, 2)
97+
21 |+ (a, _b) = (1, 2)
98+
22 22 |
99+
23 23 | bar = (1, 2)
100+
24 24 | (c, d) = bar
101+
82102
F841_0.py:26:14: F841 [*] Local variable `baz` is assigned to but never used
83103
|
84104
24 | (c, d) = bar

crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_1.py.snap

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,42 @@
11
---
22
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
33
---
4-
F841_1.py:6:5: F841 Local variable `x` is assigned to but never used
4+
F841_1.py:6:5: F841 [*] Local variable `x` is assigned to but never used
55
|
66
5 | def f():
77
6 | x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
88
| ^ F841
99
|
1010
= help: Remove assignment to unused variable `x`
1111

12-
F841_1.py:6:8: F841 Local variable `y` is assigned to but never used
12+
Unsafe fix
13+
3 3 |
14+
4 4 |
15+
5 5 | def f():
16+
6 |- x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
17+
6 |+ _x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
18+
7 7 |
19+
8 8 |
20+
9 9 | def f():
21+
22+
F841_1.py:6:8: F841 [*] Local variable `y` is assigned to but never used
1323
|
1424
5 | def f():
1525
6 | x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
1626
| ^ F841
1727
|
1828
= help: Remove assignment to unused variable `y`
1929

30+
Unsafe fix
31+
3 3 |
32+
4 4 |
33+
5 5 | def f():
34+
6 |- x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
35+
6 |+ x, _y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
36+
7 7 |
37+
8 8 |
38+
9 9 | def f():
39+
2040
F841_1.py:16:14: F841 [*] Local variable `coords` is assigned to but never used
2141
|
2242
15 | def f():
@@ -53,36 +73,64 @@ F841_1.py:20:5: F841 [*] Local variable `coords` is assigned to but never used
5373
22 22 |
5474
23 23 | def f():
5575

56-
F841_1.py:24:6: F841 Local variable `a` is assigned to but never used
76+
F841_1.py:24:6: F841 [*] Local variable `a` is assigned to but never used
5777
|
5878
23 | def f():
5979
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
6080
| ^ F841
6181
|
6282
= help: Remove assignment to unused variable `a`
6383

64-
F841_1.py:24:9: F841 Local variable `b` is assigned to but never used
84+
Unsafe fix
85+
21 21 |
86+
22 22 |
87+
23 23 | def f():
88+
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
89+
24 |+ (_a, b) = (x, y) = 1, 2 # this triggers F841 on everything
90+
91+
F841_1.py:24:9: F841 [*] Local variable `b` is assigned to but never used
6592
|
6693
23 | def f():
6794
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
6895
| ^ F841
6996
|
7097
= help: Remove assignment to unused variable `b`
7198

72-
F841_1.py:24:15: F841 Local variable `x` is assigned to but never used
99+
Unsafe fix
100+
21 21 |
101+
22 22 |
102+
23 23 | def f():
103+
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
104+
24 |+ (a, _b) = (x, y) = 1, 2 # this triggers F841 on everything
105+
106+
F841_1.py:24:15: F841 [*] Local variable `x` is assigned to but never used
73107
|
74108
23 | def f():
75109
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
76110
| ^ F841
77111
|
78112
= help: Remove assignment to unused variable `x`
79113

80-
F841_1.py:24:18: F841 Local variable `y` is assigned to but never used
114+
Unsafe fix
115+
21 21 |
116+
22 22 |
117+
23 23 | def f():
118+
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
119+
24 |+ (a, b) = (_x, y) = 1, 2 # this triggers F841 on everything
120+
121+
F841_1.py:24:18: F841 [*] Local variable `y` is assigned to but never used
81122
|
82123
23 | def f():
83124
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
84125
| ^ F841
85126
|
86127
= help: Remove assignment to unused variable `y`
87128

129+
Unsafe fix
130+
21 21 |
131+
22 22 |
132+
23 23 | def f():
133+
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
134+
24 |+ (a, b) = (x, _y) = 1, 2 # this triggers F841 on everything
135+
88136

crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_3.py.snap

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ F841_3.py:27:46: F841 [*] Local variable `z3` is assigned to but never used
156156
29 29 |
157157
30 30 |
158158

159-
F841_3.py:32:6: F841 Local variable `x1` is assigned to but never used
159+
F841_3.py:32:6: F841 [*] Local variable `x1` is assigned to but never used
160160
|
161161
31 | def f():
162162
32 | (x1, y1) = (1, 2)
@@ -166,7 +166,17 @@ F841_3.py:32:6: F841 Local variable `x1` is assigned to but never used
166166
|
167167
= help: Remove assignment to unused variable `x1`
168168

169-
F841_3.py:32:10: F841 Local variable `y1` is assigned to but never used
169+
Unsafe fix
170+
29 29 |
171+
30 30 |
172+
31 31 | def f():
173+
32 |- (x1, y1) = (1, 2)
174+
32 |+ (_x1, y1) = (1, 2)
175+
33 33 | (x2, y2) = coords2 = (1, 2)
176+
34 34 | coords3 = (x3, y3) = (1, 2)
177+
35 35 |
178+
179+
F841_3.py:32:10: F841 [*] Local variable `y1` is assigned to but never used
170180
|
171181
31 | def f():
172182
32 | (x1, y1) = (1, 2)
@@ -176,6 +186,16 @@ F841_3.py:32:10: F841 Local variable `y1` is assigned to but never used
176186
|
177187
= help: Remove assignment to unused variable `y1`
178188

189+
Unsafe fix
190+
29 29 |
191+
30 30 |
192+
31 31 | def f():
193+
32 |- (x1, y1) = (1, 2)
194+
32 |+ (x1, _y1) = (1, 2)
195+
33 33 | (x2, y2) = coords2 = (1, 2)
196+
34 34 | coords3 = (x3, y3) = (1, 2)
197+
35 35 |
198+
179199
F841_3.py:33:16: F841 [*] Local variable `coords2` is assigned to but never used
180200
|
181201
31 | def f():

crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F841_F841_4.py.snap

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ F841_4.py:12:5: F841 [*] Local variable `a` is assigned to but never used
2020
14 14 |
2121
15 15 |
2222

23-
F841_4.py:13:5: F841 Local variable `b` is assigned to but never used
23+
F841_4.py:13:5: F841 [*] Local variable `b` is assigned to but never used
2424
|
2525
11 | def bar():
2626
12 | a = foo()
@@ -29,7 +29,17 @@ F841_4.py:13:5: F841 Local variable `b` is assigned to but never used
2929
|
3030
= help: Remove assignment to unused variable `b`
3131

32-
F841_4.py:13:8: F841 Local variable `c` is assigned to but never used
32+
Unsafe fix
33+
10 10 |
34+
11 11 | def bar():
35+
12 12 | a = foo()
36+
13 |- b, c = foo()
37+
13 |+ _b, c = foo()
38+
14 14 |
39+
15 15 |
40+
16 16 | def baz():
41+
42+
F841_4.py:13:8: F841 [*] Local variable `c` is assigned to but never used
3343
|
3444
11 | def bar():
3545
12 | a = foo()
@@ -38,4 +48,14 @@ F841_4.py:13:8: F841 Local variable `c` is assigned to but never used
3848
|
3949
= help: Remove assignment to unused variable `c`
4050

51+
Unsafe fix
52+
10 10 |
53+
11 11 | def bar():
54+
12 12 | a = foo()
55+
13 |- b, c = foo()
56+
13 |+ b, _c = foo()
57+
14 14 |
58+
15 15 |
59+
16 16 | def baz():
60+
4161

0 commit comments

Comments
 (0)