Skip to content

Commit

Permalink
Change scoping ruels for NamedExpr in comp
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Apr 28, 2023
1 parent d87cf3d commit 4c1515a
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 8 deletions.
54 changes: 47 additions & 7 deletions crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4269,7 +4269,20 @@ impl<'a> Checker<'a> {
}
}

let scope = self.ctx.scope();
// Per [PEP 572](https://peps.python.org/pep-0572/#scope-of-the-target), named
// expressions in generators and comprehensions bind to the parent scope.
let scope_id = if binding.kind.is_named_expr_assignment() {
self.ctx
.scopes
.ancestor_scopes(self.ctx.scope_id)
.find(|scope| !scope.kind.is_generator())
.expect("Every scope must descend from the global scope.")
.id
} else {
self.ctx.scope_id
};

let scope = &self.ctx.scopes[scope_id];
let binding = if let Some(index) = scope.get(name) {
let existing = &self.ctx.bindings[*index];
match &existing.kind {
Expand Down Expand Up @@ -4301,11 +4314,16 @@ impl<'a> Checker<'a> {

// Don't treat annotations as assignments if there is an existing value
// in scope.
let scope = self.ctx.scope_mut();
if !(binding.kind.is_annotation() && scope.defines(name)) {
scope.add(name, binding_id);
if binding.kind.is_annotation() && scope.defines(name) {
self.ctx.bindings.push(binding);
return;
}

// Add the binding to the scope.
let scope = &mut self.ctx.scopes[scope_id];
scope.add(name, binding_id);

// Add the binding to the arena.
self.ctx.bindings.push(binding);
}

Expand Down Expand Up @@ -4582,9 +4600,10 @@ impl<'a> Checker<'a> {
return;
}

let current = self.ctx.scope();
let scope = self.ctx.scope();

if id == "__all__"
&& matches!(current.kind, ScopeKind::Module)
&& scope.kind.is_module()
&& matches!(
parent.node,
StmtKind::Assign { .. } | StmtKind::AugAssign { .. } | StmtKind::AnnAssign { .. }
Expand Down Expand Up @@ -4622,7 +4641,7 @@ impl<'a> Checker<'a> {

// Grab the existing bound __all__ values.
if let StmtKind::AugAssign { .. } = &parent.node {
if let Some(index) = current.get("__all__") {
if let Some(index) = scope.get("__all__") {
if let BindingKind::Export(Export { names: existing }) =
&self.ctx.bindings[*index].kind
{
Expand Down Expand Up @@ -4665,6 +4684,27 @@ impl<'a> Checker<'a> {
}
}

if self
.ctx
.expr_ancestors()
.any(|expr| matches!(expr.node, ExprKind::NamedExpr { .. }))
{
self.add_binding(
id,
Binding {
kind: BindingKind::NamedExprAssignment,
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: expr.range(),
source: Some(*self.ctx.current_stmt()),
context: self.ctx.execution_context(),
exceptions: self.ctx.exceptions(),
},
);
return;
}

self.add_binding(
id,
Binding {
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff/src/rules/pyflakes/rules/unused_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ pub fn unused_variable(checker: &mut Checker, scope: ScopeId) {
.map(|(name, index)| (name, &checker.ctx.bindings[*index]))
{
if !binding.used()
&& binding.kind.is_assignment()
&& (binding.kind.is_assignment() || binding.kind.is_named_expr_assignment())
&& !checker.settings.dummy_variable_rgx.is_match(name)
&& name != &"__tracebackhide__"
&& name != &"__traceback_info__"
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_python_semantic/src/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ pub enum BindingKind<'a> {
Annotation,
Argument,
Assignment,
NamedExprAssignment,
Binding,
LoopVar,
Global,
Expand Down

0 comments on commit 4c1515a

Please sign in to comment.