Skip to content

Commit

Permalink
Avoid infinite renames for unused-loop-control-variable (#2581)
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh authored Feb 5, 2023
1 parent 6b3ae1a commit 84be1df
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 35 deletions.
6 changes: 6 additions & 0 deletions resources/test/fixtures/flake8_bugbear/B007.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,9 @@ def f():

bar = 1
print(bar)


# Unfixable due to trailing underscore (`_line_` wouldn't be considered an ignorable
# variable name).
for line_ in range(self.header_lines):
fp.readline()
94 changes: 59 additions & 35 deletions src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ define_violation!(
pub struct UnusedLoopControlVariable {
/// The name of the loop control variable.
pub name: String,
/// Whether the variable is certain to be unused, or merely suspect.
/// The name to which the variable should be renamed, if it can be safely renamed.
pub rename: Option<String>,
/// Whether the variable is certain to be unused in the loop body, or merely suspect.
/// A variable _may_ be used, but undetectably so, if the loop incorporates
/// by magic control flow (e.g., `locals()`).
pub certainty: Certainty,
Expand All @@ -55,7 +57,9 @@ impl Violation for UnusedLoopControlVariable {

#[derive_message_formats]
fn message(&self) -> String {
let UnusedLoopControlVariable { name, certainty } = self;
let UnusedLoopControlVariable {
name, certainty, ..
} = self;
if matches!(certainty, Certainty::Certain) {
format!("Loop control variable `{name}` not used within loop body")
} else {
Expand All @@ -64,10 +68,13 @@ impl Violation for UnusedLoopControlVariable {
}

fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
let UnusedLoopControlVariable { certainty, .. } = self;
if matches!(certainty, Certainty::Certain) {
Some(|UnusedLoopControlVariable { name, .. }| {
format!("Rename unused `{name}` to `_{name}`")
let UnusedLoopControlVariable {
certainty, rename, ..
} = self;
if matches!(certainty, Certainty::Certain) && rename.is_some() {
Some(|UnusedLoopControlVariable { name, rename, .. }| {
let rename = rename.as_ref().unwrap();
format!("Rename unused `{name}` to `_{rename}`")
})
} else {
None
Expand Down Expand Up @@ -133,50 +140,67 @@ pub fn unused_loop_control_variable(
continue;
}

// Avoid fixing any variables that _may_ be used, but undetectably so.
let certainty = if helpers::uses_magic_variable_access(checker, body) {
Certainty::Uncertain
} else {
Certainty::Certain
};

// Attempt to rename the variable by prepending an underscore, but avoid applying the fix
// if doing so wouldn't actually cause us to ignore the violation in the next pass.
let rename = format!("_{name}");
let rename = if checker
.settings
.dummy_variable_rgx
.is_match(rename.as_str())
{
Some(rename)
} else {
None
};

let mut diagnostic = Diagnostic::new(
UnusedLoopControlVariable {
name: name.to_string(),
rename: rename.clone(),
certainty,
},
Range::from_located(expr),
);
if matches!(certainty, Certainty::Certain) && checker.patch(diagnostic.kind.rule()) {
// Find the `BindingKind::LoopVar` corresponding to the name.
let scope = checker.current_scope();
if let Some(binding) = iter::once(scope.bindings.get(name))
.flatten()
.chain(
iter::once(scope.rebounds.get(name))
.flatten()
.into_iter()
.flatten(),
)
.find_map(|index| {
let binding = &checker.bindings[*index];
if let Some(source) = &binding.source {
if source == &RefEquality(stmt) {
Some(binding)
if let Some(rename) = rename {
if matches!(certainty, Certainty::Certain) && checker.patch(diagnostic.kind.rule()) {
// Find the `BindingKind::LoopVar` corresponding to the name.
let scope = checker.current_scope();
if let Some(binding) = iter::once(scope.bindings.get(name))
.flatten()
.chain(
iter::once(scope.rebounds.get(name))
.flatten()
.into_iter()
.flatten(),
)
.find_map(|index| {
let binding = &checker.bindings[*index];
if let Some(source) = &binding.source {
if source == &RefEquality(stmt) {
Some(binding)
} else {
None
}
} else {
None
}
} else {
None
}
})
{
if matches!(binding.kind, BindingKind::LoopVar) {
if !binding.used() {
// Prefix the variable name with an underscore.
diagnostic.amend(Fix::replacement(
format!("_{name}"),
expr.location,
expr.end_location.unwrap(),
));
})
{
if matches!(binding.kind, BindingKind::LoopVar) {
if !binding.used() {
diagnostic.amend(Fix::replacement(
rename,
expr.location,
expr.end_location.unwrap(),
));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: i
rename: _i
certainty: Certain
location:
row: 6
Expand All @@ -17,6 +18,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: k
rename: _k
certainty: Certain
location:
row: 18
Expand All @@ -37,6 +39,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: i
rename: _i
certainty: Certain
location:
row: 30
Expand All @@ -49,6 +52,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: k
rename: _k
certainty: Certain
location:
row: 30
Expand All @@ -69,6 +73,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Uncertain
location:
row: 34
Expand All @@ -81,6 +86,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Uncertain
location:
row: 38
Expand All @@ -93,6 +99,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Uncertain
location:
row: 42
Expand All @@ -105,6 +112,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Uncertain
location:
row: 46
Expand All @@ -117,6 +125,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Certain
location:
row: 52
Expand All @@ -137,6 +146,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Certain
location:
row: 59
Expand All @@ -149,6 +159,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Certain
location:
row: 68
Expand All @@ -169,6 +180,7 @@ expression: diagnostics
- kind:
UnusedLoopControlVariable:
name: bar
rename: _bar
certainty: Certain
location:
row: 77
Expand All @@ -186,4 +198,17 @@ expression: diagnostics
row: 77
column: 16
parent: ~
- kind:
UnusedLoopControlVariable:
name: line_
rename: ~
certainty: Certain
location:
row: 87
column: 4
end_location:
row: 87
column: 9
fix: ~
parent: ~

0 comments on commit 84be1df

Please sign in to comment.