From c306f85691ef64284138c46e312c3f99dbbffb3a Mon Sep 17 00:00:00 2001 From: Samuel Cormier-Iijima Date: Tue, 12 Dec 2023 13:23:46 -0500 Subject: [PATCH] F841: support fixing unused assignments in tuples by renaming variables (#9107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 😅 --- .../rules/pyflakes/rules/unused_variable.rs | 8 +++ ...ules__pyflakes__tests__F841_F841_0.py.snap | 24 +++++++- ...ules__pyflakes__tests__F841_F841_1.py.snap | 60 +++++++++++++++++-- ...ules__pyflakes__tests__F841_F841_3.py.snap | 24 +++++++- ...lakes__tests__preview__F841_F841_4.py.snap | 24 +++++++- 5 files changed, 128 insertions(+), 12 deletions(-) diff --git a/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs b/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs index f5eb694483..e099c15f31 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs @@ -247,6 +247,14 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option { Some(Fix::unsafe_edit(edit).isolate(isolation)) }; } + } else { + let name = binding.name(checker.locator()); + let renamed = format!("_{name}"); + if checker.settings.dummy_variable_rgx.is_match(&renamed) { + let edit = Edit::range_replacement(renamed, binding.range()); + + return Some(Fix::unsafe_edit(edit).isolate(isolation)); + } } } diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_0.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_0.py.snap index 3fc06720a7..7e1b2458bf 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_0.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_0.py.snap @@ -57,7 +57,7 @@ F841_0.py:20:5: F841 [*] Local variable `foo` is assigned to but never used 22 21 | 23 22 | bar = (1, 2) -F841_0.py:21:6: F841 Local variable `a` is assigned to but never used +F841_0.py:21:6: F841 [*] Local variable `a` is assigned to but never used | 19 | def f(): 20 | foo = (1, 2) @@ -68,7 +68,17 @@ F841_0.py:21:6: F841 Local variable `a` is assigned to but never used | = help: Remove assignment to unused variable `a` -F841_0.py:21:9: F841 Local variable `b` is assigned to but never used +ℹ Unsafe fix +18 18 | +19 19 | def f(): +20 20 | foo = (1, 2) +21 |- (a, b) = (1, 2) + 21 |+ (_a, b) = (1, 2) +22 22 | +23 23 | bar = (1, 2) +24 24 | (c, d) = bar + +F841_0.py:21:9: F841 [*] Local variable `b` is assigned to but never used | 19 | def f(): 20 | foo = (1, 2) @@ -79,6 +89,16 @@ F841_0.py:21:9: F841 Local variable `b` is assigned to but never used | = help: Remove assignment to unused variable `b` +ℹ Unsafe fix +18 18 | +19 19 | def f(): +20 20 | foo = (1, 2) +21 |- (a, b) = (1, 2) + 21 |+ (a, _b) = (1, 2) +22 22 | +23 23 | bar = (1, 2) +24 24 | (c, d) = bar + F841_0.py:26:14: F841 [*] Local variable `baz` is assigned to but never used | 24 | (c, d) = bar diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_1.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_1.py.snap index 7985de9913..9cb0b013fd 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_1.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_1.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F841_1.py:6:5: F841 Local variable `x` is assigned to but never used +F841_1.py:6:5: F841 [*] Local variable `x` is assigned to but never used | 5 | def f(): 6 | x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed @@ -9,7 +9,17 @@ F841_1.py:6:5: F841 Local variable `x` is assigned to but never used | = help: Remove assignment to unused variable `x` -F841_1.py:6:8: F841 Local variable `y` is assigned to but never used +ℹ Unsafe fix +3 3 | +4 4 | +5 5 | def f(): +6 |- x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed + 6 |+ _x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed +7 7 | +8 8 | +9 9 | def f(): + +F841_1.py:6:8: F841 [*] Local variable `y` is assigned to but never used | 5 | def f(): 6 | x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed @@ -17,6 +27,16 @@ F841_1.py:6:8: F841 Local variable `y` is assigned to but never used | = help: Remove assignment to unused variable `y` +ℹ Unsafe fix +3 3 | +4 4 | +5 5 | def f(): +6 |- x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed + 6 |+ x, _y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed +7 7 | +8 8 | +9 9 | def f(): + F841_1.py:16:14: F841 [*] Local variable `coords` is assigned to but never used | 15 | def f(): @@ -53,7 +73,7 @@ F841_1.py:20:5: F841 [*] Local variable `coords` is assigned to but never used 22 22 | 23 23 | def f(): -F841_1.py:24:6: F841 Local variable `a` is assigned to but never used +F841_1.py:24:6: F841 [*] Local variable `a` is assigned to but never used | 23 | def f(): 24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything @@ -61,7 +81,14 @@ F841_1.py:24:6: F841 Local variable `a` is assigned to but never used | = help: Remove assignment to unused variable `a` -F841_1.py:24:9: F841 Local variable `b` is assigned to but never used +ℹ Unsafe fix +21 21 | +22 22 | +23 23 | def f(): +24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything + 24 |+ (_a, b) = (x, y) = 1, 2 # this triggers F841 on everything + +F841_1.py:24:9: F841 [*] Local variable `b` is assigned to but never used | 23 | def f(): 24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything @@ -69,7 +96,14 @@ F841_1.py:24:9: F841 Local variable `b` is assigned to but never used | = help: Remove assignment to unused variable `b` -F841_1.py:24:15: F841 Local variable `x` is assigned to but never used +ℹ Unsafe fix +21 21 | +22 22 | +23 23 | def f(): +24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything + 24 |+ (a, _b) = (x, y) = 1, 2 # this triggers F841 on everything + +F841_1.py:24:15: F841 [*] Local variable `x` is assigned to but never used | 23 | def f(): 24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything @@ -77,7 +111,14 @@ F841_1.py:24:15: F841 Local variable `x` is assigned to but never used | = help: Remove assignment to unused variable `x` -F841_1.py:24:18: F841 Local variable `y` is assigned to but never used +ℹ Unsafe fix +21 21 | +22 22 | +23 23 | def f(): +24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything + 24 |+ (a, b) = (_x, y) = 1, 2 # this triggers F841 on everything + +F841_1.py:24:18: F841 [*] Local variable `y` is assigned to but never used | 23 | def f(): 24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything @@ -85,4 +126,11 @@ F841_1.py:24:18: F841 Local variable `y` is assigned to but never used | = help: Remove assignment to unused variable `y` +ℹ Unsafe fix +21 21 | +22 22 | +23 23 | def f(): +24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything + 24 |+ (a, b) = (x, _y) = 1, 2 # this triggers F841 on everything + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_3.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_3.py.snap index 9a29ab4098..8b60f4da8a 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_3.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F841_F841_3.py.snap @@ -156,7 +156,7 @@ F841_3.py:27:46: F841 [*] Local variable `z3` is assigned to but never used 29 29 | 30 30 | -F841_3.py:32:6: F841 Local variable `x1` is assigned to but never used +F841_3.py:32:6: F841 [*] Local variable `x1` is assigned to but never used | 31 | def f(): 32 | (x1, y1) = (1, 2) @@ -166,7 +166,17 @@ F841_3.py:32:6: F841 Local variable `x1` is assigned to but never used | = help: Remove assignment to unused variable `x1` -F841_3.py:32:10: F841 Local variable `y1` is assigned to but never used +ℹ Unsafe fix +29 29 | +30 30 | +31 31 | def f(): +32 |- (x1, y1) = (1, 2) + 32 |+ (_x1, y1) = (1, 2) +33 33 | (x2, y2) = coords2 = (1, 2) +34 34 | coords3 = (x3, y3) = (1, 2) +35 35 | + +F841_3.py:32:10: F841 [*] Local variable `y1` is assigned to but never used | 31 | def f(): 32 | (x1, y1) = (1, 2) @@ -176,6 +186,16 @@ F841_3.py:32:10: F841 Local variable `y1` is assigned to but never used | = help: Remove assignment to unused variable `y1` +ℹ Unsafe fix +29 29 | +30 30 | +31 31 | def f(): +32 |- (x1, y1) = (1, 2) + 32 |+ (x1, _y1) = (1, 2) +33 33 | (x2, y2) = coords2 = (1, 2) +34 34 | coords3 = (x3, y3) = (1, 2) +35 35 | + F841_3.py:33:16: F841 [*] Local variable `coords2` is assigned to but never used | 31 | def f(): diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F841_F841_4.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F841_F841_4.py.snap index f781c31d82..661343dd14 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F841_F841_4.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__preview__F841_F841_4.py.snap @@ -20,7 +20,7 @@ F841_4.py:12:5: F841 [*] Local variable `a` is assigned to but never used 14 14 | 15 15 | -F841_4.py:13:5: F841 Local variable `b` is assigned to but never used +F841_4.py:13:5: F841 [*] Local variable `b` is assigned to but never used | 11 | def bar(): 12 | a = foo() @@ -29,7 +29,17 @@ F841_4.py:13:5: F841 Local variable `b` is assigned to but never used | = help: Remove assignment to unused variable `b` -F841_4.py:13:8: F841 Local variable `c` is assigned to but never used +ℹ Unsafe fix +10 10 | +11 11 | def bar(): +12 12 | a = foo() +13 |- b, c = foo() + 13 |+ _b, c = foo() +14 14 | +15 15 | +16 16 | def baz(): + +F841_4.py:13:8: F841 [*] Local variable `c` is assigned to but never used | 11 | def bar(): 12 | a = foo() @@ -38,4 +48,14 @@ F841_4.py:13:8: F841 Local variable `c` is assigned to but never used | = help: Remove assignment to unused variable `c` +ℹ Unsafe fix +10 10 | +11 11 | def bar(): +12 12 | a = foo() +13 |- b, c = foo() + 13 |+ b, _c = foo() +14 14 | +15 15 | +16 16 | def baz(): +