mirror of https://github.com/astral-sh/ruff
Fix B023 shadowed variables in nested functions (#4111)
This commit is contained in:
parent
e04ef42334
commit
17db2e2a62
|
|
@ -172,3 +172,14 @@ def iter_f(names):
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
return [lambda: i for i in range(3)] # error
|
return [lambda: i for i in range(3)] # error
|
||||||
|
|
||||||
|
|
||||||
|
for val in range(3):
|
||||||
|
def make_func(val=val):
|
||||||
|
def tmp():
|
||||||
|
return print(val)
|
||||||
|
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
|
||||||
|
funcs.append(make_func())
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,8 @@ struct LoadedNamesVisitor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Visitor` to collect all used identifiers in a statement.
|
/// `Visitor` to collect all used identifiers in a statement.
|
||||||
impl<'a, 'b> Visitor<'b> for LoadedNamesVisitor<'a>
|
impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> {
|
||||||
where
|
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExprKind::Name { id, ctx } => match ctx {
|
ExprKind::Name { id, ctx } => match ctx {
|
||||||
ExprContext::Load => self.loaded.push((id, expr, expr.range())),
|
ExprContext::Load => self.loaded.push((id, expr, expr.range())),
|
||||||
|
|
@ -57,11 +54,8 @@ struct SuspiciousVariablesVisitor<'a> {
|
||||||
|
|
||||||
/// `Visitor` to collect all suspicious variables (those referenced in
|
/// `Visitor` to collect all suspicious variables (those referenced in
|
||||||
/// functions, but not bound as arguments).
|
/// functions, but not bound as arguments).
|
||||||
impl<'a, 'b> Visitor<'b> for SuspiciousVariablesVisitor<'a>
|
impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> {
|
||||||
where
|
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
StmtKind::FunctionDef { args, body, .. }
|
StmtKind::FunctionDef { args, body, .. }
|
||||||
| StmtKind::AsyncFunctionDef { args, body, .. } => {
|
| StmtKind::AsyncFunctionDef { args, body, .. } => {
|
||||||
|
|
@ -77,9 +71,10 @@ where
|
||||||
self.names.extend(
|
self.names.extend(
|
||||||
visitor
|
visitor
|
||||||
.loaded
|
.loaded
|
||||||
.iter()
|
.into_iter()
|
||||||
.filter(|(id, ..)| !arg_names.contains(id)),
|
.filter(|(id, ..)| !arg_names.contains(id)),
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
StmtKind::Return { value: Some(value) } => {
|
StmtKind::Return { value: Some(value) } => {
|
||||||
// Mark `return lambda: x` as safe.
|
// Mark `return lambda: x` as safe.
|
||||||
|
|
@ -92,7 +87,7 @@ where
|
||||||
visitor::walk_stmt(self, stmt);
|
visitor::walk_stmt(self, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExprKind::Call {
|
ExprKind::Call {
|
||||||
func,
|
func,
|
||||||
|
|
@ -146,6 +141,8 @@ where
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(id, ..)| !arg_names.contains(id)),
|
.filter(|(id, ..)| !arg_names.contains(id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -160,11 +157,8 @@ struct NamesFromAssignmentsVisitor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Visitor` to collect all names used in an assignment expression.
|
/// `Visitor` to collect all names used in an assignment expression.
|
||||||
impl<'a, 'b> Visitor<'b> for NamesFromAssignmentsVisitor<'a>
|
impl<'a> Visitor<'a> for NamesFromAssignmentsVisitor<'a> {
|
||||||
where
|
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
self.names.insert(id.as_str());
|
self.names.insert(id.as_str());
|
||||||
|
|
@ -188,11 +182,8 @@ struct AssignedNamesVisitor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Visitor` to collect all used identifiers in a statement.
|
/// `Visitor` to collect all used identifiers in a statement.
|
||||||
impl<'a, 'b> Visitor<'b> for AssignedNamesVisitor<'a>
|
impl<'a> Visitor<'a> for AssignedNamesVisitor<'a> {
|
||||||
where
|
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
|
||||||
if matches!(
|
if matches!(
|
||||||
&stmt.node,
|
&stmt.node,
|
||||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. }
|
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. }
|
||||||
|
|
@ -223,7 +214,7 @@ where
|
||||||
visitor::walk_stmt(self, stmt);
|
visitor::walk_stmt(self, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||||
if matches!(&expr.node, ExprKind::Lambda { .. }) {
|
if matches!(&expr.node, ExprKind::Lambda { .. }) {
|
||||||
// Don't recurse.
|
// Don't recurse.
|
||||||
return;
|
return;
|
||||||
|
|
@ -232,7 +223,7 @@ where
|
||||||
visitor::walk_expr(self, expr);
|
visitor::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_comprehension(&mut self, comprehension: &'b Comprehension) {
|
fn visit_comprehension(&mut self, comprehension: &'a Comprehension) {
|
||||||
let mut visitor = NamesFromAssignmentsVisitor::default();
|
let mut visitor = NamesFromAssignmentsVisitor::default();
|
||||||
visitor.visit_expr(&comprehension.target);
|
visitor.visit_expr(&comprehension.target);
|
||||||
self.names.extend(visitor.names);
|
self.names.extend(visitor.names);
|
||||||
|
|
@ -242,14 +233,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// B023
|
/// B023
|
||||||
pub fn function_uses_loop_variable<'a, 'b>(checker: &'a mut Checker<'b>, node: &Node<'b>)
|
pub fn function_uses_loop_variable<'a>(checker: &mut Checker<'a>, node: &Node<'a>) {
|
||||||
where
|
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
// Identify any "suspicious" variables. These are defined as variables that are
|
// Identify any "suspicious" variables. These are defined as variables that are
|
||||||
// referenced in a function or lambda body, but aren't bound as arguments.
|
// referenced in a function or lambda body, but aren't bound as arguments.
|
||||||
let suspicious_variables = {
|
let suspicious_variables = {
|
||||||
let mut visitor = SuspiciousVariablesVisitor::<'b>::default();
|
let mut visitor = SuspiciousVariablesVisitor::default();
|
||||||
match node {
|
match node {
|
||||||
Node::Stmt(stmt) => visitor.visit_stmt(stmt),
|
Node::Stmt(stmt) => visitor.visit_stmt(stmt),
|
||||||
Node::Expr(expr) => visitor.visit_expr(expr),
|
Node::Expr(expr) => visitor.visit_expr(expr),
|
||||||
|
|
@ -260,7 +248,7 @@ where
|
||||||
if !suspicious_variables.is_empty() {
|
if !suspicious_variables.is_empty() {
|
||||||
// Identify any variables that are assigned in the loop (ignoring functions).
|
// Identify any variables that are assigned in the loop (ignoring functions).
|
||||||
let reassigned_in_loop = {
|
let reassigned_in_loop = {
|
||||||
let mut visitor = AssignedNamesVisitor::<'b>::default();
|
let mut visitor = AssignedNamesVisitor::default();
|
||||||
match node {
|
match node {
|
||||||
Node::Stmt(stmt) => visitor.visit_stmt(stmt),
|
Node::Stmt(stmt) => visitor.visit_stmt(stmt),
|
||||||
Node::Expr(expr) => visitor.visit_expr(expr),
|
Node::Expr(expr) => visitor.visit_expr(expr),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue