Fix B023 shadowed variables in nested functions (#4111)

This commit is contained in:
Micha Reiser 2023-04-26 23:01:31 +02:00 committed by GitHub
parent e04ef42334
commit 17db2e2a62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 30 deletions

View File

@ -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())

View File

@ -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),