diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 02caf6eea3dfb..2ccef25e7253b 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1635,6 +1635,11 @@ impl<'a> Checker<'a> { return; } + if parent.is_with_stmt() { + self.add_binding(id, expr.range(), BindingKind::WithItemVar, flags); + return; + } + let scope = self.semantic.current_scope(); if scope.kind.is_module() diff --git a/crates/ruff_linter/src/renamer.rs b/crates/ruff_linter/src/renamer.rs index ca8b133dec37b..fd2cea8304cee 100644 --- a/crates/ruff_linter/src/renamer.rs +++ b/crates/ruff_linter/src/renamer.rs @@ -248,6 +248,7 @@ impl Renamer { | BindingKind::Assignment | BindingKind::BoundException | BindingKind::LoopVar + | BindingKind::WithItemVar | BindingKind::Global | BindingKind::Nonlocal(_) | BindingKind::ClassDefinition(_) 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 156efde80e76f..f5eb694483617 100644 --- a/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff_linter/src/rules/pyflakes/rules/unused_variable.rs @@ -322,7 +322,9 @@ pub(crate) fn unused_variable(checker: &Checker, scope: &Scope, diagnostics: &mu .bindings() .map(|(name, binding_id)| (name, checker.semantic().binding(binding_id))) .filter_map(|(name, binding)| { - if (binding.kind.is_assignment() || binding.kind.is_named_expr_assignment()) + if (binding.kind.is_assignment() + || binding.kind.is_named_expr_assignment() + || binding.kind.is_with_item_var()) && (!binding.is_unpacked_assignment() || matches!(checker.settings.preview, PreviewMode::Enabled)) && !binding.is_nonlocal() diff --git a/crates/ruff_linter/src/rules/pylint/rules/non_ascii_name.rs b/crates/ruff_linter/src/rules/pylint/rules/non_ascii_name.rs index 0312bab0dba60..93733827b4cfc 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/non_ascii_name.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/non_ascii_name.rs @@ -52,6 +52,7 @@ pub(crate) fn non_ascii_name(binding: &Binding, locator: &Locator) -> Option Kind::Assignment, BindingKind::TypeParam => Kind::TypeParam, BindingKind::LoopVar => Kind::LoopVar, + BindingKind::WithItemVar => Kind::WithItemVar, BindingKind::Global => Kind::Global, BindingKind::Nonlocal(_) => Kind::Nonlocal, BindingKind::ClassDefinition(_) => Kind::ClassDefinition, @@ -87,6 +88,7 @@ enum Kind { Assignment, TypeParam, LoopVar, + WithItemVar, Global, Nonlocal, ClassDefinition, @@ -103,6 +105,7 @@ impl fmt::Display for Kind { Kind::Assignment => f.write_str("Variable"), Kind::TypeParam => f.write_str("Type parameter"), Kind::LoopVar => f.write_str("Variable"), + Kind::WithItemVar => f.write_str("Variable"), Kind::Global => f.write_str("Global"), Kind::Nonlocal => f.write_str("Nonlocal"), Kind::ClassDefinition => f.write_str("Class"), diff --git a/crates/ruff_python_semantic/src/binding.rs b/crates/ruff_python_semantic/src/binding.rs index b9cd7b7285db7..e2f29ae07fae7 100644 --- a/crates/ruff_python_semantic/src/binding.rs +++ b/crates/ruff_python_semantic/src/binding.rs @@ -432,6 +432,13 @@ pub enum BindingKind<'a> { /// ``` LoopVar, + /// A binding for a with statement variable, like `x` in: + /// ```python + /// with open('foo.py') as x: + /// ... + /// ``` + WithItemVar, + /// A binding for a global variable, like `x` in: /// ```python /// def foo():