Skip to content

Commit bf0fd04

Browse files
[ruff] Implemented used-dummy-variable (RUF052) (#14611)
Co-authored-by: Alex Waygood <[email protected]>
1 parent a69dfd4 commit bf0fd04

File tree

12 files changed

+778
-8
lines changed

12 files changed

+778
-8
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Correct
2+
3+
for _ in range(5):
4+
pass
5+
6+
_valid_type = int
7+
8+
_valid_var_1: _valid_type
9+
10+
_valid_var_1 = 1
11+
12+
_valid_var_2 = 2
13+
14+
_valid_var_3 = _valid_var_1 + _valid_var_2
15+
16+
def _valid_fun():
17+
pass
18+
19+
_valid_fun()
20+
21+
def fun(arg):
22+
_valid_unused_var = arg
23+
pass
24+
25+
_x = "global"
26+
27+
def fun():
28+
global _x
29+
return _x
30+
31+
def fun():
32+
__dunder__ = "dunder variable"
33+
return __dunder__
34+
35+
def fun():
36+
global _x
37+
_x = "reassigned global"
38+
return _x
39+
40+
class _ValidClass:
41+
pass
42+
43+
_ValidClass()
44+
45+
class ClassOk:
46+
_valid_private_cls_attr = 1
47+
48+
print(_valid_private_cls_attr)
49+
50+
def __init__(self):
51+
self._valid_private_ins_attr = 2
52+
print(self._valid_private_ins_attr)
53+
54+
def _valid_method(self):
55+
return self._valid_private_ins_attr
56+
57+
def method(arg):
58+
_valid_unused_var = arg
59+
return
60+
61+
def fun(x):
62+
_ = 1
63+
__ = 2
64+
___ = 3
65+
if x == 1:
66+
return _
67+
if x == 2:
68+
return __
69+
if x == 3:
70+
return ___
71+
return x
72+
73+
# Incorrect
74+
75+
class Class_:
76+
def fun(self):
77+
_var = "method variable" # [RUF052]
78+
return _var
79+
80+
def fun(_var): # [RUF052]
81+
return _var
82+
83+
def fun():
84+
_list = "built-in" # [RUF052]
85+
return _list
86+
87+
x = "global"
88+
89+
def fun():
90+
global x
91+
_x = "shadows global" # [RUF052]
92+
return _x
93+
94+
def foo():
95+
x = "outer"
96+
def bar():
97+
nonlocal x
98+
_x = "shadows nonlocal" # [RUF052]
99+
return _x
100+
bar()
101+
return x
102+
103+
def fun():
104+
x = "local"
105+
_x = "shadows local" # [RUF052]
106+
return _x

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::rules::{
1010
/// Run lint rules over the [`Binding`]s.
1111
pub(crate) fn bindings(checker: &mut Checker) {
1212
if !checker.any_enabled(&[
13+
Rule::AssignmentInAssert,
1314
Rule::InvalidAllFormat,
1415
Rule::InvalidAllObject,
1516
Rule::NonAsciiName,
@@ -18,7 +19,7 @@ pub(crate) fn bindings(checker: &mut Checker) {
1819
Rule::UnsortedDunderSlots,
1920
Rule::UnusedVariable,
2021
Rule::UnquotedTypeAlias,
21-
Rule::AssignmentInAssert,
22+
Rule::UsedDummyVariable,
2223
]) {
2324
return;
2425
}
@@ -88,6 +89,11 @@ pub(crate) fn bindings(checker: &mut Checker) {
8889
checker.diagnostics.push(diagnostic);
8990
}
9091
}
92+
if checker.enabled(Rule::UsedDummyVariable) {
93+
if let Some(diagnostic) = ruff::rules::used_dummy_variable(checker, binding) {
94+
checker.diagnostics.push(diagnostic);
95+
}
96+
}
9197
if checker.enabled(Rule::AssignmentInAssert) {
9298
if let Some(diagnostic) = ruff::rules::assignment_in_assert(checker, binding) {
9399
checker.diagnostics.push(diagnostic);

crates/ruff_linter/src/codes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
984984
(Ruff, "040") => (RuleGroup::Preview, rules::ruff::rules::InvalidAssertMessageLiteralArgument),
985985
(Ruff, "041") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryNestedLiteral),
986986
(Ruff, "048") => (RuleGroup::Preview, rules::ruff::rules::MapIntVersionParsing),
987+
(Ruff, "052") => (RuleGroup::Preview, rules::ruff::rules::UsedDummyVariable),
987988
(Ruff, "055") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRegularExpression),
988989
(Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA),
989990
(Ruff, "101") => (RuleGroup::Stable, rules::ruff::rules::RedirectedNOQA),

crates/ruff_linter/src/rules/ruff/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod tests {
1010
use std::path::Path;
1111

1212
use anyhow::Result;
13+
use regex::Regex;
1314
use rustc_hash::FxHashSet;
1415
use test_case::test_case;
1516

@@ -70,6 +71,7 @@ mod tests {
7071
#[test_case(Rule::InvalidAssertMessageLiteralArgument, Path::new("RUF040.py"))]
7172
#[test_case(Rule::UnnecessaryNestedLiteral, Path::new("RUF041.py"))]
7273
#[test_case(Rule::UnnecessaryNestedLiteral, Path::new("RUF041.pyi"))]
74+
#[test_case(Rule::UsedDummyVariable, Path::new("RUF052.py"))]
7375
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
7476
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
7577
let diagnostics = test_path(
@@ -449,4 +451,32 @@ mod tests {
449451
assert_messages!(snapshot, diagnostics);
450452
Ok(())
451453
}
454+
455+
#[test_case(Rule::UsedDummyVariable, Path::new("RUF052.py"), r"^_+", 1)]
456+
#[test_case(Rule::UsedDummyVariable, Path::new("RUF052.py"), r"", 2)]
457+
fn custom_regexp_preset(
458+
rule_code: Rule,
459+
path: &Path,
460+
regex_pattern: &str,
461+
id: u8,
462+
) -> Result<()> {
463+
// Compile the regex from the pattern string
464+
let regex = Regex::new(regex_pattern).unwrap();
465+
466+
let snapshot = format!(
467+
"custom_dummy_var_regexp_preset__{}_{}_{}",
468+
rule_code.noqa_code(),
469+
path.to_string_lossy(),
470+
id,
471+
);
472+
let diagnostics = test_path(
473+
Path::new("ruff").join(path).as_path(),
474+
&settings::LinterSettings {
475+
dummy_variable_rgx: regex,
476+
..settings::LinterSettings::for_rule(rule_code)
477+
},
478+
)?;
479+
assert_messages!(snapshot, diagnostics);
480+
Ok(())
481+
}
452482
}

crates/ruff_linter/src/rules/ruff/rules/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub(crate) use unraw_re_pattern::*;
3838
pub(crate) use unsafe_markup_use::*;
3939
pub(crate) use unused_async::*;
4040
pub(crate) use unused_noqa::*;
41+
pub(crate) use used_dummy_variable::*;
4142
pub(crate) use useless_if_else::*;
4243
pub(crate) use zip_instead_of_pairwise::*;
4344

@@ -85,6 +86,7 @@ mod unraw_re_pattern;
8586
mod unsafe_markup_use;
8687
mod unused_async;
8788
mod unused_noqa;
89+
mod used_dummy_variable;
8890
mod useless_if_else;
8991
mod zip_instead_of_pairwise;
9092

0 commit comments

Comments
 (0)