diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 53a51485635f4..d91dcc25f9524 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -588,8 +588,10 @@ impl<'a> Visitor<'a> for Checker<'a> { Stmt::Global(ast::StmtGlobal { names, range: _ }) => { if !self.semantic.scope_id.is_global() { for name in names { - if let Some(binding_id) = self.semantic.global_scope().get(name) { - // Mark the binding in the global scope as "rebound" in the current scope. + let binding_id = self.semantic.global_scope().get(name); + + // Mark the binding in the global scope as "rebound" in the current scope. + if let Some(binding_id) = binding_id { self.semantic .add_rebinding_scope(binding_id, self.semantic.scope_id); } @@ -597,7 +599,7 @@ impl<'a> Visitor<'a> for Checker<'a> { // Add a binding to the current scope. let binding_id = self.semantic.push_binding( name.range(), - BindingKind::Global, + BindingKind::Global(binding_id), BindingFlags::GLOBAL, ); let scope = self.semantic.current_scope_mut(); @@ -609,7 +611,8 @@ impl<'a> Visitor<'a> for Checker<'a> { if !self.semantic.scope_id.is_global() { for name in names { if let Some((scope_id, binding_id)) = self.semantic.nonlocal(name) { - // Mark the binding as "used". + // Mark the binding as "used", since the `nonlocal` requires an existing + // binding. self.semantic.add_local_reference( binding_id, ExprContext::Load, @@ -624,7 +627,7 @@ impl<'a> Visitor<'a> for Checker<'a> { // Add a binding to the current scope. let binding_id = self.semantic.push_binding( name.range(), - BindingKind::Nonlocal(scope_id), + BindingKind::Nonlocal(binding_id, scope_id), BindingFlags::NONLOCAL, ); let scope = self.semantic.current_scope_mut(); diff --git a/crates/ruff_linter/src/renamer.rs b/crates/ruff_linter/src/renamer.rs index 811493777cc75..153e94d0053d9 100644 --- a/crates/ruff_linter/src/renamer.rs +++ b/crates/ruff_linter/src/renamer.rs @@ -125,8 +125,8 @@ impl Renamer { let scope_id = scope.get_all(name).find_map(|binding_id| { let binding = semantic.binding(binding_id); match binding.kind { - BindingKind::Global => Some(ScopeId::global()), - BindingKind::Nonlocal(symbol_id) => Some(symbol_id), + BindingKind::Global(_) => Some(ScopeId::global()), + BindingKind::Nonlocal(_, scope_id) => Some(scope_id), _ => None, } }); @@ -266,8 +266,8 @@ impl Renamer { | BindingKind::LoopVar | BindingKind::ComprehensionVar | BindingKind::WithItemVar - | BindingKind::Global - | BindingKind::Nonlocal(_) + | BindingKind::Global(_) + | BindingKind::Nonlocal(_, _) | BindingKind::ClassDefinition(_) | BindingKind::FunctionDefinition(_) | BindingKind::Deletion diff --git a/crates/ruff_linter/src/rules/pandas_vet/helpers.rs b/crates/ruff_linter/src/rules/pandas_vet/helpers.rs index efcc6634a9c4a..13259c2946e44 100644 --- a/crates/ruff_linter/src/rules/pandas_vet/helpers.rs +++ b/crates/ruff_linter/src/rules/pandas_vet/helpers.rs @@ -48,8 +48,8 @@ pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resoluti | BindingKind::NamedExprAssignment | BindingKind::LoopVar | BindingKind::ComprehensionVar - | BindingKind::Global - | BindingKind::Nonlocal(_) => Resolution::RelevantLocal, + | BindingKind::Global(_) + | BindingKind::Nonlocal(_, _) => Resolution::RelevantLocal, BindingKind::Import(import) if matches!(import.qualified_name().segments(), ["pandas"]) => { 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 2023c58ad14a8..1aaff45b863ab 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 @@ -54,8 +54,8 @@ pub(crate) fn non_ascii_name(binding: &Binding, locator: &Locator) -> Option Kind::LoopVar, BindingKind::ComprehensionVar => Kind::ComprenhensionVar, BindingKind::WithItemVar => Kind::WithItemVar, - BindingKind::Global => Kind::Global, - BindingKind::Nonlocal(_) => Kind::Nonlocal, + BindingKind::Global(_) => Kind::Global, + BindingKind::Nonlocal(_, _) => Kind::Nonlocal, BindingKind::ClassDefinition(_) => Kind::ClassDefinition, BindingKind::FunctionDefinition(_) => Kind::FunctionDefinition, BindingKind::BoundException => Kind::BoundException, diff --git a/crates/ruff_python_semantic/src/binding.rs b/crates/ruff_python_semantic/src/binding.rs index 287fcc8627b2d..67236682f9d30 100644 --- a/crates/ruff_python_semantic/src/binding.rs +++ b/crates/ruff_python_semantic/src/binding.rs @@ -470,14 +470,14 @@ pub enum BindingKind<'a> { /// def foo(): /// global x /// ``` - Global, + Global(Option), /// A binding for a nonlocal variable, like `x` in: /// ```python /// def foo(): /// nonlocal x /// ``` - Nonlocal(ScopeId), + Nonlocal(BindingId, ScopeId), /// A binding for a builtin, like `print` or `bool`. Builtin, @@ -670,3 +670,14 @@ impl<'a, 'ast> Imported<'ast> for AnyImport<'a, 'ast> { } } } + +#[cfg(test)] +mod tests { + use crate::BindingKind; + + #[test] + #[cfg(target_pointer_width = "64")] + fn size() { + assert!(std::mem::size_of::() <= 24); + } +} diff --git a/crates/ruff_python_semantic/src/model.rs b/crates/ruff_python_semantic/src/model.rs index 5bfa87821c1a1..e567a5e936f84 100644 --- a/crates/ruff_python_semantic/src/model.rs +++ b/crates/ruff_python_semantic/src/model.rs @@ -540,6 +540,23 @@ impl<'a> SemanticModel<'a> { return ReadResult::Resolved(binding_id); } + BindingKind::Global(Some(binding_id)) + | BindingKind::Nonlocal(binding_id, _) => { + // Mark the shadowed binding as used. + let reference_id = self.resolved_references.push( + self.scope_id, + self.node_id, + ExprContext::Load, + self.flags, + name.range, + ); + self.bindings[binding_id].references.push(reference_id); + + // Treat it as resolved. + self.resolved_names.insert(name.into(), binding_id); + return ReadResult::Resolved(binding_id); + } + _ => { // Otherwise, treat it as resolved. self.resolved_names.insert(name.into(), binding_id);