Files
ruff/src/flake8_bugbear/rules/unused_loop_control_variable.rs
2023-01-09 01:39:51 -05:00

80 lines
2.0 KiB
Rust

use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, ExprKind, Stmt};
use crate::ast::types::Range;
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::autofix::Fix;
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violations;
/// Identify all `ExprKind::Name` nodes in an AST.
struct NameFinder<'a> {
/// A map from identifier to defining expression.
names: FxHashMap<&'a str, &'a Expr>,
}
impl NameFinder<'_> {
fn new() -> Self {
NameFinder {
names: FxHashMap::default(),
}
}
}
impl<'a, 'b> Visitor<'b> for NameFinder<'a>
where
'b: 'a,
{
fn visit_expr(&mut self, expr: &'a Expr) {
if let ExprKind::Name { id, .. } = &expr.node {
self.names.insert(id, expr);
}
visitor::walk_expr(self, expr);
}
}
/// B007
pub fn unused_loop_control_variable(checker: &mut Checker, target: &Expr, body: &[Stmt]) {
let control_names = {
let mut finder = NameFinder::new();
finder.visit_expr(target);
finder.names
};
let used_names = {
let mut finder = NameFinder::new();
for stmt in body {
finder.visit_stmt(stmt);
}
finder.names
};
for (name, expr) in control_names {
// Ignore names that are already underscore-prefixed.
if name.starts_with('_') {
continue;
}
// Ignore any names that are actually used in the loop body.
if used_names.contains_key(name) {
continue;
}
let mut diagnostic = Diagnostic::new(
violations::UnusedLoopControlVariable(name.to_string()),
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.code()) {
// Prefix the variable name with an underscore.
diagnostic.amend(Fix::replacement(
format!("_{name}"),
expr.location,
expr.end_location.unwrap(),
));
}
checker.diagnostics.push(diagnostic);
}
}