diff --git a/resources/test/fixtures/F841.py b/resources/test/fixtures/F841.py index a524275b2b..feae495fc9 100644 --- a/resources/test/fixtures/F841.py +++ b/resources/test/fixtures/F841.py @@ -24,3 +24,8 @@ def g(): (c, d) = bar (x, y) = baz = bar + + +def h(): + locals() + x = 1 diff --git a/src/ast/checks.rs b/src/ast/checks.rs index 823be2eb8d..72b00b0944 100644 --- a/src/ast/checks.rs +++ b/src/ast/checks.rs @@ -7,7 +7,7 @@ use rustpython_parser::ast::{ }; use crate::ast::operations::SourceCodeLocator; -use crate::ast::types::{Binding, BindingKind, Scope}; +use crate::ast::types::{Binding, BindingKind, FunctionScope, Scope, ScopeKind}; use crate::autofix::{fixer, fixes}; use crate::checks::{Check, CheckKind, Fix, RejectedCmpop}; @@ -67,6 +67,13 @@ pub fn check_not_tests( pub fn check_unused_variables(scope: &Scope) -> Vec { let mut checks: Vec = vec![]; + if matches!( + scope.kind, + ScopeKind::Function(FunctionScope { uses_locals: true }) + ) { + return checks; + } + for (name, binding) in scope.values.iter() { // TODO(charlie): Ignore if using `locals`. if binding.used.is_none() diff --git a/src/ast/types.rs b/src/ast/types.rs index 84942b445f..fb292e5b65 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -8,10 +8,15 @@ fn id() -> usize { COUNTER.fetch_add(1, Ordering::Relaxed) } +#[derive(Clone, Debug, Default)] +pub struct FunctionScope { + pub uses_locals: bool, +} + #[derive(Clone, Debug)] pub enum ScopeKind { Class, - Function, + Function(FunctionScope), Generator, Module, } diff --git a/src/check_ast.rs b/src/check_ast.rs index f450cb7513..13a8ec9dad 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -9,7 +9,7 @@ use rustpython_parser::parser; use crate::ast::operations::{extract_all_names, SourceCodeLocator}; use crate::ast::relocate::relocate_expr; -use crate::ast::types::{Binding, BindingKind, Scope, ScopeKind}; +use crate::ast::types::{Binding, BindingKind, FunctionScope, Scope, ScopeKind}; use crate::ast::visitor::{walk_excepthandler, Visitor}; use crate::ast::{checks, operations, visitor}; use crate::autofix::fixer; @@ -646,6 +646,33 @@ where self.checks.push(check) } } + + if let ExprKind::Name { id, ctx } = &func.node { + if id == "locals" && matches!(ctx, ExprContext::Load) { + let scope = &mut self.scopes[*(self + .scope_stack + .last_mut() + .expect("No current scope found."))]; + if matches!( + scope.kind, + ScopeKind::Function(FunctionScope { uses_locals: false }) + ) { + scope.kind = ScopeKind::Function(FunctionScope { uses_locals: true }); + } + } + } + + // + // if id == "locals" { + // let scope = &self.scopes + // [*(self.scope_stack.last().expect("No current scope found."))]; + // if matches!(scope.kind, ScopeKind::Function(_)) { + // let parent = + // self.parents[*(self.parent_stack.last().expect("No parent found."))]; + // if matches!(parent.node, StmtKind::Call) + // } + // + // } } ExprKind::Dict { keys, .. } => { let check_repeated_literals = self.settings.select.contains(&CheckCode::F601); @@ -1136,11 +1163,11 @@ impl<'a> Checker<'a> { &self.scopes[*(self.scope_stack.last().expect("No current scope found."))]; if self.settings.select.contains(&CheckCode::F823) - && matches!(current.kind, ScopeKind::Function) + && matches!(current.kind, ScopeKind::Function(_)) && !current.values.contains_key(id) { for scope in self.scopes.iter().rev().skip(1) { - if matches!(scope.kind, ScopeKind::Function | ScopeKind::Module) { + if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) { if let Some(binding) = scope.values.get(id) { if let Some((scope_id, location)) = binding.used { if scope_id == current.id { @@ -1266,7 +1293,7 @@ impl<'a> Checker<'a> { while let Some((stmt, scopes, parents)) = self.deferred_functions.pop() { self.parent_stack = parents; self.scope_stack = scopes; - self.push_scope(Scope::new(ScopeKind::Function)); + self.push_scope(Scope::new(ScopeKind::Function(Default::default()))); match &stmt.node { StmtKind::FunctionDef { body, args, .. } @@ -1290,7 +1317,7 @@ impl<'a> Checker<'a> { while let Some((expr, scopes, parents)) = self.deferred_lambdas.pop() { self.parent_stack = parents; self.scope_stack = scopes; - self.push_scope(Scope::new(ScopeKind::Function)); + self.push_scope(Scope::new(ScopeKind::Function(Default::default()))); if let ExprKind::Lambda { args, body } = &expr.node { self.visit_arguments(args);