diff --git a/crates/ruff/src/ast/function_type.rs b/crates/ruff/src/ast/function_type.rs index 2c79610c7429ab..ee9003be1a31b8 100644 --- a/crates/ruff/src/ast/function_type.rs +++ b/crates/ruff/src/ast/function_type.rs @@ -2,7 +2,7 @@ use rustpython_parser::ast::Expr; use crate::ast::helpers::{map_callable, to_call_path}; use crate::ast::types::{Scope, ScopeKind}; -use crate::checkers::ast::Checker; +use crate::checkers::ctx::AstContext; const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"]; const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")]; @@ -16,7 +16,7 @@ pub enum FunctionType { /// Classify a function based on its scope, name, and decorators. pub fn classify( - checker: &Checker, + ctx: &AstContext, scope: &Scope, name: &str, decorator_list: &[Expr], @@ -29,8 +29,7 @@ pub fn classify( if decorator_list.iter().any(|expr| { // The method is decorated with a static method decorator (like // `@staticmethod`). - checker - .resolve_call_path(map_callable(expr)) + ctx.resolve_call_path(map_callable(expr)) .map_or(false, |call_path| { staticmethod_decorators .iter() @@ -42,7 +41,7 @@ pub fn classify( // Special-case class method, like `__new__`. || scope.bases.iter().any(|expr| { // The class itself extends a known metaclass, so all methods are class methods. - checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| { + ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| { METACLASS_BASES .iter() .any(|(module, member)| call_path.as_slice() == [*module, *member]) @@ -50,7 +49,7 @@ pub fn classify( }) || decorator_list.iter().any(|expr| { // The method is decorated with a class method decorator (like `@classmethod`). - checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| { + ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| { classmethod_decorators .iter() .any(|decorator| call_path == to_call_path(decorator)) diff --git a/crates/ruff/src/ast/helpers.rs b/crates/ruff/src/ast/helpers.rs index 67b6b3f5dfac05..b2d280fb09239f 100644 --- a/crates/ruff/src/ast/helpers.rs +++ b/crates/ruff/src/ast/helpers.rs @@ -15,7 +15,7 @@ use smallvec::{smallvec, SmallVec}; use crate::ast::types::{Binding, BindingKind, CallPath, Range}; use crate::ast::visitor; use crate::ast::visitor::Visitor; -use crate::checkers::ast::Checker; +use crate::checkers::ctx::AstContext; use crate::source_code::{Generator, Indexer, Locator, Stylist}; /// Create an `Expr` with default location from an `ExprKind`. @@ -98,17 +98,16 @@ pub fn format_call_path(call_path: &[&str]) -> String { } /// Return `true` if the `Expr` contains a reference to `${module}.${target}`. -pub fn contains_call_path(checker: &Checker, expr: &Expr, target: &[&str]) -> bool { +pub fn contains_call_path(ctx: &AstContext, expr: &Expr, target: &[&str]) -> bool { any_over_expr(expr, &|expr| { - checker - .resolve_call_path(expr) + ctx.resolve_call_path(expr) .map_or(false, |call_path| call_path.as_slice() == target) }) } /// Return `true` if the `Expr` contains an expression that appears to include a /// side-effect (like a function call). -pub fn contains_effect(checker: &Checker, expr: &Expr) -> bool { +pub fn contains_effect(ctx: &AstContext, expr: &Expr) -> bool { any_over_expr(expr, &|expr| { // Accept empty initializers. if let ExprKind::Call { @@ -124,7 +123,7 @@ pub fn contains_effect(checker: &Checker, expr: &Expr) -> bool { || id == "tuple" || id == "dict" || id == "frozenset") - && checker.is_builtin(id); + && ctx.is_builtin(id); return !is_empty_initializer; } } @@ -669,10 +668,10 @@ pub fn has_comments_in(range: Range, locator: &Locator) -> bool { } /// Return `true` if the body uses `locals()`, `globals()`, `vars()`, `eval()`. -pub fn uses_magic_variable_access(checker: &Checker, body: &[Stmt]) -> bool { +pub fn uses_magic_variable_access(ctx: &AstContext, body: &[Stmt]) -> bool { any_over_body(body, &|expr| { if let ExprKind::Call { func, .. } = &expr.node { - checker.resolve_call_path(func).map_or(false, |call_path| { + ctx.resolve_call_path(func).map_or(false, |call_path| { call_path.as_slice() == ["", "locals"] || call_path.as_slice() == ["", "globals"] || call_path.as_slice() == ["", "vars"] diff --git a/crates/ruff/src/ast/operations.rs b/crates/ruff/src/ast/operations.rs index 15629d7548ec24..ce274bcadc6aa9 100644 --- a/crates/ruff/src/ast/operations.rs +++ b/crates/ruff/src/ast/operations.rs @@ -7,7 +7,7 @@ use crate::ast::helpers::any_over_expr; use crate::ast::types::{BindingKind, Scope}; use crate::ast::visitor; use crate::ast::visitor::Visitor; -use crate::checkers::ast::Checker; +use crate::checkers::ctx::AstContext; bitflags! { #[derive(Default)] @@ -19,7 +19,7 @@ bitflags! { /// Extract the names bound to a given __all__ assignment. pub fn extract_all_names( - checker: &Checker, + ctx: &AstContext, stmt: &Stmt, scope: &Scope, ) -> (Vec, AllNamesFlags) { @@ -38,7 +38,7 @@ pub fn extract_all_names( } fn extract_elts<'a>( - checker: &'a Checker, + ctx: &'a AstContext, expr: &'a Expr, ) -> (Option<&'a Vec>, AllNamesFlags) { match &expr.node { @@ -60,7 +60,7 @@ pub fn extract_all_names( } => { // Allow `tuple()` and `list()` calls. if keywords.is_empty() && args.len() <= 1 { - if checker.resolve_call_path(func).map_or(false, |call_path| { + if ctx.resolve_call_path(func).map_or(false, |call_path| { call_path.as_slice() == ["", "tuple"] || call_path.as_slice() == ["", "list"] }) { @@ -96,7 +96,7 @@ pub fn extract_all_names( // Grab the existing bound __all__ values. if let StmtKind::AugAssign { .. } = &stmt.node { if let Some(index) = scope.bindings.get("__all__") { - if let BindingKind::Export(existing) = &checker.bindings[*index].kind { + if let BindingKind::Export(existing) = &ctx.bindings[*index].kind { names.extend_from_slice(existing); } } @@ -113,7 +113,7 @@ pub fn extract_all_names( let mut current_right = right; loop { // Process the right side, which should be a "real" value. - let (elts, new_flags) = extract_elts(checker, current_right); + let (elts, new_flags) = extract_elts(ctx, current_right); flags |= new_flags; if let Some(elts) = elts { add_to_names(&mut names, elts, &mut flags); @@ -125,7 +125,7 @@ pub fn extract_all_names( current_left = left; current_right = right; } else { - let (elts, new_flags) = extract_elts(checker, current_left); + let (elts, new_flags) = extract_elts(ctx, current_left); flags |= new_flags; if let Some(elts) = elts { add_to_names(&mut names, elts, &mut flags); @@ -134,7 +134,7 @@ pub fn extract_all_names( } } } else { - let (elts, new_flags) = extract_elts(checker, value); + let (elts, new_flags) = extract_elts(ctx, value); flags |= new_flags; if let Some(elts) = elts { add_to_names(&mut names, elts, &mut flags); diff --git a/crates/ruff/src/checkers/ast.rs b/crates/ruff/src/checkers/ast.rs index 426000ce2b1af0..e14cebbae45866 100644 --- a/crates/ruff/src/checkers/ast.rs +++ b/crates/ruff/src/checkers/ast.rs @@ -6,8 +6,6 @@ use std::path::Path; use itertools::Itertools; use log::error; use nohash_hasher::IntMap; -use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS}; -use ruff_python::typing::TYPING_EXTENSIONS; use rustc_hash::{FxHashMap, FxHashSet}; use rustpython_common::cformat::{CFormatError, CFormatErrorType}; use rustpython_parser as parser; @@ -16,20 +14,20 @@ use rustpython_parser::ast::{ ExprKind, KeywordData, Located, Location, Operator, Pattern, PatternKind, Stmt, StmtKind, Suite, }; -use smallvec::smallvec; -use crate::ast::helpers::{ - binding_range, collect_call_path, extract_handler_names, from_relative_import, to_module_path, -}; +use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS}; + +use crate::ast::helpers::{binding_range, extract_handler_names, to_module_path}; use crate::ast::operations::{extract_all_names, AllNamesFlags}; use crate::ast::relocate::relocate_expr; use crate::ast::types::{ - Binding, BindingKind, CallPath, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range, + Binding, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range, RefEquality, Scope, ScopeKind, }; use crate::ast::typing::{match_annotated_subscript, Callable, SubscriptKind}; use crate::ast::visitor::{walk_excepthandler, walk_pattern, Visitor}; use crate::ast::{branch_detection, cast, helpers, operations, typing, visitor}; +use crate::checkers::ctx::AstContext; use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable}; use crate::registry::{Diagnostic, Rule}; use crate::resolver::is_interface_definition_path; @@ -55,7 +53,7 @@ type AnnotationContext = (bool, bool); #[allow(clippy::struct_excessive_bools)] pub struct Checker<'a> { - // Input data. + // Settings, static metadata, etc. pub(crate) path: &'a Path, module_path: Option>, package: Option<&'a Path>, @@ -67,51 +65,21 @@ pub struct Checker<'a> { pub(crate) locator: &'a Locator<'a>, pub(crate) stylist: &'a Stylist<'a>, pub(crate) indexer: &'a Indexer, - // Computed diagnostics. + // Stateful fields. + pub(crate) ctx: AstContext<'a>, pub(crate) diagnostics: Vec, - // Function and class definition tracking (e.g., for docstring enforcement). - definitions: Vec<(Definition<'a>, Visibility, DeferralContext<'a>)>, - // Edit tracking. // TODO(charlie): Instead of exposing deletions, wrap in a public API. pub(crate) deletions: FxHashSet>, - // Retain all scopes and parent nodes, along with a stack of indexes to track which are active - // at various points in time. - pub(crate) parents: Vec>, - pub(crate) depths: FxHashMap, usize>, - pub(crate) child_to_parent: FxHashMap, RefEquality<'a, Stmt>>, - // A stack of all bindings created in any scope, at any point in execution. - pub(crate) bindings: Vec>, - // Map from binding index to indexes of bindings that redefine it in other scopes. - pub(crate) redefinitions: IntMap>, - pub(crate) exprs: Vec>, - pub(crate) scopes: Vec>, - pub(crate) scope_stack: Vec, - pub(crate) dead_scopes: Vec<(usize, Vec)>, - deferred_string_type_definitions: Vec<(Range, &'a str, AnnotationContext, DeferralContext<'a>)>, - deferred_type_definitions: Vec<(&'a Expr, AnnotationContext, DeferralContext<'a>)>, - deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>, - deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>, - deferred_for_loops: Vec<(&'a Stmt, DeferralContext<'a>)>, - deferred_assignments: Vec>, - // Body iteration; used to peek at siblings. - body: &'a [Stmt], - body_index: usize, - // Internal, derivative state. - visible_scope: VisibleScope, - in_annotation: bool, - in_type_definition: bool, - in_deferred_string_type_definition: bool, - in_deferred_type_definition: bool, - in_exception_handler: bool, - in_literal: bool, - in_subscript: bool, - in_type_checking_block: bool, - pub(crate) seen_import_boundary: bool, - futures_allowed: bool, - annotations_future_enabled: bool, - except_handlers: Vec>>, - // Check-specific state. pub(crate) flake8_bugbear_seen: Vec<&'a Expr>, + // Traversal state. + pub definitions: Vec<(Definition<'a>, Visibility, DeferralContext<'a>)>, + pub deferred_string_type_definitions: + Vec<(Range, &'a str, AnnotationContext, DeferralContext<'a>)>, + pub deferred_type_definitions: Vec<(&'a Expr, AnnotationContext, DeferralContext<'a>)>, + pub deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>, + pub deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>, + pub deferred_for_loops: Vec<(&'a Stmt, DeferralContext<'a>)>, + pub deferred_assignments: Vec>, } impl<'a> Checker<'a> { @@ -136,163 +104,66 @@ impl<'a> Checker<'a> { noqa, path, package, - module_path, + module_path: module_path.clone(), is_interface_definition, locator, stylist: style, indexer, + ctx: AstContext { + settings, + module_path, + parents: vec![], + depths: FxHashMap::default(), + child_to_parent: FxHashMap::default(), + bindings: vec![], + redefinitions: IntMap::default(), + exprs: vec![], + scopes: vec![], + scope_stack: vec![], + dead_scopes: vec![], + body: &[], + body_index: 0, + visible_scope: VisibleScope { + modifier: Modifier::Module, + visibility: module_visibility(path), + }, + in_annotation: false, + in_type_definition: false, + in_deferred_string_type_definition: false, + in_deferred_type_definition: false, + in_exception_handler: false, + in_literal: false, + in_subscript: false, + in_type_checking_block: false, + seen_import_boundary: false, + futures_allowed: true, + annotations_future_enabled: is_interface_definition, + except_handlers: vec![], + }, + // Computed diagnostics. diagnostics: vec![], - definitions: vec![], deletions: FxHashSet::default(), - parents: vec![], - depths: FxHashMap::default(), - child_to_parent: FxHashMap::default(), - bindings: vec![], - redefinitions: IntMap::default(), - exprs: vec![], - scopes: vec![], - scope_stack: vec![], - dead_scopes: vec![], + // Traversal + definitions: vec![], deferred_string_type_definitions: vec![], deferred_type_definitions: vec![], deferred_functions: vec![], deferred_lambdas: vec![], deferred_for_loops: vec![], deferred_assignments: vec![], - // Body iteration. - body: &[], - body_index: 0, - // Internal, derivative state. - visible_scope: VisibleScope { - modifier: Modifier::Module, - visibility: module_visibility(path), - }, - in_annotation: false, - in_type_definition: false, - in_deferred_string_type_definition: false, - in_deferred_type_definition: false, - in_exception_handler: false, - in_literal: false, - in_subscript: false, - in_type_checking_block: false, - seen_import_boundary: false, - futures_allowed: true, - annotations_future_enabled: is_interface_definition, - except_handlers: vec![], // Check-specific state. flake8_bugbear_seen: vec![], } } +} +impl<'a> Checker<'a> { /// Return `true` if a patch should be generated under the given autofix /// `Mode`. pub fn patch(&self, code: &Rule) -> bool { self.autofix.into() && self.settings.rules.should_fix(code) } - /// Return `true` if the `Expr` is a reference to `typing.${target}`. - pub fn match_typing_expr(&self, expr: &Expr, target: &str) -> bool { - self.resolve_call_path(expr).map_or(false, |call_path| { - self.match_typing_call_path(&call_path, target) - }) - } - - /// Return `true` if the call path is a reference to `typing.${target}`. - pub fn match_typing_call_path(&self, call_path: &CallPath, target: &str) -> bool { - if call_path.as_slice() == ["typing", target] { - return true; - } - - if TYPING_EXTENSIONS.contains(target) { - if call_path.as_slice() == ["typing_extensions", target] { - return true; - } - } - - if self.settings.typing_modules.iter().any(|module| { - let mut module: CallPath = module.split('.').collect(); - module.push(target); - *call_path == module - }) { - return true; - } - - false - } - - /// Return the current `Binding` for a given `name`. - pub fn find_binding(&self, member: &str) -> Option<&Binding> { - self.current_scopes() - .find_map(|scope| scope.bindings.get(member)) - .map(|index| &self.bindings[*index]) - } - - /// Return `true` if `member` is bound as a builtin. - pub fn is_builtin(&self, member: &str) -> bool { - self.find_binding(member) - .map_or(false, |binding| binding.kind.is_builtin()) - } - - /// Resolves the call path, e.g. if you have a file - /// - /// ```python - /// from sys import version_info as python_version - /// print(python_version) - /// ``` - /// - /// then `python_version` from the print statement will resolve to `sys.version_info`. - pub fn resolve_call_path<'b>(&'a self, value: &'b Expr) -> Option> - where - 'b: 'a, - { - let call_path = collect_call_path(value); - let Some(head) = call_path.first() else { - return None; - }; - let Some(binding) = self.find_binding(head) else { - return None; - }; - match &binding.kind { - BindingKind::Importation(.., name) | BindingKind::SubmoduleImportation(name, ..) => { - if name.starts_with('.') { - if let Some(module) = &self.module_path { - let mut source_path = from_relative_import(module, name); - source_path.extend(call_path.into_iter().skip(1)); - Some(source_path) - } else { - None - } - } else { - let mut source_path: CallPath = name.split('.').collect(); - source_path.extend(call_path.into_iter().skip(1)); - Some(source_path) - } - } - BindingKind::FromImportation(.., name) => { - if name.starts_with('.') { - if let Some(module) = &self.module_path { - let mut source_path = from_relative_import(module, name); - source_path.extend(call_path.into_iter().skip(1)); - Some(source_path) - } else { - None - } - } else { - let mut source_path: CallPath = name.split('.').collect(); - source_path.extend(call_path.into_iter().skip(1)); - Some(source_path) - } - } - BindingKind::Builtin => { - let mut source_path: CallPath = smallvec![]; - source_path.push(""); - source_path.extend(call_path); - Some(source_path) - } - _ => None, - } - } - /// Return `true` if a `Rule` is disabled by a `noqa` directive. pub fn rule_is_ignored(&self, code: &Rule, lineno: usize) -> bool { // TODO(charlie): `noqa` directives are mostly enforced in `check_lines.rs`. @@ -314,32 +185,30 @@ where 'b: 'a, { fn visit_stmt(&mut self, stmt: &'b Stmt) { - self.push_parent(stmt); + self.ctx.push_parent(stmt); // Track whether we've seen docstrings, non-imports, etc. match &stmt.node { StmtKind::ImportFrom { module, .. } => { // Allow __future__ imports until we see a non-__future__ import. - if self.futures_allowed { + if self.ctx.futures_allowed { if let Some(module) = module { if module != "__future__" { - self.futures_allowed = false; + self.ctx.futures_allowed = false; } } } } StmtKind::Import { .. } => { - self.futures_allowed = false; + self.ctx.futures_allowed = false; } _ => { - self.futures_allowed = false; - if !self.seen_import_boundary + self.ctx.futures_allowed = false; + if !self.ctx.seen_import_boundary && !helpers::is_assignment_to_a_dunder(stmt) - && !operations::in_nested_block( - self.parents.iter().rev().map(std::convert::Into::into), - ) + && !operations::in_nested_block(self.ctx.parents.iter().rev().map(Into::into)) { - self.seen_import_boundary = true; + self.ctx.seen_import_boundary = true; } } } @@ -347,16 +216,16 @@ where // Pre-visit. match &stmt.node { StmtKind::Global { names } => { - let scope_index = *self.scope_stack.last().expect("No current scope found"); + let scope_index = *self.ctx.scope_stack.last().expect("No current scope found"); let ranges: Vec = helpers::find_names(stmt, self.locator).collect(); if scope_index != GLOBAL_SCOPE_INDEX { // Add the binding to the current scope. - let context = self.execution_context(); - let scope = &mut self.scopes[scope_index]; + let context = self.ctx.execution_context(); + let scope = &mut self.ctx.scopes[scope_index]; let usage = Some((scope.id, Range::from_located(stmt))); for (name, range) in names.iter().zip(ranges.iter()) { - let index = self.bindings.len(); - self.bindings.push(Binding { + let index = self.ctx.bindings.len(); + self.ctx.bindings.push(Binding { kind: BindingKind::Global, runtime_usage: None, synthetic_usage: usage, @@ -377,16 +246,16 @@ where } } StmtKind::Nonlocal { names } => { - let scope_index = *self.scope_stack.last().expect("No current scope found"); + let scope_index = *self.ctx.scope_stack.last().expect("No current scope found"); let ranges: Vec = helpers::find_names(stmt, self.locator).collect(); if scope_index != GLOBAL_SCOPE_INDEX { - let context = self.execution_context(); - let scope = &mut self.scopes[scope_index]; + let context = self.ctx.execution_context(); + let scope = &mut self.ctx.scopes[scope_index]; let usage = Some((scope.id, Range::from_located(stmt))); for (name, range) in names.iter().zip(ranges.iter()) { // Add a binding to the current scope. - let index = self.bindings.len(); - self.bindings.push(Binding { + let index = self.ctx.bindings.len(); + self.ctx.bindings.push(Binding { kind: BindingKind::Nonlocal, runtime_usage: None, synthetic_usage: usage, @@ -402,10 +271,12 @@ where // and the current scope.) for (name, range) in names.iter().zip(ranges.iter()) { let mut exists = false; - for index in self.scope_stack.iter().skip(1).rev().skip(1) { - if let Some(index) = self.scopes[*index].bindings.get(&name.as_str()) { + for index in self.ctx.scope_stack.iter().skip(1).rev().skip(1) { + if let Some(index) = + self.ctx.scopes[*index].bindings.get(&name.as_str()) + { exists = true; - self.bindings[*index].runtime_usage = usage; + self.ctx.bindings[*index].runtime_usage = usage; } } @@ -434,12 +305,7 @@ where if self.settings.rules.enabled(&Rule::BreakOutsideLoop) { if let Some(diagnostic) = pyflakes::rules::break_outside_loop( stmt, - &mut self - .parents - .iter() - .rev() - .map(std::convert::Into::into) - .skip(1), + &mut self.ctx.parents.iter().rev().map(Into::into).skip(1), ) { self.diagnostics.push(diagnostic); } @@ -449,12 +315,7 @@ where if self.settings.rules.enabled(&Rule::ContinueOutsideLoop) { if let Some(diagnostic) = pyflakes::rules::continue_outside_loop( stmt, - &mut self - .parents - .iter() - .rev() - .map(std::convert::Into::into) - .skip(1), + &mut self.ctx.parents.iter().rev().map(Into::into).skip(1), ) { self.diagnostics.push(diagnostic); } @@ -484,7 +345,7 @@ where self.diagnostics .extend(flake8_django::rules::non_leading_receiver_decorator( decorator_list, - |expr| self.resolve_call_path(expr), + |expr| self.ctx.resolve_call_path(expr), )); } if self.settings.rules.enabled(&Rule::AmbiguousFunctionName) { @@ -516,7 +377,7 @@ where if let Some(diagnostic) = pep8_naming::rules::invalid_first_argument_name_for_class_method( self, - self.current_scope(), + self.ctx.current_scope(), name, decorator_list, args, @@ -534,7 +395,7 @@ where if let Some(diagnostic) = pep8_naming::rules::invalid_first_argument_name_for_method( self, - self.current_scope(), + self.ctx.current_scope(), name, decorator_list, args, @@ -555,7 +416,7 @@ where if self.settings.rules.enabled(&Rule::DunderFunctionName) { if let Some(diagnostic) = pep8_naming::rules::dunder_function_name( - self.current_scope(), + self.ctx.current_scope(), stmt, name, self.locator, @@ -789,7 +650,7 @@ where self.visit_expr(expr); } - let context = self.execution_context(); + let context = self.ctx.execution_context(); self.add_binding( name, Binding { @@ -798,7 +659,7 @@ where synthetic_usage: None, typing_usage: None, range: Range::from_located(stmt), - source: Some(self.current_stmt().clone()), + source: Some(self.ctx.current_stmt().clone()), context, }, ); @@ -980,7 +841,8 @@ where runtime_usage: None, // Always mark `__future__` imports as used. synthetic_usage: Some(( - self.scopes[*(self + self.ctx.scopes[*(self + .ctx .scope_stack .last() .expect("No current scope found"))] @@ -989,13 +851,13 @@ where )), typing_usage: None, range: Range::from_located(alias), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); if self.settings.rules.enabled(&Rule::LateFutureImport) - && !self.futures_allowed + && !self.ctx.futures_allowed { self.diagnostics.push(Diagnostic::new( pyflakes::rules::LateFutureImport, @@ -1015,8 +877,8 @@ where synthetic_usage: None, typing_usage: None, range: Range::from_located(alias), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); } else { @@ -1039,7 +901,8 @@ where .map_or(false, |asname| asname == &alias.node.name) { Some(( - self.scopes[*(self + self.ctx.scopes[*(self + .ctx .scope_stack .last() .expect("No current scope found"))] @@ -1051,8 +914,8 @@ where }, typing_usage: None, range: Range::from_located(alias), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); @@ -1303,7 +1166,8 @@ where runtime_usage: None, // Always mark `__future__` imports as used. synthetic_usage: Some(( - self.scopes[*(self + self.ctx.scopes[*(self + .ctx .scope_stack .last() .expect("No current scope found"))] @@ -1312,13 +1176,13 @@ where )), typing_usage: None, range: Range::from_located(alias), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); if alias.node.name == "annotations" { - self.annotations_future_enabled = true; + self.ctx.annotations_future_enabled = true; } if self.settings.rules.enabled(&Rule::FutureFeatureNotDefined) { @@ -1326,7 +1190,7 @@ where } if self.settings.rules.enabled(&Rule::LateFutureImport) - && !self.futures_allowed + && !self.ctx.futures_allowed { self.diagnostics.push(Diagnostic::new( pyflakes::rules::LateFutureImport, @@ -1342,14 +1206,14 @@ where synthetic_usage: None, typing_usage: None, range: Range::from_located(stmt), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); if self.settings.rules.enabled(&Rule::ImportStarNotPermitted) { - let scope = &self.scopes - [*(self.scope_stack.last().expect("No current scope found"))]; + let scope = &self.ctx.scopes + [*(self.ctx.scope_stack.last().expect("No current scope found"))]; if !matches!(scope.kind, ScopeKind::Module) { self.diagnostics.push(Diagnostic::new( pyflakes::rules::ImportStarNotPermitted { @@ -1375,8 +1239,8 @@ where )); } - let scope = &mut self.scopes - [*(self.scope_stack.last().expect("No current scope found"))]; + let scope = &mut self.ctx.scopes + [*(self.ctx.scope_stack.last().expect("No current scope found"))]; scope.import_starred = true; } else { if let Some(asname) = &alias.node.asname { @@ -1407,7 +1271,8 @@ where .map_or(false, |asname| asname == &alias.node.name) { Some(( - self.scopes[*(self + self.ctx.scopes[*(self + .ctx .scope_stack .last() .expect("No current scope found"))] @@ -1419,8 +1284,8 @@ where }, typing_usage: None, range, - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); } @@ -1630,14 +1495,14 @@ where test, body, orelse, - self.current_stmt_parent().map(Into::into), + self.ctx.current_stmt_parent().map(Into::into), ); } if self.settings.rules.enabled(&Rule::IfWithSameArms) { flake8_simplify::rules::if_with_same_arms( self, stmt, - self.current_stmt_parent().map(std::convert::Into::into), + self.ctx.current_stmt_parent().map(Into::into), ); } if self.settings.rules.enabled(&Rule::NeedlessBool) { @@ -1650,14 +1515,14 @@ where test, body, orelse, - self.current_stmt_parent().map(std::convert::Into::into), + self.ctx.current_stmt_parent().map(Into::into), ); } if self.settings.rules.enabled(&Rule::UseTernaryOperator) { flake8_simplify::rules::use_ternary_operator( self, stmt, - self.current_stmt_parent().map(std::convert::Into::into), + self.ctx.current_stmt_parent().map(Into::into), ); } if self.settings.rules.enabled(&Rule::DictGetWithDefault) { @@ -1667,7 +1532,7 @@ where test, body, orelse, - self.current_stmt_parent().map(std::convert::Into::into), + self.ctx.current_stmt_parent().map(Into::into), ); } if self.settings.rules.enabled(&Rule::PreferTypeError) { @@ -1676,7 +1541,7 @@ where body, test, orelse, - self.current_stmt_parent().map(Into::into), + self.ctx.current_stmt_parent().map(Into::into), ); } if self.settings.rules.enabled(&Rule::OutdatedVersionBlock) { @@ -1731,7 +1596,7 @@ where self, stmt, body, - self.current_stmt_parent().map(Into::into), + self.ctx.current_stmt_parent().map(Into::into), ); } if self.settings.rules.enabled(&Rule::RedefinedLoopName) { @@ -1765,8 +1630,10 @@ where .rules .enabled(&Rule::UnusedLoopControlVariable) { - self.deferred_for_loops - .push((stmt, (self.scope_stack.clone(), self.parents.clone()))); + self.deferred_for_loops.push(( + stmt, + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), + )); } if self .settings @@ -1789,7 +1656,7 @@ where flake8_simplify::rules::convert_for_loop_to_any_all( self, stmt, - self.current_sibling_stmt(), + self.ctx.current_sibling_stmt(), ); } if self.settings.rules.enabled(&Rule::KeyInDict) { @@ -1975,7 +1842,7 @@ where } if self.settings.rules.enabled(&Rule::AsyncioDanglingTask) { if let Some(diagnostic) = ruff::rules::asyncio_dangling_task(value, |expr| { - self.resolve_call_path(expr) + self.ctx.resolve_call_path(expr) }) { self.diagnostics.push(diagnostic); } @@ -1985,8 +1852,8 @@ where } // Recurse. - let prev_in_exception_handler = self.in_exception_handler; - let prev_visible_scope = self.visible_scope.clone(); + let prev_in_exception_handler = self.ctx.in_exception_handler; + let prev_visible_scope = self.ctx.visible_scope.clone(); match &stmt.node { StmtKind::FunctionDef { body, @@ -2006,7 +1873,7 @@ where flake8_bugbear::rules::f_string_docstring(self, body); } let definition = docstrings::extraction::extract( - &self.visible_scope, + &self.ctx.visible_scope, stmt, body, &Documentable::Function, @@ -2014,49 +1881,53 @@ where if self.settings.rules.enabled(&Rule::RewriteYieldFrom) { pyupgrade::rules::rewrite_yield_from(self, stmt); } - let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Function); + let scope = + transition_scope(&self.ctx.visible_scope, stmt, &Documentable::Function); self.definitions.push(( definition, scope.visibility.clone(), - (self.scope_stack.clone(), self.parents.clone()), + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), )); - self.visible_scope = scope; + self.ctx.visible_scope = scope; // If any global bindings don't already exist in the global scope, add it. let globals = operations::extract_globals(body); for (name, stmt) in operations::extract_globals(body) { - if self.scopes[GLOBAL_SCOPE_INDEX] + if self.ctx.scopes[GLOBAL_SCOPE_INDEX] .bindings .get(name) - .map_or(true, |index| self.bindings[*index].kind.is_annotation()) + .map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation()) { - let index = self.bindings.len(); - self.bindings.push(Binding { + let index = self.ctx.bindings.len(); + self.ctx.bindings.push(Binding { kind: BindingKind::Assignment, runtime_usage: None, synthetic_usage: None, typing_usage: None, range: Range::from_located(stmt), source: Some(RefEquality(stmt)), - context: self.execution_context(), + context: self.ctx.execution_context(), }); - self.scopes[GLOBAL_SCOPE_INDEX].bindings.insert(name, index); + self.ctx.scopes[GLOBAL_SCOPE_INDEX] + .bindings + .insert(name, index); } } - self.push_scope(Scope::new(ScopeKind::Function(FunctionDef { - name, - body, - args, - decorator_list, - async_: matches!(stmt.node, StmtKind::AsyncFunctionDef { .. }), - globals, - }))); + self.ctx + .push_scope(Scope::new(ScopeKind::Function(FunctionDef { + name, + body, + args, + decorator_list, + async_: matches!(stmt.node, StmtKind::AsyncFunctionDef { .. }), + globals, + }))); self.deferred_functions.push(( stmt, - (self.scope_stack.clone(), self.parents.clone()), - self.visible_scope.clone(), + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), + self.ctx.visible_scope.clone(), )); } StmtKind::ClassDef { @@ -2071,42 +1942,44 @@ where flake8_bugbear::rules::f_string_docstring(self, body); } let definition = docstrings::extraction::extract( - &self.visible_scope, + &self.ctx.visible_scope, stmt, body, &Documentable::Class, ); - let scope = transition_scope(&self.visible_scope, stmt, &Documentable::Class); + let scope = transition_scope(&self.ctx.visible_scope, stmt, &Documentable::Class); self.definitions.push(( definition, scope.visibility.clone(), - (self.scope_stack.clone(), self.parents.clone()), + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), )); - self.visible_scope = scope; + self.ctx.visible_scope = scope; // If any global bindings don't already exist in the global scope, add it. let globals = operations::extract_globals(body); for (name, stmt) in &globals { - if self.scopes[GLOBAL_SCOPE_INDEX] + if self.ctx.scopes[GLOBAL_SCOPE_INDEX] .bindings .get(name) - .map_or(true, |index| self.bindings[*index].kind.is_annotation()) + .map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation()) { - let index = self.bindings.len(); - self.bindings.push(Binding { + let index = self.ctx.bindings.len(); + self.ctx.bindings.push(Binding { kind: BindingKind::Assignment, runtime_usage: None, synthetic_usage: None, typing_usage: None, range: Range::from_located(stmt), source: Some(RefEquality(stmt)), - context: self.execution_context(), + context: self.ctx.execution_context(), }); - self.scopes[GLOBAL_SCOPE_INDEX].bindings.insert(name, index); + self.ctx.scopes[GLOBAL_SCOPE_INDEX] + .bindings + .insert(name, index); } } - self.push_scope(Scope::new(ScopeKind::Class(ClassDef { + self.ctx.push_scope(Scope::new(ScopeKind::Class(ClassDef { name, bases, keywords, @@ -2133,18 +2006,18 @@ where .into_iter() .map(|call_path| call_path.to_vec()) .collect(); - self.except_handlers.push(handler_names); + self.ctx.except_handlers.push(handler_names); if self.settings.rules.enabled(&Rule::JumpStatementInFinally) { flake8_bugbear::rules::jump_statement_in_finally(self, finalbody); } self.visit_body(body); - self.except_handlers.pop(); + self.ctx.except_handlers.pop(); - self.in_exception_handler = true; + self.ctx.in_exception_handler = true; for excepthandler in handlers { self.visit_excepthandler(excepthandler); } - self.in_exception_handler = prev_in_exception_handler; + self.ctx.in_exception_handler = prev_in_exception_handler; self.visit_body(orelse); self.visit_body(finalbody); @@ -2158,23 +2031,23 @@ where // If we're in a class or module scope, then the annotation needs to be // available at runtime. // See: https://docs.python.org/3/reference/simple_stmts.html#annotated-assignment-statements - if !self.annotations_future_enabled + if !self.ctx.annotations_future_enabled && matches!( - self.current_scope().kind, + self.ctx.current_scope().kind, ScopeKind::Class(..) | ScopeKind::Module ) { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(annotation); - self.in_type_definition = false; + self.ctx.in_type_definition = false; } else { self.visit_annotation(annotation); } if let Some(expr) = value { - if self.match_typing_expr(annotation, "TypeAlias") { - self.in_type_definition = true; + if self.ctx.match_typing_expr(annotation, "TypeAlias") { + self.ctx.in_type_definition = true; self.visit_expr(expr); - self.in_type_definition = false; + self.ctx.in_type_definition = false; } else { self.visit_expr(expr); } @@ -2189,10 +2062,10 @@ where flake8_type_checking::rules::empty_type_checking_block(self, stmt, body); } - let prev_in_type_checking_block = self.in_type_checking_block; - self.in_type_checking_block = true; + let prev_in_type_checking_block = self.ctx.in_type_checking_block; + self.ctx.in_type_checking_block = true; self.visit_body(body); - self.in_type_checking_block = prev_in_type_checking_block; + self.ctx.in_type_checking_block = prev_in_type_checking_block; } else { self.visit_body(body); } @@ -2201,15 +2074,15 @@ where } _ => visitor::walk_stmt(self, stmt), }; - self.visible_scope = prev_visible_scope; + self.ctx.visible_scope = prev_visible_scope; // Post-visit. match &stmt.node { StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => { - self.pop_scope(); + self.ctx.pop_scope(); } StmtKind::ClassDef { name, .. } => { - self.pop_scope(); + self.ctx.pop_scope(); self.add_binding( name, Binding { @@ -2218,31 +2091,31 @@ where synthetic_usage: None, typing_usage: None, range: Range::from_located(stmt), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); } _ => {} } - self.pop_parent(); + self.ctx.pop_parent(); } fn visit_annotation(&mut self, expr: &'b Expr) { - let prev_in_annotation = self.in_annotation; - let prev_in_type_definition = self.in_type_definition; - self.in_annotation = true; - self.in_type_definition = true; + let prev_in_annotation = self.ctx.in_annotation; + let prev_in_type_definition = self.ctx.in_type_definition; + self.ctx.in_annotation = true; + self.ctx.in_type_definition = true; self.visit_expr(expr); - self.in_annotation = prev_in_annotation; - self.in_type_definition = prev_in_type_definition; + self.ctx.in_annotation = prev_in_annotation; + self.ctx.in_type_definition = prev_in_type_definition; } fn visit_expr(&mut self, expr: &'b Expr) { - if !(self.in_deferred_type_definition || self.in_deferred_string_type_definition) - && self.in_type_definition - && self.annotations_future_enabled + if !(self.ctx.in_deferred_type_definition || self.ctx.in_deferred_string_type_definition) + && self.ctx.in_type_definition + && self.ctx.annotations_future_enabled { if let ExprKind::Constant { value: Constant::Str(value), @@ -2252,42 +2125,42 @@ where self.deferred_string_type_definitions.push(( Range::from_located(expr), value, - (self.in_annotation, self.in_type_checking_block), - (self.scope_stack.clone(), self.parents.clone()), + (self.ctx.in_annotation, self.ctx.in_type_checking_block), + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), )); } else { self.deferred_type_definitions.push(( expr, - (self.in_annotation, self.in_type_checking_block), - (self.scope_stack.clone(), self.parents.clone()), + (self.ctx.in_annotation, self.ctx.in_type_checking_block), + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), )); } return; } - self.push_expr(expr); + self.ctx.push_expr(expr); - let prev_in_literal = self.in_literal; - let prev_in_type_definition = self.in_type_definition; + let prev_in_literal = self.ctx.in_literal; + let prev_in_type_definition = self.ctx.in_type_definition; // Pre-visit. match &expr.node { ExprKind::Subscript { value, slice, .. } => { // Ex) Optional[...], Union[...] - if self.in_type_definition - && !self.in_deferred_string_type_definition + if self.ctx.in_type_definition + && !self.ctx.in_deferred_string_type_definition && !self.settings.pyupgrade.keep_runtime_typing && self.settings.rules.enabled(&Rule::TypingUnion) && (self.settings.target_version >= PythonVersion::Py310 || (self.settings.target_version >= PythonVersion::Py37 - && self.annotations_future_enabled - && self.in_annotation)) + && self.ctx.annotations_future_enabled + && self.ctx.in_annotation)) { pyupgrade::rules::use_pep604_annotation(self, expr, value, slice); } - if self.match_typing_expr(value, "Literal") { - self.in_literal = true; + if self.ctx.match_typing_expr(value, "Literal") { + self.ctx.in_literal = true; } if self @@ -2333,14 +2206,16 @@ where } // Ex) List[...] - if !self.in_deferred_string_type_definition + if !self.ctx.in_deferred_string_type_definition && !self.settings.pyupgrade.keep_runtime_typing && self.settings.rules.enabled(&Rule::DeprecatedCollectionType) && (self.settings.target_version >= PythonVersion::Py39 || (self.settings.target_version >= PythonVersion::Py37 - && self.annotations_future_enabled - && self.in_annotation)) - && typing::is_pep585_builtin(expr, |expr| self.resolve_call_path(expr)) + && self.ctx.annotations_future_enabled + && self.ctx.in_annotation)) + && typing::is_pep585_builtin(expr, |expr| { + self.ctx.resolve_call_path(expr) + }) { pyupgrade::rules::use_pep585_annotation(self, expr); } @@ -2378,14 +2253,14 @@ where } ExprKind::Attribute { attr, value, .. } => { // Ex) typing.List[...] - if !self.in_deferred_string_type_definition + if !self.ctx.in_deferred_string_type_definition && !self.settings.pyupgrade.keep_runtime_typing && self.settings.rules.enabled(&Rule::DeprecatedCollectionType) && (self.settings.target_version >= PythonVersion::Py39 || (self.settings.target_version >= PythonVersion::Py37 - && self.annotations_future_enabled - && self.in_annotation)) - && typing::is_pep585_builtin(expr, |expr| self.resolve_call_path(expr)) + && self.ctx.annotations_future_enabled + && self.ctx.in_annotation)) + && typing::is_pep585_builtin(expr, |expr| self.ctx.resolve_call_path(expr)) { pyupgrade::rules::use_pep585_annotation(self, expr); } @@ -2664,7 +2539,7 @@ where flake8_comprehensions::rules::unnecessary_generator_set( self, expr, - self.current_expr_parent().map(Into::into), + self.ctx.current_expr_parent().map(Into::into), func, args, keywords, @@ -2674,7 +2549,7 @@ where flake8_comprehensions::rules::unnecessary_generator_dict( self, expr, - self.current_expr_parent().map(Into::into), + self.ctx.current_expr_parent().map(Into::into), func, args, keywords, @@ -2774,7 +2649,7 @@ where flake8_comprehensions::rules::unnecessary_map( self, expr, - self.current_expr_parent().map(Into::into), + self.ctx.current_expr_parent().map(Into::into), func, args, ); @@ -2792,8 +2667,8 @@ where } if let ExprKind::Name { id, ctx } = &func.node { if id == "locals" && matches!(ctx, ExprContext::Load) { - let scope = &mut self.scopes - [*(self.scope_stack.last().expect("No current scope found"))]; + let scope = &mut self.ctx.scopes + [*(self.ctx.scope_stack.last().expect("No current scope found"))]; scope.uses_locals = true; } } @@ -3403,12 +3278,12 @@ where value: Constant::Str(value), kind, } => { - if self.in_type_definition && !self.in_literal { + if self.ctx.in_type_definition && !self.ctx.in_literal { self.deferred_string_type_definitions.push(( Range::from_located(expr), value, - (self.in_annotation, self.in_type_checking_block), - (self.scope_stack.clone(), self.parents.clone()), + (self.ctx.in_annotation, self.ctx.in_type_checking_block), + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), )); } if self @@ -3473,7 +3348,8 @@ where for expr in &args.defaults { self.visit_expr(expr); } - self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body }))); + self.ctx + .push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body }))); } ExprKind::IfExp { test, body, orelse } => { if self.settings.rules.enabled(&Rule::IfExprWithTrueFalse) { @@ -3499,13 +3375,13 @@ where if self.settings.rules.enabled(&Rule::FunctionUsesLoopVariable) { flake8_bugbear::rules::function_uses_loop_variable(self, &Node::Expr(expr)); } - self.push_scope(Scope::new(ScopeKind::Generator)); + self.ctx.push_scope(Scope::new(ScopeKind::Generator)); } ExprKind::GeneratorExp { .. } | ExprKind::DictComp { .. } => { if self.settings.rules.enabled(&Rule::FunctionUsesLoopVariable) { flake8_bugbear::rules::function_uses_loop_variable(self, &Node::Expr(expr)); } - self.push_scope(Scope::new(ScopeKind::Generator)); + self.ctx.push_scope(Scope::new(ScopeKind::Generator)); } ExprKind::BoolOp { op, values } => { if self @@ -3543,26 +3419,28 @@ where // Recurse. match &expr.node { ExprKind::Lambda { .. } => { - self.deferred_lambdas - .push((expr, (self.scope_stack.clone(), self.parents.clone()))); + self.deferred_lambdas.push(( + expr, + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), + )); } ExprKind::Call { func, args, keywords, } => { - let callable = self.resolve_call_path(func).and_then(|call_path| { - if self.match_typing_call_path(&call_path, "ForwardRef") { + let callable = self.ctx.resolve_call_path(func).and_then(|call_path| { + if self.ctx.match_typing_call_path(&call_path, "ForwardRef") { Some(Callable::ForwardRef) - } else if self.match_typing_call_path(&call_path, "cast") { + } else if self.ctx.match_typing_call_path(&call_path, "cast") { Some(Callable::Cast) - } else if self.match_typing_call_path(&call_path, "NewType") { + } else if self.ctx.match_typing_call_path(&call_path, "NewType") { Some(Callable::NewType) - } else if self.match_typing_call_path(&call_path, "TypeVar") { + } else if self.ctx.match_typing_call_path(&call_path, "TypeVar") { Some(Callable::TypeVar) - } else if self.match_typing_call_path(&call_path, "NamedTuple") { + } else if self.ctx.match_typing_call_path(&call_path, "NamedTuple") { Some(Callable::NamedTuple) - } else if self.match_typing_call_path(&call_path, "TypedDict") { + } else if self.ctx.match_typing_call_path(&call_path, "TypedDict") { Some(Callable::TypedDict) } else if ["Arg", "DefaultArg", "NamedArg", "DefaultNamedArg"] .iter() @@ -3577,17 +3455,17 @@ where Some(Callable::ForwardRef) => { self.visit_expr(func); for expr in args { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(expr); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } Some(Callable::Cast) => { self.visit_expr(func); if !args.is_empty() { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(&args[0]); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } for expr in args.iter().skip(1) { self.visit_expr(expr); @@ -3596,29 +3474,29 @@ where Some(Callable::NewType) => { self.visit_expr(func); for expr in args.iter().skip(1) { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(expr); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } Some(Callable::TypeVar) => { self.visit_expr(func); for expr in args.iter().skip(1) { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(expr); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } for keyword in keywords { let KeywordData { arg, value } = &keyword.node; if let Some(id) = arg { if id == "bound" { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } else { - self.in_type_definition = false; + self.ctx.in_type_definition = false; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } } @@ -3635,15 +3513,15 @@ where ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => { if elts.len() == 2 { - self.in_type_definition = false; + self.ctx.in_type_definition = false; self.visit_expr(&elts[0]); - self.in_type_definition = + self.ctx.in_type_definition = prev_in_type_definition; - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(&elts[1]); - self.in_type_definition = + self.ctx.in_type_definition = prev_in_type_definition; } } @@ -3658,9 +3536,9 @@ where // Ex) NamedTuple("a", a=int) for keyword in keywords { let KeywordData { value, .. } = &keyword.node; - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } Some(Callable::TypedDict) => { @@ -3670,14 +3548,14 @@ where if args.len() > 1 { if let ExprKind::Dict { keys, values } = &args[1].node { for key in keys.iter().flatten() { - self.in_type_definition = false; + self.ctx.in_type_definition = false; self.visit_expr(key); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } for value in values { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } } @@ -3685,9 +3563,9 @@ where // Ex) TypedDict("a", a=int) for keyword in keywords { let KeywordData { value, .. } = &keyword.node; - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } Some(Callable::MypyExtension) => { @@ -3695,33 +3573,33 @@ where if let Some(arg) = args.first() { // Ex) DefaultNamedArg(bool | None, name="some_prop_name") - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(arg); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; for arg in args.iter().skip(1) { - self.in_type_definition = false; + self.ctx.in_type_definition = false; self.visit_expr(arg); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } for keyword in keywords { let KeywordData { value, .. } = &keyword.node; - self.in_type_definition = false; + self.ctx.in_type_definition = false; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } else { // Ex) DefaultNamedArg(type="bool", name="some_prop_name") for keyword in keywords { let KeywordData { value, arg, .. } = &keyword.node; if arg.as_ref().map_or(false, |arg| arg == "type") { - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } else { - self.in_type_definition = false; + self.ctx.in_type_definition = false; self.visit_expr(value); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; } } } @@ -3736,16 +3614,16 @@ where // `obj["foo"]["bar"]`, we need to avoid treating the `obj["foo"]` // portion as an annotation, despite having `ExprContext::Load`. Thus, we track // the `ExprContext` at the top-level. - let prev_in_subscript = self.in_subscript; - if self.in_subscript { + let prev_in_subscript = self.ctx.in_subscript; + if self.ctx.in_subscript { visitor::walk_expr(self, expr); } else if matches!(ctx, ExprContext::Store | ExprContext::Del) { - self.in_subscript = true; + self.ctx.in_subscript = true; visitor::walk_expr(self, expr); } else { match match_annotated_subscript( value, - |expr| self.resolve_call_path(expr), + |expr| self.ctx.resolve_call_path(expr), self.settings.typing_modules.iter().map(String::as_str), ) { Some(subscript) => { @@ -3753,9 +3631,9 @@ where // Ex) Optional[int] SubscriptKind::AnnotatedSubscript => { self.visit_expr(value); - self.in_type_definition = true; + self.ctx.in_type_definition = true; self.visit_expr(slice); - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; self.visit_expr_context(ctx); } // Ex) Annotated[int, "Hello, world!"] @@ -3766,11 +3644,11 @@ where if let ExprKind::Tuple { elts, ctx } = &slice.node { if let Some(expr) = elts.first() { self.visit_expr(expr); - self.in_type_definition = false; + self.ctx.in_type_definition = false; for expr in elts.iter().skip(1) { self.visit_expr(expr); } - self.in_type_definition = prev_in_type_definition; + self.ctx.in_type_definition = prev_in_type_definition; self.visit_expr_context(ctx); } } else { @@ -3785,7 +3663,7 @@ where None => visitor::walk_expr(self, expr), } } - self.in_subscript = prev_in_subscript; + self.ctx.in_subscript = prev_in_subscript; } ExprKind::JoinedStr { .. } => { visitor::walk_expr(self, expr); @@ -3800,15 +3678,26 @@ where | ExprKind::ListComp { .. } | ExprKind::DictComp { .. } | ExprKind::SetComp { .. } => { - self.pop_scope(); + self.ctx.pop_scope(); } _ => {} }; - self.in_type_definition = prev_in_type_definition; - self.in_literal = prev_in_literal; + self.ctx.in_type_definition = prev_in_type_definition; + self.ctx.in_literal = prev_in_literal; - self.pop_expr(); + self.ctx.pop_expr(); + } + + fn visit_comprehension(&mut self, comprehension: &'b Comprehension) { + if self.settings.rules.enabled(&Rule::KeyInDict) { + flake8_simplify::rules::key_in_dict_for( + self, + &comprehension.target, + &comprehension.iter, + ); + } + visitor::walk_comprehension(self, comprehension); } fn visit_excepthandler(&mut self, excepthandler: &'b Excepthandler) { @@ -3884,7 +3773,12 @@ where let name_range = helpers::excepthandler_name_range(excepthandler, self.locator).unwrap(); - if self.current_scope().bindings.contains_key(&name.as_str()) { + if self + .ctx + .current_scope() + .bindings + .contains_key(&name.as_str()) + { self.handle_node_store( name, &Expr::new( @@ -3898,7 +3792,12 @@ where ); } - let definition = self.current_scope().bindings.get(&name.as_str()).copied(); + let definition = self + .ctx + .current_scope() + .bindings + .get(&name.as_str()) + .copied(); self.handle_node_store( name, &Expr::new( @@ -3914,11 +3813,11 @@ where walk_excepthandler(self, excepthandler); if let Some(index) = { - let scope = &mut self.scopes - [*(self.scope_stack.last().expect("No current scope found"))]; + let scope = &mut self.ctx.scopes + [*(self.ctx.scope_stack.last().expect("No current scope found"))]; &scope.bindings.remove(&name.as_str()) } { - if !self.bindings[*index].used() { + if !self.ctx.bindings[*index].used() { if self.settings.rules.enabled(&Rule::UnusedVariable) { let mut diagnostic = Diagnostic::new( pyflakes::rules::UnusedVariable { @@ -3949,8 +3848,8 @@ where } if let Some(index) = definition { - let scope = &mut self.scopes - [*(self.scope_stack.last().expect("No current scope found"))]; + let scope = &mut self.ctx.scopes + [*(self.ctx.scope_stack.last().expect("No current scope found"))]; scope.bindings.insert(name, index); } } @@ -3960,32 +3859,6 @@ where } } - fn visit_pattern(&mut self, pattern: &'b Pattern) { - if let PatternKind::MatchAs { - name: Some(name), .. - } - | PatternKind::MatchStar { name: Some(name) } - | PatternKind::MatchMapping { - rest: Some(name), .. - } = &pattern.node - { - self.add_binding( - name, - Binding { - kind: BindingKind::Assignment, - runtime_usage: None, - synthetic_usage: None, - typing_usage: None, - range: Range::from_located(pattern), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), - }, - ); - } - - walk_pattern(self, pattern); - } - fn visit_format_spec(&mut self, format_spec: &'b Expr) { match &format_spec.node { ExprKind::JoinedStr { values } => { @@ -3997,17 +3870,6 @@ where } } - fn visit_comprehension(&mut self, comprehension: &'b Comprehension) { - if self.settings.rules.enabled(&Rule::KeyInDict) { - flake8_simplify::rules::key_in_dict_for( - self, - &comprehension.target, - &comprehension.iter, - ); - } - visitor::walk_comprehension(self, comprehension); - } - fn visit_arguments(&mut self, arguments: &'b Arguments) { if self.settings.rules.enabled(&Rule::MutableArgumentDefault) { flake8_bugbear::rules::mutable_argument_default(self, arguments); @@ -4065,8 +3927,8 @@ where synthetic_usage: None, typing_usage: None, range: Range::from_located(arg), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); @@ -4091,165 +3953,69 @@ where self.check_builtin_arg_shadowing(&arg.node.arg, arg); } + fn visit_pattern(&mut self, pattern: &'b Pattern) { + if let PatternKind::MatchAs { + name: Some(name), .. + } + | PatternKind::MatchStar { name: Some(name) } + | PatternKind::MatchMapping { + rest: Some(name), .. + } = &pattern.node + { + self.add_binding( + name, + Binding { + kind: BindingKind::Assignment, + runtime_usage: None, + synthetic_usage: None, + typing_usage: None, + range: Range::from_located(pattern), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), + }, + ); + } + + walk_pattern(self, pattern); + } + fn visit_body(&mut self, body: &'b [Stmt]) { if self.settings.rules.enabled(&Rule::UnnecessaryPass) { flake8_pie::rules::no_unnecessary_pass(self, body); } - let prev_body = self.body; - let prev_body_index = self.body_index; - self.body = body; - self.body_index = 0; + let prev_body = self.ctx.body; + let prev_body_index = self.ctx.body_index; + self.ctx.body = body; + self.ctx.body_index = 0; for stmt in body { self.visit_stmt(stmt); - self.body_index += 1; + self.ctx.body_index += 1; } - self.body = prev_body; - self.body_index = prev_body_index; + self.ctx.body = prev_body; + self.ctx.body_index = prev_body_index; } } impl<'a> Checker<'a> { - fn push_parent(&mut self, parent: &'a Stmt) { - let num_existing = self.parents.len(); - self.parents.push(RefEquality(parent)); - self.depths - .insert(self.parents[num_existing].clone(), num_existing); - if num_existing > 0 { - self.child_to_parent.insert( - self.parents[num_existing].clone(), - self.parents[num_existing - 1].clone(), - ); - } - } - - fn pop_parent(&mut self) { - self.parents.pop().expect("Attempted to pop without parent"); - } - - fn push_expr(&mut self, expr: &'a Expr) { - self.exprs.push(RefEquality(expr)); - } - - fn pop_expr(&mut self) { - self.exprs - .pop() - .expect("Attempted to pop without expression"); - } - - fn push_scope(&mut self, scope: Scope<'a>) { - self.scope_stack.push(self.scopes.len()); - self.scopes.push(scope); - } - - fn pop_scope(&mut self) { - self.dead_scopes.push(( - self.scope_stack - .pop() - .expect("Attempted to pop without scope"), - self.scope_stack.clone(), - )); - } - - fn bind_builtins(&mut self) { - let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))]; - - for builtin in BUILTINS - .iter() - .chain(MAGIC_GLOBALS.iter()) - .copied() - .chain(self.settings.builtins.iter().map(String::as_str)) - { - let index = self.bindings.len(); - self.bindings.push(Binding { - kind: BindingKind::Builtin, - range: Range::default(), - runtime_usage: None, - synthetic_usage: Some((0, Range::default())), - typing_usage: None, - source: None, - context: ExecutionContext::Runtime, - }); - scope.bindings.insert(builtin, index); - } - } - - /// Return the current `Stmt`. - pub fn current_stmt(&self) -> &RefEquality<'a, Stmt> { - self.parents.iter().rev().next().expect("No parent found") - } - - /// Return the parent `Stmt` of the current `Stmt`, if any. - pub fn current_stmt_parent(&self) -> Option<&RefEquality<'a, Stmt>> { - self.parents.iter().rev().nth(1) - } - - /// Return the parent `Expr` of the current `Expr`. - pub fn current_expr_parent(&self) -> Option<&RefEquality<'a, Expr>> { - self.exprs.iter().rev().nth(1) - } - - /// Return the grandparent `Expr` of the current `Expr`. - pub fn current_expr_grandparent(&self) -> Option<&RefEquality<'a, Expr>> { - self.exprs.iter().rev().nth(2) - } - - /// Return the `Stmt` that immediately follows the current `Stmt`, if any. - pub fn current_sibling_stmt(&self) -> Option<&'a Stmt> { - self.body.get(self.body_index + 1) - } - - pub fn current_scope(&self) -> &Scope { - &self.scopes[*(self.scope_stack.last().expect("No current scope found"))] - } - - pub fn current_scope_parent(&self) -> Option<&Scope> { - self.scope_stack - .iter() - .rev() - .nth(1) - .map(|index| &self.scopes[*index]) - } - - pub fn current_scopes(&self) -> impl Iterator { - self.scope_stack - .iter() - .rev() - .map(|index| &self.scopes[*index]) - } - - pub const fn in_exception_handler(&self) -> bool { - self.in_exception_handler - } - - pub const fn execution_context(&self) -> ExecutionContext { - if self.in_type_checking_block - || self.in_annotation - || self.in_deferred_string_type_definition - { - ExecutionContext::Typing - } else { - ExecutionContext::Runtime - } - } - fn add_binding<'b>(&mut self, name: &'b str, binding: Binding<'a>) where 'b: 'a, { - let binding_index = self.bindings.len(); + let binding_index = self.ctx.bindings.len(); if let Some((stack_index, scope_index)) = self + .ctx .scope_stack .iter() .rev() .enumerate() - .find(|(_, scope_index)| self.scopes[**scope_index].bindings.contains_key(&name)) + .find(|(_, scope_index)| self.ctx.scopes[**scope_index].bindings.contains_key(&name)) { - let existing_binding_index = self.scopes[*scope_index].bindings.get(&name).unwrap(); - let existing = &self.bindings[*existing_binding_index]; + let existing_binding_index = self.ctx.scopes[*scope_index].bindings.get(&name).unwrap(); + let existing = &self.ctx.bindings[*existing_binding_index]; let in_current_scope = stack_index == 0; if !existing.kind.is_builtin() && existing.source.as_ref().map_or(true, |left| { @@ -4257,8 +4023,8 @@ impl<'a> Checker<'a> { !branch_detection::different_forks( left, right, - &self.depths, - &self.child_to_parent, + &self.ctx.depths, + &self.ctx.child_to_parent, ) }) }) @@ -4310,7 +4076,8 @@ impl<'a> Checker<'a> { } } } else if existing_is_import && binding.redefines(existing) { - self.redefinitions + self.ctx + .redefinitions .entry(*existing_binding_index) .or_insert_with(Vec::new) .push(binding_index); @@ -4318,9 +4085,9 @@ impl<'a> Checker<'a> { } } - let scope = self.current_scope(); + let scope = self.ctx.current_scope(); let binding = if let Some(index) = scope.bindings.get(&name) { - let existing = &self.bindings[*index]; + let existing = &self.ctx.bindings[*index]; match &existing.kind { BindingKind::Builtin => { // Avoid overriding builtins. @@ -4350,7 +4117,8 @@ impl<'a> Checker<'a> { // Don't treat annotations as assignments if there is an existing value // in scope. - let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))]; + let scope = + &mut self.ctx.scopes[*(self.ctx.scope_stack.last().expect("No current scope found"))]; if !(binding.kind.is_annotation() && scope.bindings.contains_key(name)) { if let Some(rebound_index) = scope.bindings.insert(name, binding_index) { scope @@ -4361,21 +4129,45 @@ impl<'a> Checker<'a> { } } - self.bindings.push(binding); + self.ctx.bindings.push(binding); + } + + fn bind_builtins(&mut self) { + let scope = + &mut self.ctx.scopes[*(self.ctx.scope_stack.last().expect("No current scope found"))]; + + for builtin in BUILTINS + .iter() + .chain(MAGIC_GLOBALS.iter()) + .copied() + .chain(self.settings.builtins.iter().map(String::as_str)) + { + let index = self.ctx.bindings.len(); + self.ctx.bindings.push(Binding { + kind: BindingKind::Builtin, + range: Range::default(), + runtime_usage: None, + synthetic_usage: Some((0, Range::default())), + typing_usage: None, + source: None, + context: ExecutionContext::Runtime, + }); + scope.bindings.insert(builtin, index); + } } fn handle_node_load(&mut self, expr: &Expr) { let ExprKind::Name { id, .. } = &expr.node else { return; }; - let scope_id = self.current_scope().id; + let scope_id = self.ctx.current_scope().id; let mut first_iter = true; let mut in_generator = false; let mut import_starred = false; - for scope_index in self.scope_stack.iter().rev() { - let scope = &self.scopes[*scope_index]; + for scope_index in self.ctx.scope_stack.iter().rev() { + let scope = &self.ctx.scopes[*scope_index]; if matches!(scope.kind, ScopeKind::Class(_)) { if id == "__class__" { @@ -4387,12 +4179,12 @@ impl<'a> Checker<'a> { if let Some(index) = scope.bindings.get(&id.as_str()) { // Mark the binding as used. - let context = self.execution_context(); - self.bindings[*index].mark_used(scope_id, Range::from_located(expr), context); + let context = self.ctx.execution_context(); + self.ctx.bindings[*index].mark_used(scope_id, Range::from_located(expr), context); - if self.bindings[*index].kind.is_annotation() - && !self.in_deferred_string_type_definition - && !self.in_deferred_type_definition + if self.ctx.bindings[*index].kind.is_annotation() + && !self.ctx.in_deferred_string_type_definition + && !self.ctx.in_deferred_type_definition { continue; } @@ -4405,7 +4197,7 @@ impl<'a> Checker<'a> { // import pyarrow as pa // import pyarrow.csv // print(pa.csv.read_csv("test.csv")) - match &self.bindings[*index].kind { + match &self.ctx.bindings[*index].kind { BindingKind::Importation(name, full_name) | BindingKind::SubmoduleImportation(name, full_name) => { let has_alias = full_name @@ -4416,7 +4208,7 @@ impl<'a> Checker<'a> { if has_alias { // Mark the sub-importation as used. if let Some(index) = scope.bindings.get(full_name) { - self.bindings[*index].mark_used( + self.ctx.bindings[*index].mark_used( scope_id, Range::from_located(expr), context, @@ -4433,7 +4225,7 @@ impl<'a> Checker<'a> { if has_alias { // Mark the sub-importation as used. if let Some(index) = scope.bindings.get(full_name.as_str()) { - self.bindings[*index].mark_used( + self.ctx.bindings[*index].mark_used( scope_id, Range::from_located(expr), context, @@ -4455,9 +4247,13 @@ impl<'a> Checker<'a> { if import_starred { if self.settings.rules.enabled(&Rule::ImportStarUsage) { let mut from_list = vec![]; - for scope_index in self.scope_stack.iter().rev() { - let scope = &self.scopes[*scope_index]; - for binding in scope.bindings.values().map(|index| &self.bindings[*index]) { + for scope_index in self.ctx.scope_stack.iter().rev() { + let scope = &self.ctx.scopes[*scope_index]; + for binding in scope + .bindings + .values() + .map(|index| &self.ctx.bindings[*index]) + { if let BindingKind::StarImportation(level, module) = &binding.kind { from_list.push(helpers::format_import_from( level.as_ref(), @@ -4487,13 +4283,13 @@ impl<'a> Checker<'a> { // Allow "__module__" and "__qualname__" in class scopes. if (id == "__module__" || id == "__qualname__") - && matches!(self.current_scope().kind, ScopeKind::Class(..)) + && matches!(self.ctx.current_scope().kind, ScopeKind::Class(..)) { return; } // Avoid flagging if NameError is handled. - if let Some(handler_names) = self.except_handlers.last() { + if let Some(handler_names) = self.ctx.except_handlers.last() { if handler_names .iter() .any(|call_path| call_path.as_slice() == ["NameError"]) @@ -4513,15 +4309,17 @@ impl<'a> Checker<'a> { where 'b: 'a, { - let parent = self.current_stmt().0; + let parent = self.ctx.current_stmt().0; if self.settings.rules.enabled(&Rule::UndefinedLocal) { let scopes: Vec<&Scope> = self + .ctx .scope_stack .iter() - .map(|index| &self.scopes[*index]) + .map(|index| &self.ctx.scopes[*index]) .collect(); - if let Some(diagnostic) = pyflakes::rules::undefined_local(id, &scopes, &self.bindings) + if let Some(diagnostic) = + pyflakes::rules::undefined_local(id, &scopes, &self.ctx.bindings) { self.diagnostics.push(diagnostic); } @@ -4532,13 +4330,14 @@ impl<'a> Checker<'a> { .rules .enabled(&Rule::NonLowercaseVariableInFunction) { - if matches!(self.current_scope().kind, ScopeKind::Function(..)) { + if matches!(self.ctx.current_scope().kind, ScopeKind::Function(..)) { // Ignore globals. if !self + .ctx .current_scope() .bindings .get(id) - .map_or(false, |index| self.bindings[*index].kind.is_global()) + .map_or(false, |index| self.ctx.bindings[*index].kind.is_global()) { pep8_naming::rules::non_lowercase_variable_in_function(self, expr, parent, id); } @@ -4550,7 +4349,7 @@ impl<'a> Checker<'a> { .rules .enabled(&Rule::MixedCaseVariableInClassScope) { - if matches!(self.current_scope().kind, ScopeKind::Class(..)) { + if matches!(self.ctx.current_scope().kind, ScopeKind::Class(..)) { pep8_naming::rules::mixed_case_variable_in_class_scope(self, expr, parent, id); } } @@ -4560,7 +4359,7 @@ impl<'a> Checker<'a> { .rules .enabled(&Rule::MixedCaseVariableInGlobalScope) { - if matches!(self.current_scope().kind, ScopeKind::Module) { + if matches!(self.ctx.current_scope().kind, ScopeKind::Module) { pep8_naming::rules::mixed_case_variable_in_global_scope(self, expr, parent, id); } } @@ -4574,8 +4373,8 @@ impl<'a> Checker<'a> { synthetic_usage: None, typing_usage: None, range: Range::from_located(expr), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); return; @@ -4594,8 +4393,8 @@ impl<'a> Checker<'a> { synthetic_usage: None, typing_usage: None, range: Range::from_located(expr), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); return; @@ -4610,14 +4409,14 @@ impl<'a> Checker<'a> { synthetic_usage: None, typing_usage: None, range: Range::from_located(expr), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); return; } - let current = self.current_scope(); + let current = self.ctx.current_scope(); if id == "__all__" && matches!(current.kind, ScopeKind::Module) && matches!( @@ -4651,7 +4450,7 @@ impl<'a> Checker<'a> { } _ => false, } { - let (all_names, all_names_flags) = extract_all_names(self, parent, current); + let (all_names, all_names_flags) = extract_all_names(&self.ctx, parent, current); if self.settings.rules.enabled(&Rule::InvalidAllFormat) { if matches!(all_names_flags, AllNamesFlags::INVALID_FORMAT) { @@ -4675,8 +4474,8 @@ impl<'a> Checker<'a> { synthetic_usage: None, typing_usage: None, range: Range::from_located(expr), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); return; @@ -4691,8 +4490,8 @@ impl<'a> Checker<'a> { synthetic_usage: None, typing_usage: None, range: Range::from_located(expr), - source: Some(self.current_stmt().clone()), - context: self.execution_context(), + source: Some(self.ctx.current_stmt().clone()), + context: self.ctx.execution_context(), }, ); } @@ -4704,13 +4503,12 @@ impl<'a> Checker<'a> { let ExprKind::Name { id, .. } = &expr.node else { return; }; - if operations::on_conditional_branch( - &mut self.parents.iter().rev().map(std::convert::Into::into), - ) { + if operations::on_conditional_branch(&mut self.ctx.parents.iter().rev().map(Into::into)) { return; } - let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))]; + let scope = + &mut self.ctx.scopes[*(self.ctx.scope_stack.last().expect("No current scope found"))]; if scope.bindings.remove(&id.as_str()).is_some() { return; } @@ -4743,8 +4541,8 @@ impl<'a> Checker<'a> { }, docstring, }, - self.visible_scope.visibility.clone(), - (self.scope_stack.clone(), self.parents.clone()), + self.ctx.visible_scope.visibility.clone(), + (self.ctx.scope_stack.clone(), self.ctx.parents.clone()), )); docstring.is_some() } @@ -4754,15 +4552,15 @@ impl<'a> Checker<'a> { while let Some((expr, (in_annotation, in_type_checking_block), (scopes, parents))) = self.deferred_type_definitions.pop() { - self.scope_stack = scopes; - self.parents = parents; - self.in_annotation = in_annotation; - self.in_type_checking_block = in_type_checking_block; - self.in_type_definition = true; - self.in_deferred_type_definition = true; + self.ctx.scope_stack = scopes; + self.ctx.parents = parents; + self.ctx.in_annotation = in_annotation; + self.ctx.in_type_checking_block = in_type_checking_block; + self.ctx.in_type_definition = true; + self.ctx.in_deferred_type_definition = true; self.visit_expr(expr); - self.in_deferred_type_definition = false; - self.in_type_definition = false; + self.ctx.in_deferred_type_definition = false; + self.ctx.in_type_definition = false; } } @@ -4776,7 +4574,7 @@ impl<'a> Checker<'a> { self.deferred_string_type_definitions.pop() { if let Ok(mut expr) = parser::parse_expression(expression, "") { - if in_annotation && self.annotations_future_enabled { + if in_annotation && self.ctx.annotations_future_enabled { if self.settings.rules.enabled(&Rule::QuotedAnnotation) { pyupgrade::rules::quoted_annotation(self, expression, range); } @@ -4802,24 +4600,24 @@ impl<'a> Checker<'a> { for (expr, ((in_annotation, in_type_checking_block), (scopes, parents))) in allocator.iter().zip(stacks) { - self.scope_stack = scopes; - self.parents = parents; - self.in_annotation = in_annotation; - self.in_type_checking_block = in_type_checking_block; - self.in_type_definition = true; - self.in_deferred_string_type_definition = true; + self.ctx.scope_stack = scopes; + self.ctx.parents = parents; + self.ctx.in_annotation = in_annotation; + self.ctx.in_type_checking_block = in_type_checking_block; + self.ctx.in_type_definition = true; + self.ctx.in_deferred_string_type_definition = true; self.visit_expr(expr); - self.in_deferred_string_type_definition = false; - self.in_type_definition = false; + self.ctx.in_deferred_string_type_definition = false; + self.ctx.in_type_definition = false; } } fn check_deferred_functions(&mut self) { self.deferred_functions.reverse(); while let Some((stmt, (scopes, parents), visibility)) = self.deferred_functions.pop() { - self.scope_stack = scopes.clone(); - self.parents = parents.clone(); - self.visible_scope = visibility; + self.ctx.scope_stack = scopes.clone(); + self.ctx.parents = parents.clone(); + self.ctx.visible_scope = visibility; match &stmt.node { StmtKind::FunctionDef { body, args, .. } @@ -4837,8 +4635,8 @@ impl<'a> Checker<'a> { fn check_deferred_lambdas(&mut self) { self.deferred_lambdas.reverse(); while let Some((expr, (scopes, parents))) = self.deferred_lambdas.pop() { - self.scope_stack = scopes.clone(); - self.parents = parents.clone(); + self.ctx.scope_stack = scopes.clone(); + self.ctx.parents = parents.clone(); if let ExprKind::Lambda { args, body } = &expr.node { self.visit_arguments(args); @@ -4877,9 +4675,9 @@ impl<'a> Checker<'a> { self.diagnostics .extend(flake8_unused_arguments::rules::unused_arguments( self, - &self.scopes[parent_scope_index], - &self.scopes[scope_index], - &self.bindings, + &self.ctx.scopes[parent_scope_index], + &self.ctx.scopes[scope_index], + &self.ctx.bindings, )); } } @@ -4888,8 +4686,8 @@ impl<'a> Checker<'a> { fn check_deferred_for_loops(&mut self) { self.deferred_for_loops.reverse(); while let Some((stmt, (scopes, parents))) = self.deferred_for_loops.pop() { - self.scope_stack = scopes.clone(); - self.parents = parents.clone(); + self.ctx.scope_stack = scopes.clone(); + self.ctx.parents = parents.clone(); if let StmtKind::For { target, body, .. } | StmtKind::AsyncFor { target, body, .. } = &stmt.node @@ -4937,11 +4735,11 @@ impl<'a> Checker<'a> { } // Mark anything referenced in `__all__` as used. - let global_scope = &self.scopes[GLOBAL_SCOPE_INDEX]; + let global_scope = &self.ctx.scopes[GLOBAL_SCOPE_INDEX]; let all_names: Option<(&Vec, Range)> = global_scope .bindings .get("__all__") - .map(|index| &self.bindings[*index]) + .map(|index| &self.ctx.bindings[*index]) .and_then(|binding| match &binding.kind { BindingKind::Export(names) => Some((names, binding.range)), _ => None, @@ -4957,7 +4755,7 @@ impl<'a> Checker<'a> { }); if let Some((bindings, range)) = all_bindings { for index in bindings { - self.bindings[index].mark_used( + self.ctx.bindings[index].mark_used( GLOBAL_SCOPE_INDEX, range, ExecutionContext::Runtime, @@ -4969,7 +4767,7 @@ impl<'a> Checker<'a> { let all_names: Option<(Vec<&str>, Range)> = global_scope .bindings .get("__all__") - .map(|index| &self.bindings[*index]) + .map(|index| &self.ctx.bindings[*index]) .and_then(|binding| match &binding.kind { BindingKind::Export(names) => { Some((names.iter().map(String::as_str).collect(), binding.range)) @@ -5000,13 +4798,14 @@ impl<'a> Checker<'a> { .rules .enabled(&Rule::TypingOnlyStandardLibraryImport) { - self.scopes + self.ctx + .scopes .iter() .map(|scope| { scope .bindings .values() - .map(|index| &self.bindings[*index]) + .map(|index| &self.ctx.bindings[*index]) .filter(|binding| { flake8_type_checking::helpers::is_valid_runtime_import(binding) }) @@ -5019,8 +4818,8 @@ impl<'a> Checker<'a> { }; let mut diagnostics: Vec = vec![]; - for (index, stack) in self.dead_scopes.iter().rev() { - let scope = &self.scopes[*index]; + for (index, stack) in self.ctx.dead_scopes.iter().rev() { + let scope = &self.ctx.scopes[*index]; // F822 if *index == GLOBAL_SCOPE_INDEX { @@ -5040,7 +4839,7 @@ impl<'a> Checker<'a> { .enabled(&Rule::GlobalVariableNotAssigned) { for (name, index) in &scope.bindings { - let binding = &self.bindings[*index]; + let binding = &self.ctx.bindings[*index]; if binding.kind.is_global() { if let Some(stmt) = &binding.source { if matches!(stmt.node, StmtKind::Global { .. }) { @@ -5066,7 +4865,7 @@ impl<'a> Checker<'a> { // the bindings are in different scopes. if self.settings.rules.enabled(&Rule::RedefinedWhileUnused) { for (name, index) in &scope.bindings { - let binding = &self.bindings[*index]; + let binding = &self.ctx.bindings[*index]; if matches!( binding.kind, @@ -5080,9 +4879,9 @@ impl<'a> Checker<'a> { continue; } - if let Some(indices) = self.redefinitions.get(index) { + if let Some(indices) = self.ctx.redefinitions.get(index) { for index in indices { - let rebound = &self.bindings[*index]; + let rebound = &self.ctx.bindings[*index]; let mut diagnostic = Diagnostic::new( pyflakes::rules::RedefinedWhileUnused { name: (*name).to_string(), @@ -5108,7 +4907,11 @@ impl<'a> Checker<'a> { if scope.import_starred { if let Some((names, range)) = &all_names { let mut from_list = vec![]; - for binding in scope.bindings.values().map(|index| &self.bindings[*index]) { + for binding in scope + .bindings + .values() + .map(|index| &self.ctx.bindings[*index]) + { if let BindingKind::StarImportation(level, module) = &binding.kind { from_list.push(helpers::format_import_from( level.as_ref(), @@ -5161,7 +4964,7 @@ impl<'a> Checker<'a> { .collect() }; for (.., index) in &scope.bindings { - let binding = &self.bindings[*index]; + let binding = &self.ctx.bindings[*index]; if let Some(diagnostic) = flake8_type_checking::rules::runtime_import_in_type_checking_block(binding) @@ -5195,7 +4998,7 @@ impl<'a> Checker<'a> { FxHashMap::default(); for index in scope.bindings.values() { - let binding = &self.bindings[*index]; + let binding = &self.ctx.bindings[*index]; let full_name = match &binding.kind { BindingKind::Importation(.., full_name) => full_name, @@ -5209,7 +5012,7 @@ impl<'a> Checker<'a> { } let defined_by = binding.source.as_ref().unwrap(); - let defined_in = self.child_to_parent.get(defined_by); + let defined_in = self.ctx.child_to_parent.get(defined_by); let child: &Stmt = defined_by.into(); let diagnostic_lineno = binding.range.location.row(); @@ -5245,14 +5048,10 @@ impl<'a> Checker<'a> { .sorted_by_key(|((defined_by, _), _)| defined_by.location) { let child: &Stmt = defined_by.into(); - let parent: Option<&Stmt> = defined_in.map(std::convert::Into::into); + let parent: Option<&Stmt> = defined_in.map(Into::into); let fix = if !ignore_init && self.patch(&Rule::UnusedImport) { - let deleted: Vec<&Stmt> = self - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); + let deleted: Vec<&Stmt> = self.deletions.iter().map(Into::into).collect(); match autofix::helpers::remove_unused_imports( unused_imports.iter().map(|(full_name, _)| *full_name), child, @@ -5439,8 +5238,8 @@ impl<'a> Checker<'a> { let mut overloaded_name: Option = None; self.definitions.reverse(); while let Some((definition, visibility, (scopes, parents))) = self.definitions.pop() { - self.scope_stack = scopes.clone(); - self.parents = parents.clone(); + self.ctx.scope_stack = scopes.clone(); + self.ctx.parents = parents.clone(); // flake8-annotations if enforce_annotations { @@ -5633,7 +5432,7 @@ impl<'a> Checker<'a> { } fn check_builtin_shadowing(&mut self, name: &str, located: &Located, is_attribute: bool) { - if is_attribute && matches!(self.current_scope().kind, ScopeKind::Class(_)) { + if is_attribute && matches!(self.ctx.current_scope().kind, ScopeKind::Class(_)) { if self .settings .rules @@ -5701,7 +5500,7 @@ pub fn check_ast( stylist, indexer, ); - checker.push_scope(Scope::new(ScopeKind::Module)); + checker.ctx.push_scope(Scope::new(ScopeKind::Module)); checker.bind_builtins(); // Check for module docstring. @@ -5727,8 +5526,8 @@ pub fn check_ast( checker.check_definitions(); // Reset the scope to module-level, and check all consumed scopes. - checker.scope_stack = vec![GLOBAL_SCOPE_INDEX]; - checker.pop_scope(); + checker.ctx.scope_stack = vec![GLOBAL_SCOPE_INDEX]; + checker.ctx.pop_scope(); checker.check_dead_scopes(); checker.diagnostics diff --git a/crates/ruff/src/checkers/ctx.rs b/crates/ruff/src/checkers/ctx.rs new file mode 100644 index 00000000000000..1892b66ee6d24f --- /dev/null +++ b/crates/ruff/src/checkers/ctx.rs @@ -0,0 +1,252 @@ +use nohash_hasher::IntMap; +use rustc_hash::FxHashMap; +use rustpython_parser::ast::{Expr, Stmt}; +use smallvec::smallvec; + +use ruff_python::typing::TYPING_EXTENSIONS; + +use crate::ast::helpers::{collect_call_path, from_relative_import}; +use crate::ast::types::{Binding, BindingKind, CallPath, ExecutionContext, RefEquality, Scope}; +use crate::settings::Settings; +use crate::visibility::VisibleScope; + +#[allow(clippy::struct_excessive_bools)] +pub struct AstContext<'a> { + pub settings: &'a Settings, + pub module_path: Option>, + // Retain all scopes and parent nodes, along with a stack of indexes to track which are active + // at various points in time. + pub parents: Vec>, + pub depths: FxHashMap, usize>, + pub child_to_parent: FxHashMap, RefEquality<'a, Stmt>>, + // A stack of all bindings created in any scope, at any point in execution. + pub bindings: Vec>, + // Map from binding index to indexes of bindings that redefine it in other scopes. + pub redefinitions: IntMap>, + pub exprs: Vec>, + pub scopes: Vec>, + pub scope_stack: Vec, + pub dead_scopes: Vec<(usize, Vec)>, + // Body iteration; used to peek at siblings. + pub body: &'a [Stmt], + pub body_index: usize, + // Internal, derivative state. + pub visible_scope: VisibleScope, + pub in_annotation: bool, + pub in_type_definition: bool, + pub in_deferred_string_type_definition: bool, + pub in_deferred_type_definition: bool, + pub in_exception_handler: bool, + pub in_literal: bool, + pub in_subscript: bool, + pub in_type_checking_block: bool, + pub seen_import_boundary: bool, + pub futures_allowed: bool, + pub annotations_future_enabled: bool, + pub except_handlers: Vec>>, +} + +impl<'a> AstContext<'a> { + /// Return `true` if the `Expr` is a reference to `typing.${target}`. + pub fn match_typing_expr(&self, expr: &Expr, target: &str) -> bool { + self.resolve_call_path(expr).map_or(false, |call_path| { + self.match_typing_call_path(&call_path, target) + }) + } + + /// Return `true` if the call path is a reference to `typing.${target}`. + pub fn match_typing_call_path(&self, call_path: &CallPath, target: &str) -> bool { + if call_path.as_slice() == ["typing", target] { + return true; + } + + if TYPING_EXTENSIONS.contains(target) { + if call_path.as_slice() == ["typing_extensions", target] { + return true; + } + } + + if self.settings.typing_modules.iter().any(|module| { + let mut module: CallPath = module.split('.').collect(); + module.push(target); + *call_path == module + }) { + return true; + } + + false + } + + /// Return the current `Binding` for a given `name`. + pub fn find_binding(&self, member: &str) -> Option<&Binding> { + self.current_scopes() + .find_map(|scope| scope.bindings.get(member)) + .map(|index| &self.bindings[*index]) + } + + /// Return `true` if `member` is bound as a builtin. + pub fn is_builtin(&self, member: &str) -> bool { + self.find_binding(member) + .map_or(false, |binding| binding.kind.is_builtin()) + } + + /// Resolves the call path, e.g. if you have a file + /// + /// ```python + /// from sys import version_info as python_version + /// print(python_version) + /// ``` + /// + /// then `python_version` from the print statement will resolve to `sys.version_info`. + pub fn resolve_call_path<'b>(&'a self, value: &'b Expr) -> Option> + where + 'b: 'a, + { + let call_path = collect_call_path(value); + let Some(head) = call_path.first() else { + return None; + }; + let Some(binding) = self.find_binding(head) else { + return None; + }; + match &binding.kind { + BindingKind::Importation(.., name) | BindingKind::SubmoduleImportation(name, ..) => { + if name.starts_with('.') { + if let Some(module) = &self.module_path { + let mut source_path = from_relative_import(module, name); + source_path.extend(call_path.into_iter().skip(1)); + Some(source_path) + } else { + None + } + } else { + let mut source_path: CallPath = name.split('.').collect(); + source_path.extend(call_path.into_iter().skip(1)); + Some(source_path) + } + } + BindingKind::FromImportation(.., name) => { + if name.starts_with('.') { + if let Some(module) = &self.module_path { + let mut source_path = from_relative_import(module, name); + source_path.extend(call_path.into_iter().skip(1)); + Some(source_path) + } else { + None + } + } else { + let mut source_path: CallPath = name.split('.').collect(); + source_path.extend(call_path.into_iter().skip(1)); + Some(source_path) + } + } + BindingKind::Builtin => { + let mut source_path: CallPath = smallvec![]; + source_path.push(""); + source_path.extend(call_path); + Some(source_path) + } + _ => None, + } + } + + pub fn push_parent(&mut self, parent: &'a Stmt) { + let num_existing = self.parents.len(); + self.parents.push(RefEquality(parent)); + self.depths + .insert(self.parents[num_existing].clone(), num_existing); + if num_existing > 0 { + self.child_to_parent.insert( + self.parents[num_existing].clone(), + self.parents[num_existing - 1].clone(), + ); + } + } + + pub fn pop_parent(&mut self) { + self.parents.pop().expect("Attempted to pop without parent"); + } + + pub fn push_expr(&mut self, expr: &'a Expr) { + self.exprs.push(RefEquality(expr)); + } + + pub fn pop_expr(&mut self) { + self.exprs + .pop() + .expect("Attempted to pop without expression"); + } + + pub fn push_scope(&mut self, scope: Scope<'a>) { + self.scope_stack.push(self.scopes.len()); + self.scopes.push(scope); + } + + pub fn pop_scope(&mut self) { + self.dead_scopes.push(( + self.scope_stack + .pop() + .expect("Attempted to pop without scope"), + self.scope_stack.clone(), + )); + } + + /// Return the current `Stmt`. + pub fn current_stmt(&self) -> &RefEquality<'a, Stmt> { + self.parents.iter().rev().next().expect("No parent found") + } + + /// Return the parent `Stmt` of the current `Stmt`, if any. + pub fn current_stmt_parent(&self) -> Option<&RefEquality<'a, Stmt>> { + self.parents.iter().rev().nth(1) + } + + /// Return the parent `Expr` of the current `Expr`. + pub fn current_expr_parent(&self) -> Option<&RefEquality<'a, Expr>> { + self.exprs.iter().rev().nth(1) + } + + /// Return the grandparent `Expr` of the current `Expr`. + pub fn current_expr_grandparent(&self) -> Option<&RefEquality<'a, Expr>> { + self.exprs.iter().rev().nth(2) + } + + /// Return the `Stmt` that immediately follows the current `Stmt`, if any. + pub fn current_sibling_stmt(&self) -> Option<&'a Stmt> { + self.body.get(self.body_index + 1) + } + + pub fn current_scope(&self) -> &Scope { + &self.scopes[*(self.scope_stack.last().expect("No current scope found"))] + } + + pub fn current_scope_parent(&self) -> Option<&Scope> { + self.scope_stack + .iter() + .rev() + .nth(1) + .map(|index| &self.scopes[*index]) + } + + pub fn current_scopes(&self) -> impl Iterator { + self.scope_stack + .iter() + .rev() + .map(|index| &self.scopes[*index]) + } + + pub const fn in_exception_handler(&self) -> bool { + self.in_exception_handler + } + + pub const fn execution_context(&self) -> ExecutionContext { + if self.in_type_checking_block + || self.in_annotation + || self.in_deferred_string_type_definition + { + ExecutionContext::Typing + } else { + ExecutionContext::Runtime + } + } +} diff --git a/crates/ruff/src/checkers/mod.rs b/crates/ruff/src/checkers/mod.rs index 0befee33cef527..811e0cd82df222 100644 --- a/crates/ruff/src/checkers/mod.rs +++ b/crates/ruff/src/checkers/mod.rs @@ -1,4 +1,5 @@ pub mod ast; +pub mod ctx; pub mod filesystem; pub mod imports; pub mod logical_lines; diff --git a/crates/ruff/src/rules/flake8_2020/rules.rs b/crates/ruff/src/rules/flake8_2020/rules.rs index 5d9f1f3f5f17d7..6d1963e189eb49 100644 --- a/crates/ruff/src/rules/flake8_2020/rules.rs +++ b/crates/ruff/src/rules/flake8_2020/rules.rs @@ -115,6 +115,7 @@ impl Violation for SysVersionSlice1Referenced { fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool { checker + .ctx .resolve_call_path(expr) .map_or(false, |call_path| call_path.as_slice() == ["sys", target]) } @@ -306,6 +307,7 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: & /// YTT202 pub fn name_or_attribute(checker: &mut Checker, expr: &Expr) { if checker + .ctx .resolve_call_path(expr) .map_or(false, |call_path| call_path.as_slice() == ["six", "PY3"]) { diff --git a/crates/ruff/src/rules/flake8_annotations/rules.rs b/crates/ruff/src/rules/flake8_annotations/rules.rs index d2b44ccaa48e5f..c975d3fc4b2734 100644 --- a/crates/ruff/src/rules/flake8_annotations/rules.rs +++ b/crates/ruff/src/rules/flake8_annotations/rules.rs @@ -441,7 +441,7 @@ fn check_dynamically_typed( ) where F: FnOnce() -> String, { - if checker.match_typing_expr(annotation, "Any") { + if checker.ctx.match_typing_expr(annotation, "Any") { diagnostics.push(Diagnostic::new( AnyType { name: func() }, Range::from_located(annotation), diff --git a/crates/ruff/src/rules/flake8_bandit/helpers.rs b/crates/ruff/src/rules/flake8_bandit/helpers.rs index 2f33ee6e4aaaa6..e578cb8430cd0e 100644 --- a/crates/ruff/src/rules/flake8_bandit/helpers.rs +++ b/crates/ruff/src/rules/flake8_bandit/helpers.rs @@ -26,16 +26,22 @@ pub fn is_untyped_exception(type_: Option<&Expr>, checker: &Checker) -> bool { type_.map_or(true, |type_| { if let ExprKind::Tuple { elts, .. } = &type_.node { elts.iter().any(|type_| { - checker.resolve_call_path(type_).map_or(false, |call_path| { + checker + .ctx + .resolve_call_path(type_) + .map_or(false, |call_path| { + call_path.as_slice() == ["", "Exception"] + || call_path.as_slice() == ["", "BaseException"] + }) + }) + } else { + checker + .ctx + .resolve_call_path(type_) + .map_or(false, |call_path| { call_path.as_slice() == ["", "Exception"] || call_path.as_slice() == ["", "BaseException"] }) - }) - } else { - checker.resolve_call_path(type_).map_or(false, |call_path| { - call_path.as_slice() == ["", "Exception"] - || call_path.as_slice() == ["", "BaseException"] - }) } }) } diff --git a/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs b/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs index 3f16534a973c7c..1436959b7ecbc4 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/bad_file_permissions.rs @@ -103,6 +103,7 @@ pub fn bad_file_permissions( keywords: &[Keyword], ) { if checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["os", "chmod"]) { diff --git a/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs b/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs index a0e2df4f85bfa3..f105f1de2a6733 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/hardcoded_sql_expression.rs @@ -60,7 +60,7 @@ fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Optio op: Operator::Add | Operator::Mod, .. } => { - let Some(parent) = checker.current_expr_parent() else { + let Some(parent) = checker.ctx.current_expr_parent() else { if any_over_expr(expr, &has_string_literal) { return Some(unparse_expr(expr, checker.stylist)); } diff --git a/crates/ruff/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs b/crates/ruff/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs index be7d750f1b2741..14366e74bbb57d 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/hashlib_insecure_hash_functions.rs @@ -48,7 +48,7 @@ pub fn hashlib_insecure_hash_functions( args: &[Expr], keywords: &[Keyword], ) { - if let Some(hashlib_call) = checker.resolve_call_path(func).and_then(|call_path| { + if let Some(hashlib_call) = checker.ctx.resolve_call_path(func).and_then(|call_path| { if call_path.as_slice() == ["hashlib", "new"] { Some(HashlibCall::New) } else { diff --git a/crates/ruff/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs b/crates/ruff/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs index dd1e78da03e83e..8df19a8ed1df36 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/jinja2_autoescape_false.rs @@ -37,9 +37,13 @@ pub fn jinja2_autoescape_false( args: &[Expr], keywords: &[Keyword], ) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["jinja2", "Environment"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["jinja2", "Environment"] + }) + { let call_args = SimpleCallArgs::new(args, keywords); if let Some(autoescape_arg) = call_args.get_argument("autoescape", None) { diff --git a/crates/ruff/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs b/crates/ruff/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs index 0cf5f521f3d98f..9acf38a0eb9ed7 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/logging_config_insecure_listen.rs @@ -24,9 +24,13 @@ pub fn logging_config_insecure_listen( args: &[Expr], keywords: &[Keyword], ) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["logging", "config", "listen"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["logging", "config", "listen"] + }) + { let call_args = SimpleCallArgs::new(args, keywords); if call_args.get_argument("verify", None).is_none() { diff --git a/crates/ruff/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs b/crates/ruff/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs index 3ca91f530d99d4..e50c61cc50ae69 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/request_with_no_cert_validation.rs @@ -44,7 +44,7 @@ pub fn request_with_no_cert_validation( args: &[Expr], keywords: &[Keyword], ) { - if let Some(target) = checker.resolve_call_path(func).and_then(|call_path| { + if let Some(target) = checker.ctx.resolve_call_path(func).and_then(|call_path| { if call_path.len() == 2 { if call_path[0] == "requests" && REQUESTS_HTTP_VERBS.contains(&call_path[1]) { return Some("requests"); diff --git a/crates/ruff/src/rules/flake8_bandit/rules/request_without_timeout.rs b/crates/ruff/src/rules/flake8_bandit/rules/request_without_timeout.rs index 11767d0fe6eeba..7dd6adc0e05a21 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/request_without_timeout.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/request_without_timeout.rs @@ -34,11 +34,15 @@ pub fn request_without_timeout( args: &[Expr], keywords: &[Keyword], ) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - HTTP_VERBS - .iter() - .any(|func_name| call_path.as_slice() == ["requests", func_name]) - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + HTTP_VERBS + .iter() + .any(|func_name| call_path.as_slice() == ["requests", func_name]) + }) + { let call_args = SimpleCallArgs::new(args, keywords); if let Some(timeout_arg) = call_args.get_argument("timeout", None) { if let Some(timeout) = match &timeout_arg.node { diff --git a/crates/ruff/src/rules/flake8_bandit/rules/snmp_insecure_version.rs b/crates/ruff/src/rules/flake8_bandit/rules/snmp_insecure_version.rs index ee5a959e3aa550..a3c47695b54f02 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/snmp_insecure_version.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/snmp_insecure_version.rs @@ -25,9 +25,13 @@ pub fn snmp_insecure_version( args: &[Expr], keywords: &[Keyword], ) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["pysnmp", "hlapi", "CommunityData"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["pysnmp", "hlapi", "CommunityData"] + }) + { let call_args = SimpleCallArgs::new(args, keywords); if let Some(mp_model_arg) = call_args.get_argument("mpModel", None) { if let ExprKind::Constant { diff --git a/crates/ruff/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs b/crates/ruff/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs index 8928944131e231..4a7527b4b99182 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/snmp_weak_cryptography.rs @@ -27,9 +27,13 @@ pub fn snmp_weak_cryptography( args: &[Expr], keywords: &[Keyword], ) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["pysnmp", "hlapi", "UsmUserData"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["pysnmp", "hlapi", "UsmUserData"] + }) + { let call_args = SimpleCallArgs::new(args, keywords); if call_args.len() < 3 { checker.diagnostics.push(Diagnostic::new( diff --git a/crates/ruff/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs b/crates/ruff/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs index 36242e828e55f4..a8e9fd5c729d35 100644 --- a/crates/ruff/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs +++ b/crates/ruff/src/rules/flake8_bandit/rules/unsafe_yaml_load.rs @@ -34,12 +34,14 @@ impl Violation for UnsafeYAMLLoad { /// S506 pub fn unsafe_yaml_load(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: &[Keyword]) { if checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["yaml", "load"]) { let call_args = SimpleCallArgs::new(args, keywords); if let Some(loader_arg) = call_args.get_argument("Loader", Some(1)) { if !checker + .ctx .resolve_call_path(loader_arg) .map_or(false, |call_path| { call_path.as_slice() == ["yaml", "SafeLoader"] diff --git a/crates/ruff/src/rules/flake8_blind_except/rules.rs b/crates/ruff/src/rules/flake8_blind_except/rules.rs index 14ea148cf87ab0..d37bce40c5fe4d 100644 --- a/crates/ruff/src/rules/flake8_blind_except/rules.rs +++ b/crates/ruff/src/rules/flake8_blind_except/rules.rs @@ -35,7 +35,7 @@ pub fn blind_except( return; }; for exception in ["BaseException", "Exception"] { - if id == exception && checker.is_builtin(exception) { + if id == exception && checker.ctx.is_builtin(exception) { // If the exception is re-raised, don't flag an error. if body.iter().any(|stmt| { if let StmtKind::Raise { exc, .. } = &stmt.node { diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs b/crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs index b3a45309fa3b9f..a42e7fc565628d 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs @@ -42,12 +42,14 @@ fn is_abc_class(checker: &Checker, bases: &[Expr], keywords: &[Keyword]) -> bool .as_ref() .map_or(false, |arg| arg == "metaclass") && checker + .ctx .resolve_call_path(&keyword.node.value) .map_or(false, |call_path| { call_path.as_slice() == ["abc", "ABCMeta"] }) }) || bases.iter().any(|base| { checker + .ctx .resolve_call_path(base) .map_or(false, |call_path| call_path.as_slice() == ["abc", "ABC"]) }) diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/assert_raises_exception.rs b/crates/ruff/src/rules/flake8_bugbear/rules/assert_raises_exception.rs index ebb37036381e34..64bd84f9436c65 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/assert_raises_exception.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/assert_raises_exception.rs @@ -54,6 +54,7 @@ pub fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items: &[With return; } if !checker + .ctx .resolve_call_path(args.first().unwrap()) .map_or(false, |call_path| call_path.as_slice() == ["", "Exception"]) { diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs index b13c79b96c6041..82744f96d2ac96 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs @@ -19,15 +19,18 @@ impl Violation for CachedInstanceMethod { } fn is_cache_func(checker: &Checker, expr: &Expr) -> bool { - checker.resolve_call_path(expr).map_or(false, |call_path| { - call_path.as_slice() == ["functools", "lru_cache"] - || call_path.as_slice() == ["functools", "cache"] - }) + checker + .ctx + .resolve_call_path(expr) + .map_or(false, |call_path| { + call_path.as_slice() == ["functools", "lru_cache"] + || call_path.as_slice() == ["functools", "cache"] + }) } /// B019 pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) { - if !matches!(checker.current_scope().kind, ScopeKind::Class(_)) { + if !matches!(checker.ctx.current_scope().kind, ScopeKind::Class(_)) { return; } for decorator in decorator_list { diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs index bb57a7480d0080..f8f9914fce9598 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/function_call_argument_default.rs @@ -38,14 +38,17 @@ const IMMUTABLE_FUNCS: &[&[&str]] = &[ ]; fn is_immutable_func(checker: &Checker, func: &Expr, extend_immutable_calls: &[CallPath]) -> bool { - checker.resolve_call_path(func).map_or(false, |call_path| { - IMMUTABLE_FUNCS - .iter() - .any(|target| call_path.as_slice() == *target) - || extend_immutable_calls + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + IMMUTABLE_FUNCS .iter() - .any(|target| call_path == *target) - }) + .any(|target| call_path.as_slice() == *target) + || extend_immutable_calls + .iter() + .any(|target| call_path == *target) + }) } struct ArgumentDefaultVisitor<'a> { diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index f1b7dadb3f8114..732ba217c6eaee 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -67,11 +67,14 @@ const IMMUTABLE_GENERIC_TYPES: &[&[&str]] = &[ ]; pub fn is_mutable_func(checker: &Checker, func: &Expr) -> bool { - checker.resolve_call_path(func).map_or(false, |call_path| { - MUTABLE_FUNCS - .iter() - .any(|target| call_path.as_slice() == *target) - }) + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + MUTABLE_FUNCS + .iter() + .any(|target| call_path.as_slice() == *target) + }) } fn is_mutable_expr(checker: &Checker, expr: &Expr) -> bool { @@ -89,40 +92,44 @@ fn is_mutable_expr(checker: &Checker, expr: &Expr) -> bool { fn is_immutable_annotation(checker: &Checker, expr: &Expr) -> bool { match &expr.node { - ExprKind::Name { .. } | ExprKind::Attribute { .. } => { - checker.resolve_call_path(expr).map_or(false, |call_path| { + ExprKind::Name { .. } | ExprKind::Attribute { .. } => checker + .ctx + .resolve_call_path(expr) + .map_or(false, |call_path| { IMMUTABLE_TYPES .iter() .chain(IMMUTABLE_GENERIC_TYPES) .any(|target| call_path.as_slice() == *target) - }) - } + }), ExprKind::Subscript { value, slice, .. } => { - checker.resolve_call_path(value).map_or(false, |call_path| { - if IMMUTABLE_GENERIC_TYPES - .iter() - .any(|target| call_path.as_slice() == *target) - { - true - } else if call_path.as_slice() == ["typing", "Union"] { - if let ExprKind::Tuple { elts, .. } = &slice.node { - elts.iter().all(|elt| is_immutable_annotation(checker, elt)) - } else { - false - } - } else if call_path.as_slice() == ["typing", "Optional"] { - is_immutable_annotation(checker, slice) - } else if call_path.as_slice() == ["typing", "Annotated"] { - if let ExprKind::Tuple { elts, .. } = &slice.node { - elts.first() - .map_or(false, |elt| is_immutable_annotation(checker, elt)) + checker + .ctx + .resolve_call_path(value) + .map_or(false, |call_path| { + if IMMUTABLE_GENERIC_TYPES + .iter() + .any(|target| call_path.as_slice() == *target) + { + true + } else if call_path.as_slice() == ["typing", "Union"] { + if let ExprKind::Tuple { elts, .. } = &slice.node { + elts.iter().all(|elt| is_immutable_annotation(checker, elt)) + } else { + false + } + } else if call_path.as_slice() == ["typing", "Optional"] { + is_immutable_annotation(checker, slice) + } else if call_path.as_slice() == ["typing", "Annotated"] { + if let ExprKind::Tuple { elts, .. } = &slice.node { + elts.first() + .map_or(false, |elt| is_immutable_annotation(checker, elt)) + } else { + false + } } else { false } - } else { - false - } - }) + }) } ExprKind::BinOp { left, diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs b/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs index cc297a588d511a..76af14036877c7 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/setattr_with_constant.rs @@ -75,7 +75,7 @@ pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar // We can only replace a `setattr` call (which is an `Expr`) with an assignment // (which is a `Stmt`) if the `Expr` is already being used as a `Stmt` // (i.e., it's directly within an `StmtKind::Expr`). - if let StmtKind::Expr { value: child } = &checker.current_stmt().node { + if let StmtKind::Expr { value: child } = &checker.ctx.current_stmt().node { if expr == child.as_ref() { let mut diagnostic = Diagnostic::new(SetAttrWithConstant, Range::from_located(expr)); diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs b/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs index 16011c4f9aa19f..7db53a3762b9c1 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/unused_loop_control_variable.rs @@ -140,7 +140,7 @@ pub fn unused_loop_control_variable( } // Avoid fixing any variables that _may_ be used, but undetectably so. - let certainty = Certainty::from(!helpers::uses_magic_variable_access(checker, body)); + let certainty = Certainty::from(!helpers::uses_magic_variable_access(&checker.ctx, body)); // Attempt to rename the variable by prepending an underscore, but avoid // applying the fix if doing so wouldn't actually cause us to ignore the @@ -163,14 +163,14 @@ pub fn unused_loop_control_variable( if let Some(rename) = rename { if certainty.into() && checker.patch(diagnostic.kind.rule()) { // Find the `BindingKind::LoopVar` corresponding to the name. - let scope = checker.current_scope(); + let scope = checker.ctx.current_scope(); let binding = scope .bindings .get(name) .into_iter() .chain(scope.rebounds.get(name).into_iter().flatten()) .find_map(|index| { - let binding = &checker.bindings[*index]; + let binding = &checker.ctx.bindings[*index]; binding .source .as_ref() diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs b/crates/ruff/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs index 5b453b4e8faa97..ee73ff120077e1 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/useless_contextlib_suppress.rs @@ -22,9 +22,12 @@ impl Violation for UselessContextlibSuppress { /// B022 pub fn useless_contextlib_suppress(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) { if args.is_empty() - && checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["contextlib", "suppress"] - }) + && checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["contextlib", "suppress"] + }) { checker.diagnostics.push(Diagnostic::new( UselessContextlibSuppress, diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs b/crates/ruff/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs index a56d8c80d4531f..1a99f3c8ffdc49 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/zip_without_explicit_strict.rs @@ -25,7 +25,7 @@ pub fn zip_without_explicit_strict( ) { if let ExprKind::Name { id, .. } = &func.node { if id == "zip" - && checker.is_builtin("zip") + && checker.ctx.is_builtin("zip") && !kwargs.iter().any(|keyword| { keyword .node diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_call_around_sorted.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_call_around_sorted.rs index efd43fd9b395cc..38858fcb76d27f 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_call_around_sorted.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_call_around_sorted.rs @@ -75,7 +75,7 @@ pub fn unnecessary_call_around_sorted( if inner != "sorted" { return; } - if !checker.is_builtin(inner) || !checker.is_builtin(outer) { + if !checker.ctx.is_builtin(inner) || !checker.ctx.is_builtin(outer) { return; } let mut diagnostic = Diagnostic::new( diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_collection_call.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_collection_call.rs index 8af27e39cd276d..4ae011facafab2 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_collection_call.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_collection_call.rs @@ -55,7 +55,7 @@ pub fn unnecessary_collection_call( } _ => return, }; - if !checker.is_builtin(id) { + if !checker.ctx.is_builtin(id) { return; } let mut diagnostic = Diagnostic::new( diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_comprehension.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_comprehension.rs index 3d991b103874be..926ab356b508ad 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_comprehension.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_comprehension.rs @@ -56,7 +56,7 @@ pub fn unnecessary_comprehension( ExprKind::SetComp { .. } => "set", _ => return, }; - if !checker.is_builtin(id) { + if !checker.ctx.is_builtin(id) { return; } let mut diagnostic = Diagnostic::new( diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_double_cast_or_process.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_double_cast_or_process.rs index 81fb28bfe069ba..21442c4fd1cfa8 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_double_cast_or_process.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_double_cast_or_process.rs @@ -100,7 +100,7 @@ pub fn unnecessary_double_cast_or_process( let Some(inner) = helpers::function_name(func) else { return; }; - if !checker.is_builtin(inner) || !checker.is_builtin(outer) { + if !checker.ctx.is_builtin(inner) || !checker.ctx.is_builtin(outer) { return; } diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_list.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_list.rs index a9defc36165fc3..43f0974c176e96 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_list.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_list.rs @@ -52,7 +52,7 @@ pub fn unnecessary_generator_list( let Some(argument) = helpers::exactly_one_argument_with_matching_function("list", func, args, keywords) else { return; }; - if !checker.is_builtin("list") { + if !checker.ctx.is_builtin("list") { return; } if let ExprKind::GeneratorExp { .. } = argument { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_set.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_set.rs index bb9236457f8821..29d54e41775f22 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_set.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_generator_set.rs @@ -53,7 +53,7 @@ pub fn unnecessary_generator_set( let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else { return; }; - if !checker.is_builtin("set") { + if !checker.ctx.is_builtin("set") { return; } if let ExprKind::GeneratorExp { .. } = argument { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_call.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_call.rs index bdf9aa0bcc7b77..596954fa10d426 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_call.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_call.rs @@ -28,7 +28,7 @@ pub fn unnecessary_list_call(checker: &mut Checker, expr: &Expr, func: &Expr, ar let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else { return; }; - if !checker.is_builtin("list") { + if !checker.ctx.is_builtin("list") { return; } if !matches!(argument, ExprKind::ListComp { .. }) { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_dict.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_dict.rs index a7ec0adb34c826..55f5c4c61379b9 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_dict.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_dict.rs @@ -34,7 +34,7 @@ pub fn unnecessary_list_comprehension_dict( let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else { return; }; - if !checker.is_builtin("dict") { + if !checker.ctx.is_builtin("dict") { return; } let ExprKind::ListComp { elt, .. } = &argument else { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_set.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_set.rs index 2de9bb8f24a24f..2fde071a85c51e 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_set.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_list_comprehension_set.rs @@ -34,7 +34,7 @@ pub fn unnecessary_list_comprehension_set( let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else { return; }; - if !checker.is_builtin("set") { + if !checker.ctx.is_builtin("set") { return; } if let ExprKind::ListComp { .. } = &argument { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_dict.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_dict.rs index e7466f5ee8aeb2..ce622308720f3a 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_dict.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_dict.rs @@ -37,7 +37,7 @@ pub fn unnecessary_literal_dict( let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else { return; }; - if !checker.is_builtin("dict") { + if !checker.ctx.is_builtin("dict") { return; } let (kind, elts) = match argument { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_set.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_set.rs index d724c06caf73e1..f4f6b2e85da612 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_set.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_set.rs @@ -37,7 +37,7 @@ pub fn unnecessary_literal_set( let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else { return; }; - if !checker.is_builtin("set") { + if !checker.ctx.is_builtin("set") { return; } let kind = match argument { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_list_call.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_list_call.rs index f0ece44d0cf033..3832ff29279469 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_list_call.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_list_call.rs @@ -52,7 +52,7 @@ pub fn unnecessary_literal_within_list_call( let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else { return; }; - if !checker.is_builtin("list") { + if !checker.ctx.is_builtin("list") { return; } let argument_kind = match argument { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_tuple_call.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_tuple_call.rs index 5b58dc318ee2c8..4782c598e82285 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_tuple_call.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_literal_within_tuple_call.rs @@ -53,7 +53,7 @@ pub fn unnecessary_literal_within_tuple_call( let Some(argument) = helpers::first_argument_with_matching_function("tuple", func, args) else { return; }; - if !checker.is_builtin("tuple") { + if !checker.ctx.is_builtin("tuple") { return; } let argument_kind = match argument { diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs index 091b3eb3802cec..95d49c7a5b1535 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs @@ -88,7 +88,7 @@ pub fn unnecessary_map( }; match id { "map" => { - if !checker.is_builtin(id) { + if !checker.ctx.is_builtin(id) { return; } @@ -123,7 +123,7 @@ pub fn unnecessary_map( } } "list" | "set" => { - if !checker.is_builtin(id) { + if !checker.ctx.is_builtin(id) { return; } @@ -157,7 +157,7 @@ pub fn unnecessary_map( } } "dict" => { - if !checker.is_builtin(id) { + if !checker.ctx.is_builtin(id) { return; } diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_subscript_reversal.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_subscript_reversal.rs index ff40c7417dc529..63b6434c5e33fb 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_subscript_reversal.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_subscript_reversal.rs @@ -37,7 +37,7 @@ pub fn unnecessary_subscript_reversal( if !(id == "set" || id == "sorted" || id == "reversed") { return; } - if !checker.is_builtin(id) { + if !checker.ctx.is_builtin(id) { return; } let ExprKind::Subscript { slice, .. } = &first_arg.node else { diff --git a/crates/ruff/src/rules/flake8_datetimez/rules.rs b/crates/ruff/src/rules/flake8_datetimez/rules.rs index d4aadc41ec79da..19e155a595a256 100644 --- a/crates/ruff/src/rules/flake8_datetimez/rules.rs +++ b/crates/ruff/src/rules/flake8_datetimez/rules.rs @@ -124,9 +124,13 @@ pub fn call_datetime_without_tzinfo( keywords: &[Keyword], location: Range, ) { - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "datetime"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "datetime"] + }) + { return; } @@ -153,9 +157,13 @@ pub fn call_datetime_without_tzinfo( /// It uses the system local timezone. /// Use `datetime.datetime.now(tz=)` instead. pub fn call_datetime_today(checker: &mut Checker, func: &Expr, location: Range) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "datetime", "today"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "datetime", "today"] + }) + { checker .diagnostics .push(Diagnostic::new(CallDatetimeToday, location)); @@ -171,9 +179,13 @@ pub fn call_datetime_today(checker: &mut Checker, func: &Expr, location: Range) /// UTC. As such, the recommended way to create an object representing the /// current time in UTC is by calling `datetime.now(timezone.utc)`. pub fn call_datetime_utcnow(checker: &mut Checker, func: &Expr, location: Range) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "datetime", "utcnow"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "datetime", "utcnow"] + }) + { checker .diagnostics .push(Diagnostic::new(CallDatetimeUtcnow, location)); @@ -190,9 +202,13 @@ pub fn call_datetime_utcnow(checker: &mut Checker, func: &Expr, location: Range) /// specific timestamp in UTC is by calling `datetime.fromtimestamp(timestamp, /// tz=timezone.utc)`. pub fn call_datetime_utcfromtimestamp(checker: &mut Checker, func: &Expr, location: Range) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "datetime", "utcfromtimestamp"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "datetime", "utcfromtimestamp"] + }) + { checker .diagnostics .push(Diagnostic::new(CallDatetimeUtcfromtimestamp, location)); @@ -207,9 +223,13 @@ pub fn call_datetime_now_without_tzinfo( keywords: &[Keyword], location: Range, ) { - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "datetime", "now"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "datetime", "now"] + }) + { return; } @@ -245,9 +265,13 @@ pub fn call_datetime_fromtimestamp( keywords: &[Keyword], location: Range, ) { - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "datetime", "fromtimestamp"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "datetime", "fromtimestamp"] + }) + { return; } @@ -282,9 +306,13 @@ pub fn call_datetime_strptime_without_zone( args: &[Expr], location: Range, ) { - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "datetime", "strptime"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "datetime", "strptime"] + }) + { return; } @@ -299,7 +327,7 @@ pub fn call_datetime_strptime_without_zone( } }; - let (Some(grandparent), Some(parent)) = (checker.current_expr_grandparent(), checker.current_expr_parent()) else { + let (Some(grandparent), Some(parent)) = (checker.ctx.current_expr_grandparent(), checker.ctx.current_expr_parent()) else { checker.diagnostics.push(Diagnostic::new( CallDatetimeStrptimeWithoutZone, location, @@ -335,9 +363,13 @@ pub fn call_datetime_strptime_without_zone( /// It uses the system local timezone. /// Use `datetime.datetime.now(tz=).date()` instead. pub fn call_date_today(checker: &mut Checker, func: &Expr, location: Range) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "date", "today"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "date", "today"] + }) + { checker .diagnostics .push(Diagnostic::new(CallDateToday, location)); @@ -351,9 +383,13 @@ pub fn call_date_today(checker: &mut Checker, func: &Expr, location: Range) { /// It uses the system local timezone. /// Use `datetime.datetime.fromtimestamp(, tz=).date()` instead. pub fn call_date_fromtimestamp(checker: &mut Checker, func: &Expr, location: Range) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "date", "fromtimestamp"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "date", "fromtimestamp"] + }) + { checker .diagnostics .push(Diagnostic::new(CallDateFromtimestamp, location)); diff --git a/crates/ruff/src/rules/flake8_debugger/rules.rs b/crates/ruff/src/rules/flake8_debugger/rules.rs index 8a6f5840d628af..9bbf1dbeb9b3fd 100644 --- a/crates/ruff/src/rules/flake8_debugger/rules.rs +++ b/crates/ruff/src/rules/flake8_debugger/rules.rs @@ -46,7 +46,7 @@ const DEBUGGERS: &[&[&str]] = &[ /// Checks for the presence of a debugger call. pub fn debugger_call(checker: &mut Checker, expr: &Expr, func: &Expr) { - if let Some(target) = checker.resolve_call_path(func).and_then(|call_path| { + if let Some(target) = checker.ctx.resolve_call_path(func).and_then(|call_path| { DEBUGGERS .iter() .find(|target| call_path.as_slice() == **target) diff --git a/crates/ruff/src/rules/flake8_django/rules/helpers.rs b/crates/ruff/src/rules/flake8_django/rules/helpers.rs index da5f3e989bcb51..e6c0842c9d02d1 100644 --- a/crates/ruff/src/rules/flake8_django/rules/helpers.rs +++ b/crates/ruff/src/rules/flake8_django/rules/helpers.rs @@ -4,22 +4,28 @@ use crate::checkers::ast::Checker; /// Return `true` if a Python class appears to be a Django model, based on its base classes. pub fn is_model(checker: &Checker, base: &Expr) -> bool { - checker.resolve_call_path(base).map_or(false, |call_path| { - call_path.as_slice() == ["django", "db", "models", "Model"] - }) + checker + .ctx + .resolve_call_path(base) + .map_or(false, |call_path| { + call_path.as_slice() == ["django", "db", "models", "Model"] + }) } /// Return `true` if a Python class appears to be a Django model form, based on its base classes. pub fn is_model_form(checker: &Checker, base: &Expr) -> bool { - checker.resolve_call_path(base).map_or(false, |call_path| { - call_path.as_slice() == ["django", "forms", "ModelForm"] - || call_path.as_slice() == ["django", "forms", "models", "ModelForm"] - }) + checker + .ctx + .resolve_call_path(base) + .map_or(false, |call_path| { + call_path.as_slice() == ["django", "forms", "ModelForm"] + || call_path.as_slice() == ["django", "forms", "models", "ModelForm"] + }) } /// Return the name of the field type, if the expression is constructor for a Django model field. pub fn get_model_field_name<'a>(checker: &'a Checker, expr: &'a Expr) -> Option<&'a str> { - checker.resolve_call_path(expr).and_then(|call_path| { + checker.ctx.resolve_call_path(expr).and_then(|call_path| { let call_path = call_path.as_slice(); if !call_path.starts_with(&["django", "db", "models"]) { return None; diff --git a/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs b/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs index afb341c73e25c3..7bfde268df5c1b 100644 --- a/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs +++ b/crates/ruff/src/rules/flake8_django/rules/locals_in_render_function.rs @@ -46,9 +46,13 @@ pub fn locals_in_render_function( args: &[Expr], keywords: &[Keyword], ) { - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["django", "shortcuts", "render"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["django", "shortcuts", "render"] + }) + { return; } @@ -83,6 +87,7 @@ fn is_locals_call(checker: &Checker, expr: &Expr) -> bool { return false }; checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["", "locals"]) } diff --git a/crates/ruff/src/rules/flake8_logging_format/rules.rs b/crates/ruff/src/rules/flake8_logging_format/rules.rs index 427b021728df32..5889e33133f4fa 100644 --- a/crates/ruff/src/rules/flake8_logging_format/rules.rs +++ b/crates/ruff/src/rules/flake8_logging_format/rules.rs @@ -107,6 +107,7 @@ fn check_log_record_attr_clash(checker: &mut Checker, extra: &Keyword) { } ExprKind::Call { func, keywords, .. } => { if checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["", "dict"]) { @@ -180,7 +181,7 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: .rules .enabled(&Rule::LoggingRedundantExcInfo) { - if !checker.in_exception_handler() { + if !checker.ctx.in_exception_handler() { return; } if let Some(exc_info) = find_keyword(keywords, "exc_info") { @@ -193,9 +194,12 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: .. } ) || if let ExprKind::Call { func, .. } = &exc_info.node.value.node { - checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["sys", "exc_info"] - }) + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["sys", "exc_info"] + }) } else { false }) { diff --git a/crates/ruff/src/rules/flake8_pie/rules.rs b/crates/ruff/src/rules/flake8_pie/rules.rs index 8ec35bc2c8464e..389e43b659c4c0 100644 --- a/crates/ruff/src/rules/flake8_pie/rules.rs +++ b/crates/ruff/src/rules/flake8_pie/rules.rs @@ -242,11 +242,7 @@ pub fn dupe_class_field_definitions<'a, 'b>( Range::from_located(stmt), ); if checker.patch(diagnostic.kind.rule()) { - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); let locator = checker.locator; match delete_stmt( stmt, @@ -281,6 +277,7 @@ where if !bases.iter().any(|expr| { checker + .ctx .resolve_call_path(expr) .map_or(false, |call_path| call_path.as_slice() == ["enum", "Enum"]) }) { @@ -295,6 +292,7 @@ where if let ExprKind::Call { func, .. } = &value.node { if checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["enum", "auto"]) { @@ -337,7 +335,7 @@ pub fn unnecessary_comprehension_any_all( ) { if let ExprKind::Name { id, .. } = &func.node { if (id == "all" || id == "any") && args.len() == 1 { - if !checker.is_builtin(id) { + if !checker.ctx.is_builtin(id) { return; } if let ExprKind::ListComp { .. } = args[0].node { diff --git a/crates/ruff/src/rules/flake8_print/rules/print_call.rs b/crates/ruff/src/rules/flake8_print/rules/print_call.rs index 1fcc678ecf3215..820af8dd61692a 100644 --- a/crates/ruff/src/rules/flake8_print/rules/print_call.rs +++ b/crates/ruff/src/rules/flake8_print/rules/print_call.rs @@ -31,7 +31,7 @@ impl Violation for PPrintFound { /// T201, T203 pub fn print_call(checker: &mut Checker, func: &Expr, keywords: &[Keyword]) { let diagnostic = { - let call_path = checker.resolve_call_path(func); + let call_path = checker.ctx.resolve_call_path(func); if call_path .as_ref() .map_or(false, |call_path| *call_path.as_slice() == ["", "print"]) @@ -43,13 +43,13 @@ pub fn print_call(checker: &mut Checker, func: &Expr, keywords: &[Keyword]) { .find(|keyword| keyword.node.arg.as_ref().map_or(false, |arg| arg == "file")) { if !is_const_none(&keyword.node.value) { - if checker - .resolve_call_path(&keyword.node.value) - .map_or(true, |call_path| { + if checker.ctx.resolve_call_path(&keyword.node.value).map_or( + true, + |call_path| { call_path.as_slice() != ["sys", "stdout"] && call_path.as_slice() != ["sys", "stderr"] - }) - { + }, + ) { return; } } diff --git a/crates/ruff/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs b/crates/ruff/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs index a6bb51018e5d3c..4eb26b147e31fb 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs @@ -70,9 +70,13 @@ pub fn bad_version_info_comparison( return; }; - if !checker.resolve_call_path(left).map_or(false, |call_path| { - call_path.as_slice() == ["sys", "version_info"] - }) { + if !checker + .ctx + .resolve_call_path(left) + .map_or(false, |call_path| { + call_path.as_slice() == ["sys", "version_info"] + }) + { return; } diff --git a/crates/ruff/src/rules/flake8_pyi/rules/prefix_type_params.rs b/crates/ruff/src/rules/flake8_pyi/rules/prefix_type_params.rs index 3a1da9befbd0d0..e5f9911f0eb0f9 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/prefix_type_params.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/prefix_type_params.rs @@ -71,12 +71,12 @@ pub fn prefix_type_params(checker: &mut Checker, value: &Expr, targets: &[Expr]) }; if let ExprKind::Call { func, .. } = &value.node { - let Some(kind) = checker.resolve_call_path(func).and_then(|call_path| { - if checker.match_typing_call_path(&call_path, "ParamSpec") { + let Some(kind) = checker.ctx.resolve_call_path(func).and_then(|call_path| { + if checker.ctx.match_typing_call_path(&call_path, "ParamSpec") { Some(VarKind::ParamSpec) - } else if checker.match_typing_call_path(&call_path, "TypeVar") { + } else if checker.ctx.match_typing_call_path(&call_path, "TypeVar") { Some(VarKind::TypeVar) - } else if checker.match_typing_call_path(&call_path, "TypeVarTuple") { + } else if checker.ctx.match_typing_call_path(&call_path, "TypeVarTuple") { Some(VarKind::TypeVarTuple) } else { None diff --git a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs index 066ebeff2aaf35..b411366d965ce4 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/simple_defaults.rs @@ -122,6 +122,7 @@ fn is_valid_default_value_with_annotation(default: &Expr, checker: &Checker) -> // `sys.stdin`, etc. ExprKind::Attribute { .. } => { if checker + .ctx .resolve_call_path(default) .map_or(false, |call_path| { ALLOWED_ATTRIBUTES_IN_DEFAULTS diff --git a/crates/ruff/src/rules/flake8_pyi/rules/unrecognized_platform.rs b/crates/ruff/src/rules/flake8_pyi/rules/unrecognized_platform.rs index b551de5198328c..b591b7c15b0e9c 100644 --- a/crates/ruff/src/rules/flake8_pyi/rules/unrecognized_platform.rs +++ b/crates/ruff/src/rules/flake8_pyi/rules/unrecognized_platform.rs @@ -103,9 +103,13 @@ pub fn unrecognized_platform( let diagnostic_unrecognized_platform_check = Diagnostic::new(UnrecognizedPlatformCheck, Range::from_located(expr)); - if !checker.resolve_call_path(left).map_or(false, |call_path| { - call_path.as_slice() == ["sys", "platform"] - }) { + if !checker + .ctx + .resolve_call_path(left) + .map_or(false, |call_path| { + call_path.as_slice() == ["sys", "platform"] + }) + { return; } diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs index 282ea4ebfe70d0..fc01b9a840efd6 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/assertion.rs @@ -195,8 +195,8 @@ pub fn unittest_assertion( if let Ok(unittest_assert) = UnittestAssert::try_from(attr.as_str()) { // We're converting an expression to a statement, so avoid applying the fix if // the assertion is part of a larger expression. - let fixable = checker.current_expr_parent().is_none() - && matches!(checker.current_stmt().node, StmtKind::Expr { .. }) + let fixable = checker.ctx.current_expr_parent().is_none() + && matches!(checker.ctx.current_stmt().node, StmtKind::Expr { .. }) && !has_comments_in(Range::from_located(expr), checker.locator); let mut diagnostic = Diagnostic::new( UnittestAssertion { diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs index c629c97415dcc4..29b0b34486a46c 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/helpers.rs @@ -17,13 +17,17 @@ pub fn get_mark_name(decorator: &Expr) -> &str { } pub fn is_pytest_fail(call: &Expr, checker: &Checker) -> bool { - checker.resolve_call_path(call).map_or(false, |call_path| { - call_path.as_slice() == ["pytest", "fail"] - }) + checker + .ctx + .resolve_call_path(call) + .map_or(false, |call_path| { + call_path.as_slice() == ["pytest", "fail"] + }) } pub fn is_pytest_fixture(decorator: &Expr, checker: &Checker) -> bool { checker + .ctx .resolve_call_path(if let ExprKind::Call { func, .. } = &decorator.node { func } else { @@ -45,6 +49,7 @@ pub fn is_pytest_mark(decorator: &Expr) -> bool { pub fn is_pytest_yield_fixture(decorator: &Expr, checker: &Checker) -> bool { checker + .ctx .resolve_call_path(map_callable(decorator)) .map_or(false, |call_path| { call_path.as_slice() == ["pytest", "yield_fixture"] @@ -53,6 +58,7 @@ pub fn is_pytest_yield_fixture(decorator: &Expr, checker: &Checker) -> bool { pub fn is_abstractmethod_decorator(decorator: &Expr, checker: &Checker) -> bool { checker + .ctx .resolve_call_path(decorator) .map_or(false, |call_path| { call_path.as_slice() == ["abc", "abstractmethod"] @@ -103,6 +109,7 @@ pub fn is_falsy_constant(expr: &Expr) -> bool { pub fn is_pytest_parametrize(decorator: &Expr, checker: &Checker) -> bool { checker + .ctx .resolve_call_path(map_callable(decorator)) .map_or(false, |call_path| { call_path.as_slice() == ["pytest", "mark", "parametrize"] diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs index aab62cc5788f77..73fdbb1ab33006 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/raises.rs @@ -45,9 +45,12 @@ impl Violation for RaisesWithoutException { } fn is_pytest_raises(checker: &Checker, func: &Expr) -> bool { - checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["pytest", "raises"] - }) + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["pytest", "raises"] + }) } const fn is_non_trivial_with_body(body: &[Stmt]) -> bool { @@ -136,25 +139,29 @@ pub fn complex_raises(checker: &mut Checker, stmt: &Stmt, items: &[Withitem], bo /// PT011 fn exception_needs_match(checker: &mut Checker, exception: &Expr) { - if let Some(call_path) = checker.resolve_call_path(exception).and_then(|call_path| { - let is_broad_exception = checker - .settings - .flake8_pytest_style - .raises_require_match_for - .iter() - .chain( - &checker - .settings - .flake8_pytest_style - .raises_extend_require_match_for, - ) - .any(|target| call_path == to_call_path(target)); - if is_broad_exception { - Some(format_call_path(&call_path)) - } else { - None - } - }) { + if let Some(call_path) = checker + .ctx + .resolve_call_path(exception) + .and_then(|call_path| { + let is_broad_exception = checker + .settings + .flake8_pytest_style + .raises_require_match_for + .iter() + .chain( + &checker + .settings + .flake8_pytest_style + .raises_extend_require_match_for, + ) + .any(|target| call_path == to_call_path(target)); + if is_broad_exception { + Some(format_call_path(&call_path)) + } else { + None + } + }) + { checker.diagnostics.push(Diagnostic::new( RaisesTooBroad { exception: call_path, diff --git a/crates/ruff/src/rules/flake8_return/rules.rs b/crates/ruff/src/rules/flake8_return/rules.rs index f8f880d99549c5..8e55e030b8c7fc 100644 --- a/crates/ruff/src/rules/flake8_return/rules.rs +++ b/crates/ruff/src/rules/flake8_return/rules.rs @@ -192,11 +192,14 @@ const NORETURN_FUNCS: &[&[&str]] = &[ /// Return `true` if the `func` is a known function that never returns. fn is_noreturn_func(checker: &Checker, func: &Expr) -> bool { - checker.resolve_call_path(func).map_or(false, |call_path| { - NORETURN_FUNCS - .iter() - .any(|target| call_path.as_slice() == *target) - }) + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + NORETURN_FUNCS + .iter() + .any(|target| call_path.as_slice() == *target) + }) } /// RET503 diff --git a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs index e2b0ca04859e4f..d8828c777786d1 100644 --- a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs +++ b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs @@ -89,6 +89,7 @@ pub fn private_member_access(checker: &mut Checker, expr: &Expr) { // Ignore accesses on class members from _within_ the class. if checker + .ctx .scopes .iter() .rev() @@ -99,6 +100,7 @@ pub fn private_member_access(checker: &mut Checker, expr: &Expr) { .map_or(false, |class_def| { if call_path.as_slice() == [class_def.name] { checker + .ctx .find_binding(class_def.name) .map_or(false, |binding| { // TODO(charlie): Could the name ever be bound to a _different_ diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_bool_op.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_bool_op.rs index 23474391e05c8d..d98b2db3a705ab 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_bool_op.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_bool_op.rs @@ -172,7 +172,7 @@ pub fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) { if func_name != "isinstance" { continue; } - if !checker.is_builtin("isinstance") { + if !checker.ctx.is_builtin("isinstance") { continue; } @@ -313,7 +313,7 @@ pub fn compare_with_tuple(checker: &mut Checker, expr: &Expr) { // Avoid rewriting (e.g.) `a == "foo" or a == f()`. if comparators .iter() - .any(|expr| contains_effect(checker, expr)) + .any(|expr| contains_effect(&checker.ctx, expr)) { continue; } @@ -395,7 +395,7 @@ pub fn expr_and_not_expr(checker: &mut Checker, expr: &Expr) { return; } - if contains_effect(checker, expr) { + if contains_effect(&checker.ctx, expr) { return; } @@ -449,7 +449,7 @@ pub fn expr_or_not_expr(checker: &mut Checker, expr: &Expr) { return; } - if contains_effect(checker, expr) { + if contains_effect(&checker.ctx, expr) { return; } @@ -480,7 +480,7 @@ pub fn expr_or_true(checker: &mut Checker, expr: &Expr) { let ExprKind::BoolOp { op: Boolop::Or, values, } = &expr.node else { return; }; - if contains_effect(checker, expr) { + if contains_effect(&checker.ctx, expr) { return; } for value in values { @@ -507,7 +507,7 @@ pub fn expr_and_false(checker: &mut Checker, expr: &Expr) { let ExprKind::BoolOp { op: Boolop::And, values, } = &expr.node else { return; }; - if contains_effect(checker, expr) { + if contains_effect(&checker.ctx, expr) { return; } for value in values { diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_expr.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_expr.rs index f425535e7c7121..5dfa853ef1ce22 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_expr.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_expr.rs @@ -45,9 +45,14 @@ pub fn use_capital_environment_variables(checker: &mut Checker, expr: &Expr) { let ExprKind::Constant { value: Constant::Str(env_var), kind } = &arg.node else { return; }; - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["os", "environ", "get"] || call_path.as_slice() == ["os", "getenv"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["os", "environ", "get"] + || call_path.as_slice() == ["os", "getenv"] + }) + { return; } diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs index d3278d92d8812e..b1413e357a4e54 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs @@ -347,7 +347,7 @@ pub fn needless_bool(checker: &mut Checker, stmt: &Stmt) { let fixable = matches!(if_return, Bool::True) && matches!(else_return, Bool::False) && !has_comments(stmt, checker.locator) - && (matches!(test.node, ExprKind::Compare { .. }) || checker.is_builtin("bool")); + && (matches!(test.node, ExprKind::Compare { .. }) || checker.ctx.is_builtin("bool")); let mut diagnostic = Diagnostic::new( NeedlessBool { condition, fixable }, @@ -431,13 +431,13 @@ pub fn use_ternary_operator(checker: &mut Checker, stmt: &Stmt, parent: Option<& } // Avoid suggesting ternary for `if sys.version_info >= ...`-style checks. - if contains_call_path(checker, test, &["sys", "version_info"]) { + if contains_call_path(&checker.ctx, test, &["sys", "version_info"]) { return; } // Avoid suggesting ternary for `if sys.platform.startswith("...")`-style // checks. - if contains_call_path(checker, test, &["sys", "platform"]) { + if contains_call_path(&checker.ctx, test, &["sys", "platform"]) { return; } @@ -629,7 +629,7 @@ pub fn manual_dict_lookup( }; if value .as_ref() - .map_or(false, |value| contains_effect(checker, value)) + .map_or(false, |value| contains_effect(&checker.ctx, value)) { return; } @@ -702,7 +702,7 @@ pub fn manual_dict_lookup( }; if value .as_ref() - .map_or(false, |value| contains_effect(checker, value)) + .map_or(false, |value| contains_effect(&checker.ctx, value)) { return; }; @@ -784,7 +784,7 @@ pub fn use_dict_get_with_default( } // Check that the default value is not "complex". - if contains_effect(checker, default_val) { + if contains_effect(&checker.ctx, default_val) { return; } diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_ifexp.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_ifexp.rs index 1d9be72f3dc9d0..a807cfdda036cc 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_ifexp.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_ifexp.rs @@ -106,7 +106,7 @@ pub fn explicit_true_false_in_ifexpr( expr.location, expr.end_location.unwrap(), )); - } else if checker.is_builtin("bool") { + } else if checker.ctx.is_builtin("bool") { diagnostic.amend(Fix::replacement( unparse_expr( &create_expr(ExprKind::Call { diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs index fbbb94379bf9cb..37947310e5455f 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs @@ -88,12 +88,12 @@ pub fn negation_with_equal_op(checker: &mut Checker, expr: &Expr, op: &Unaryop, if !matches!(&ops[..], [Cmpop::Eq]) { return; } - if is_exception_check(checker.current_stmt()) { + if is_exception_check(checker.ctx.current_stmt()) { return; } // Avoid flagging issues in dunder implementations. - if let ScopeKind::Function(def) = &checker.current_scope().kind { + if let ScopeKind::Function(def) = &checker.ctx.current_scope().kind { if DUNDER_METHODS.contains(&def.name) { return; } @@ -139,12 +139,12 @@ pub fn negation_with_not_equal_op( if !matches!(&ops[..], [Cmpop::NotEq]) { return; } - if is_exception_check(checker.current_stmt()) { + if is_exception_check(checker.ctx.current_stmt()) { return; } // Avoid flagging issues in dunder implementations. - if let ScopeKind::Function(def) = &checker.current_scope().kind { + if let ScopeKind::Function(def) = &checker.ctx.current_scope().kind { if DUNDER_METHODS.contains(&def.name) { return; } diff --git a/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs b/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs index f17a7e93622ded..d93556002df4d7 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/open_file_with_context_handler.rs @@ -19,7 +19,7 @@ impl Violation for OpenFileWithContextHandler { /// Return `true` if the current expression is nested in an `await /// exit_stack.enter_async_context` call. fn match_async_exit_stack(checker: &Checker) -> bool { - let Some(expr) = checker.current_expr_grandparent() else { + let Some(expr) = checker.ctx.current_expr_grandparent() else { return false; }; let ExprKind::Await { value } = &expr.node else { @@ -34,13 +34,17 @@ fn match_async_exit_stack(checker: &Checker) -> bool { if attr != "enter_async_context" { return false; } - for parent in &checker.parents { + for parent in &checker.ctx.parents { if let StmtKind::With { items, .. } = &parent.node { for item in items { if let ExprKind::Call { func, .. } = &item.context_expr.node { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["contextlib", "AsyncExitStack"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["contextlib", "AsyncExitStack"] + }) + { return true; } } @@ -53,7 +57,7 @@ fn match_async_exit_stack(checker: &Checker) -> bool { /// Return `true` if the current expression is nested in an /// `exit_stack.enter_context` call. fn match_exit_stack(checker: &Checker) -> bool { - let Some(expr) = checker.current_expr_parent() else { + let Some(expr) = checker.ctx.current_expr_parent() else { return false; }; let ExprKind::Call { func, .. } = &expr.node else { @@ -65,13 +69,17 @@ fn match_exit_stack(checker: &Checker) -> bool { if attr != "enter_context" { return false; } - for parent in &checker.parents { + for parent in &checker.ctx.parents { if let StmtKind::With { items, .. } = &parent.node { for item in items { if let ExprKind::Call { func, .. } = &item.context_expr.node { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["contextlib", "ExitStack"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["contextlib", "ExitStack"] + }) + { return true; } } @@ -84,12 +92,13 @@ fn match_exit_stack(checker: &Checker) -> bool { /// SIM115 pub fn open_file_with_context_handler(checker: &mut Checker, func: &Expr) { if checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["", "open"]) { - if checker.is_builtin("open") { + if checker.ctx.is_builtin("open") { // Ex) `with open("foo.txt") as f: ...` - if matches!(checker.current_stmt().node, StmtKind::With { .. }) { + if matches!(checker.ctx.current_stmt().node, StmtKind::With { .. }) { return; } diff --git a/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs b/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs index 7ad8f81e643afb..18c5b7bd1caaa1 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/reimplemented_builtin.rs @@ -221,7 +221,7 @@ pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling: }, Range::from_located(stmt), ); - if checker.patch(diagnostic.kind.rule()) && checker.is_builtin("any") { + if checker.patch(diagnostic.kind.rule()) && checker.ctx.is_builtin("any") { diagnostic.amend(Fix::replacement( contents, stmt.location, @@ -298,7 +298,7 @@ pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling: }, Range::from_located(stmt), ); - if checker.patch(diagnostic.kind.rule()) && checker.is_builtin("all") { + if checker.patch(diagnostic.kind.rule()) && checker.ctx.is_builtin("all") { diagnostic.amend(Fix::replacement( contents, stmt.location, diff --git a/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs b/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs index 16e236e173b633..0ca8182c00d866 100644 --- a/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs +++ b/crates/ruff/src/rules/flake8_tidy_imports/banned_api.rs @@ -97,7 +97,7 @@ pub fn name_or_parent_is_banned( /// TID251 pub fn banned_attribute_access(checker: &mut Checker, expr: &Expr) { - if let Some((banned_path, ban)) = checker.resolve_call_path(expr).and_then(|call_path| { + if let Some((banned_path, ban)) = checker.ctx.resolve_call_path(expr).and_then(|call_path| { checker .settings .flake8_tidy_imports diff --git a/crates/ruff/src/rules/flake8_type_checking/helpers.rs b/crates/ruff/src/rules/flake8_type_checking/helpers.rs index 93d93557c8989d..78f3247c52bb14 100644 --- a/crates/ruff/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff/src/rules/flake8_type_checking/helpers.rs @@ -29,9 +29,13 @@ pub fn is_type_checking_block(checker: &Checker, test: &Expr) -> bool { } // Ex) `if typing.TYPE_CHECKING:` - if checker.resolve_call_path(test).map_or(false, |call_path| { - call_path.as_slice() == ["typing", "TYPE_CHECKING"] - }) { + if checker + .ctx + .resolve_call_path(test) + .map_or(false, |call_path| { + call_path.as_slice() == ["typing", "TYPE_CHECKING"] + }) + { return true; } diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs b/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs index 20712dfe1a2e3c..250189bf4563d4 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/empty_type_checking_block.rs @@ -36,14 +36,11 @@ pub fn empty_type_checking_block<'a, 'b>( // Delete the entire type-checking block. if checker.patch(diagnostic.kind.rule()) { let parent = checker + .ctx .child_to_parent .get(&RefEquality(stmt)) - .map(std::convert::Into::into); - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); + .map(Into::into); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); match delete_stmt( stmt, parent, diff --git a/crates/ruff/src/rules/flake8_unused_arguments/rules.rs b/crates/ruff/src/rules/flake8_unused_arguments/rules.rs index 721f3328686770..636f822371a37b 100644 --- a/crates/ruff/src/rules/flake8_unused_arguments/rules.rs +++ b/crates/ruff/src/rules/flake8_unused_arguments/rules.rs @@ -178,7 +178,7 @@ pub fn unused_arguments( .. }) => { match function_type::classify( - checker, + &checker.ctx, parent, name, decorator_list, diff --git a/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs b/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs index 68277e13c79236..99f2b3d2944e6d 100644 --- a/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs +++ b/crates/ruff/src/rules/flake8_use_pathlib/helpers.rs @@ -15,6 +15,7 @@ use crate::settings::types::PythonVersion; pub fn replaceable_by_pathlib(checker: &mut Checker, expr: &Expr) { if let Some(diagnostic_kind) = checker + .ctx .resolve_call_path(expr) .and_then(|call_path| match call_path.as_slice() { ["os", "path", "abspath"] => Some(PathlibAbspath.into()), diff --git a/crates/ruff/src/rules/numpy/rules/deprecated_type_alias.rs b/crates/ruff/src/rules/numpy/rules/deprecated_type_alias.rs index 28eb1fd1c0ef1c..cfb2f1edab5eab 100644 --- a/crates/ruff/src/rules/numpy/rules/deprecated_type_alias.rs +++ b/crates/ruff/src/rules/numpy/rules/deprecated_type_alias.rs @@ -49,7 +49,7 @@ impl AlwaysAutofixableViolation for NumpyDeprecatedTypeAlias { /// NPY001 pub fn deprecated_type_alias(checker: &mut Checker, expr: &Expr) { - if let Some(type_name) = checker.resolve_call_path(expr).and_then(|call_path| { + if let Some(type_name) = checker.ctx.resolve_call_path(expr).and_then(|call_path| { if call_path.as_slice() == ["numpy", "bool"] || call_path.as_slice() == ["numpy", "int"] || call_path.as_slice() == ["numpy", "float"] diff --git a/crates/ruff/src/rules/numpy/rules/numpy_legacy_random.rs b/crates/ruff/src/rules/numpy/rules/numpy_legacy_random.rs index 7d27a591c77692..175bdff5859f6f 100644 --- a/crates/ruff/src/rules/numpy/rules/numpy_legacy_random.rs +++ b/crates/ruff/src/rules/numpy/rules/numpy_legacy_random.rs @@ -59,7 +59,7 @@ impl Violation for NumpyLegacyRandom { /// NPY002 pub fn numpy_legacy_random(checker: &mut Checker, expr: &Expr) { - if let Some(method_name) = checker.resolve_call_path(expr).and_then(|call_path| { + if let Some(method_name) = checker.ctx.resolve_call_path(expr).and_then(|call_path| { // seeding state if call_path.as_slice() == ["numpy", "random", "seed"] || call_path.as_slice() == ["numpy", "random", "get_state"] diff --git a/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs b/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs index be0461dd7678b0..efe541d0cf20c2 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs @@ -59,7 +59,7 @@ pub fn check_attr(checker: &mut Checker, attr: &str, value: &Expr, attr_expr: &E }; // Avoid flagging on function calls (e.g., `df.values()`). - if let Some(parent) = checker.current_expr_parent() { + if let Some(parent) = checker.ctx.current_expr_parent() { if matches!(parent.node, ExprKind::Call { .. }) { return; } @@ -72,7 +72,7 @@ pub fn check_attr(checker: &mut Checker, attr: &str, value: &Expr, attr_expr: &E // If the target is a named variable, avoid triggering on // irrelevant bindings (like imports). if let ExprKind::Name { id, .. } = &value.node { - if checker.find_binding(id).map_or(true, |binding| { + if checker.ctx.find_binding(id).map_or(true, |binding| { matches!( binding.kind, BindingKind::Builtin diff --git a/crates/ruff/src/rules/pandas_vet/rules/check_call.rs b/crates/ruff/src/rules/pandas_vet/rules/check_call.rs index 52cf6cb7843e6c..5bc8adb3c5e38e 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/check_call.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/check_call.rs @@ -81,7 +81,7 @@ pub fn check_call(checker: &mut Checker, func: &Expr) { // If the target is a named variable, avoid triggering on // irrelevant bindings (like non-Pandas imports). if let ExprKind::Name { id, .. } = &value.node { - if checker.find_binding(id).map_or(true, |binding| { + if checker.ctx.find_binding(id).map_or(true, |binding| { if let BindingKind::Importation(.., module) = &binding.kind { module != &"pandas" } else { diff --git a/crates/ruff/src/rules/pep8_naming/helpers.rs b/crates/ruff/src/rules/pep8_naming/helpers.rs index d533c646cd41a1..01cf8b2b39400d 100644 --- a/crates/ruff/src/rules/pep8_naming/helpers.rs +++ b/crates/ruff/src/rules/pep8_naming/helpers.rs @@ -29,10 +29,13 @@ pub fn is_namedtuple_assignment(checker: &Checker, stmt: &Stmt) -> bool { let ExprKind::Call {func, ..} = &value.node else { return false; }; - checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["collections", "namedtuple"] - || call_path.as_slice() == ["typing", "NamedTuple"] - }) + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["collections", "namedtuple"] + || call_path.as_slice() == ["typing", "NamedTuple"] + }) } pub fn is_typeddict_assignment(checker: &Checker, stmt: &Stmt) -> bool { @@ -42,9 +45,12 @@ pub fn is_typeddict_assignment(checker: &Checker, stmt: &Stmt) -> bool { let ExprKind::Call {func, ..} = &value.node else { return false; }; - checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["typing", "TypedDict"] - }) + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["typing", "TypedDict"] + }) } pub fn is_type_var_assignment(checker: &Checker, stmt: &Stmt) -> bool { @@ -54,10 +60,13 @@ pub fn is_type_var_assignment(checker: &Checker, stmt: &Stmt) -> bool { let ExprKind::Call {func, ..} = &value.node else { return false; }; - checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["typing", "TypeVar"] - || call_path.as_slice() == ["typing", "NewType"] - }) + checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["typing", "TypeVar"] + || call_path.as_slice() == ["typing", "NewType"] + }) } #[cfg(test)] diff --git a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs index 9404e494f15946..e99ba1c1329ba1 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs @@ -64,7 +64,7 @@ pub fn invalid_first_argument_name_for_class_method( ) -> Option { if !matches!( function_type::classify( - checker, + &checker.ctx, scope, name, decorator_list, diff --git a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs index 1a39d8ea8e0433..fbad2f9121395a 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs @@ -61,7 +61,7 @@ pub fn invalid_first_argument_name_for_method( ) -> Option { if !matches!( function_type::classify( - checker, + &checker.ctx, scope, name, decorator_list, diff --git a/crates/ruff/src/rules/pycodestyle/rules/imports.rs b/crates/ruff/src/rules/pycodestyle/rules/imports.rs index d50be16c419026..bd9fcb9d4ca2c9 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/imports.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/imports.rs @@ -36,7 +36,7 @@ pub fn multiple_imports_on_one_line(checker: &mut Checker, stmt: &Stmt, names: & } pub fn module_import_not_at_top_of_file(checker: &mut Checker, stmt: &Stmt) { - if checker.seen_import_boundary && stmt.location.column() == 0 { + if checker.ctx.seen_import_boundary && stmt.location.column() == 0 { checker.diagnostics.push(Diagnostic::new( ModuleImportNotAtTopOfFile, Range::from_located(stmt), diff --git a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs index 212796ee099101..adad2e0ed7dd77 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs @@ -40,7 +40,7 @@ pub fn lambda_assignment(checker: &mut Checker, target: &Expr, value: &Expr, stm // package like dataclasses, which wouldn't consider the // rewritten function definition to be equivalent. // See https://github.com/charliermarsh/ruff/issues/3046 - let fixable = !matches!(checker.current_scope().kind, ScopeKind::Class(_)); + let fixable = !matches!(checker.ctx.current_scope().kind, ScopeKind::Class(_)); let mut diagnostic = Diagnostic::new( LambdaAssignment { diff --git a/crates/ruff/src/rules/pydocstyle/helpers.rs b/crates/ruff/src/rules/pydocstyle/helpers.rs index 0a6b46e4e26f8f..471f1f4e2b1324 100644 --- a/crates/ruff/src/rules/pydocstyle/helpers.rs +++ b/crates/ruff/src/rules/pydocstyle/helpers.rs @@ -85,7 +85,7 @@ pub fn should_ignore_definition( | DefinitionKind::Method(parent) = definition.kind { for decorator in cast::decorator_list(parent) { - if let Some(call_path) = checker.resolve_call_path(map_callable(decorator)) { + if let Some(call_path) = checker.ctx.resolve_call_path(map_callable(decorator)) { if ignore_decorators .iter() .any(|decorator| to_call_path(decorator) == call_path) diff --git a/crates/ruff/src/rules/pyflakes/rules/invalid_print_syntax.rs b/crates/ruff/src/rules/pyflakes/rules/invalid_print_syntax.rs index c70b77b92e51ea..013157b3542646 100644 --- a/crates/ruff/src/rules/pyflakes/rules/invalid_print_syntax.rs +++ b/crates/ruff/src/rules/pyflakes/rules/invalid_print_syntax.rs @@ -24,7 +24,7 @@ pub fn invalid_print_syntax(checker: &mut Checker, left: &Expr) { if id != "print" { return; } - if !checker.is_builtin("print") { + if !checker.ctx.is_builtin("print") { return; }; checker.diagnostics.push(Diagnostic::new( diff --git a/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs b/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs index 68ac91b8b03203..aa1200e9250714 100644 --- a/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs +++ b/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs @@ -17,9 +17,9 @@ impl Violation for ReturnOutsideFunction { } pub fn return_outside_function(checker: &mut Checker, stmt: &Stmt) { - if let Some(&index) = checker.scope_stack.last() { + if let Some(&index) = checker.ctx.scope_stack.last() { if matches!( - checker.scopes[index].kind, + checker.ctx.scopes[index].kind, ScopeKind::Class(_) | ScopeKind::Module ) { checker.diagnostics.push(Diagnostic::new( diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs b/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs index acd4b5d5cbf4b3..7aae3212cd49a3 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs @@ -19,11 +19,11 @@ impl Violation for UnusedAnnotation { /// F842 pub fn unused_annotation(checker: &mut Checker, scope: usize) { - let scope = &checker.scopes[scope]; + let scope = &checker.ctx.scopes[scope]; for (name, binding) in scope .bindings .iter() - .map(|(name, index)| (name, &checker.bindings[*index])) + .map(|(name, index)| (name, &checker.ctx.bindings[*index])) { if !binding.used() && binding.kind.is_annotation() diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs index 7df5357aca6eea..4cb54c05d3822d 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs @@ -200,7 +200,7 @@ fn remove_unused_variable( range.location == target.location && range.end_location == target.end_location.unwrap() }) { if matches!(target.node, ExprKind::Name { .. }) { - return if targets.len() > 1 || contains_effect(checker, value) { + return if targets.len() > 1 || contains_effect(&checker.ctx, value) { // If the expression is complex (`x = foo()`), remove the assignment, // but preserve the right-hand side. Some(( @@ -214,14 +214,11 @@ fn remove_unused_variable( } else { // If (e.g.) assigning to a constant (`x = 1`), delete the entire statement. let parent = checker + .ctx .child_to_parent .get(&RefEquality(stmt)) - .map(std::convert::Into::into); - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); + .map(Into::into); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); match delete_stmt( stmt, parent, @@ -249,7 +246,7 @@ fn remove_unused_variable( } = &stmt.node { if matches!(target.node, ExprKind::Name { .. }) { - return if contains_effect(checker, value) { + return if contains_effect(&checker.ctx, value) { // If the expression is complex (`x = foo()`), remove the assignment, // but preserve the right-hand side. Some(( @@ -262,14 +259,11 @@ fn remove_unused_variable( } else { // If assigning to a constant (`x = 1`), delete the entire statement. let parent = checker + .ctx .child_to_parent .get(&RefEquality(stmt)) - .map(std::convert::Into::into); - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); + .map(Into::into); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); match delete_stmt( stmt, parent, @@ -319,7 +313,7 @@ fn remove_unused_variable( /// F841 pub fn unused_variable(checker: &mut Checker, scope: usize) { - let scope = &checker.scopes[scope]; + let scope = &checker.ctx.scopes[scope]; if scope.uses_locals && matches!(scope.kind, ScopeKind::Function(..)) { return; } @@ -327,7 +321,7 @@ pub fn unused_variable(checker: &mut Checker, scope: usize) { for (name, binding) in scope .bindings .iter() - .map(|(name, index)| (name, &checker.bindings[*index])) + .map(|(name, index)| (name, &checker.ctx.bindings[*index])) { if !binding.used() && binding.kind.is_assignment() @@ -343,7 +337,7 @@ pub fn unused_variable(checker: &mut Checker, scope: usize) { binding.range, ); if checker.patch(diagnostic.kind.rule()) { - if let Some(stmt) = binding.source.as_ref().map(std::convert::Into::into) { + if let Some(stmt) = binding.source.as_ref().map(Into::into) { if let Some((kind, fix)) = remove_unused_variable(stmt, &binding.range, checker) { if matches!(kind, DeletionKind::Whole) { diff --git a/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs b/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs index 6176ccf5787262..cf789fbd458bd6 100644 --- a/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs +++ b/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs @@ -41,7 +41,7 @@ impl Violation for YieldOutsideFunction { pub fn yield_outside_function(checker: &mut Checker, expr: &Expr) { if matches!( - checker.current_scope().kind, + checker.ctx.current_scope().kind, ScopeKind::Class(_) | ScopeKind::Module ) { let keyword = match expr.node { diff --git a/crates/ruff/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs b/crates/ruff/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs index 04e625c51e717f..694c1dacf58512 100644 --- a/crates/ruff/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs +++ b/crates/ruff/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs @@ -18,9 +18,13 @@ impl Violation for DeprecatedLogWarn { /// PGH002 - deprecated use of logging.warn pub fn deprecated_log_warn(checker: &mut Checker, func: &Expr) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["logging", "warn"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["logging", "warn"] + }) + { checker.diagnostics.push(Diagnostic::new( DeprecatedLogWarn, Range::from_located(func), diff --git a/crates/ruff/src/rules/pygrep_hooks/rules/no_eval.rs b/crates/ruff/src/rules/pygrep_hooks/rules/no_eval.rs index 41c3a4f3fed46f..ac7d98ed4b46e3 100644 --- a/crates/ruff/src/rules/pygrep_hooks/rules/no_eval.rs +++ b/crates/ruff/src/rules/pygrep_hooks/rules/no_eval.rs @@ -23,7 +23,7 @@ pub fn no_eval(checker: &mut Checker, func: &Expr) { if id != "eval" { return; } - if !checker.is_builtin("eval") { + if !checker.ctx.is_builtin("eval") { return; } checker diff --git a/crates/ruff/src/rules/pylint/helpers.rs b/crates/ruff/src/rules/pylint/helpers.rs index faa9b07a84f669..c2d71e4e0ab9c2 100644 --- a/crates/ruff/src/rules/pylint/helpers.rs +++ b/crates/ruff/src/rules/pylint/helpers.rs @@ -5,8 +5,8 @@ use crate::{ checkers::ast::Checker, }; -pub fn in_dunder_init(checker: &mut Checker) -> bool { - let scope = checker.current_scope(); +pub fn in_dunder_init(checker: &Checker) -> bool { + let scope = checker.ctx.current_scope(); let ScopeKind::Function(FunctionDef { name, decorator_list, @@ -17,13 +17,13 @@ pub fn in_dunder_init(checker: &mut Checker) -> bool { if *name != "__init__" { return false; } - let Some(parent) = checker.current_scope_parent() else { + let Some(parent) = checker.ctx.current_scope_parent() else { return false; }; if !matches!( function_type::classify( - checker, + &checker.ctx, parent, name, decorator_list, diff --git a/crates/ruff/src/rules/pylint/rules/await_outside_async.rs b/crates/ruff/src/rules/pylint/rules/await_outside_async.rs index 0f3d8de43b2fc8..8ba3601bad46cd 100644 --- a/crates/ruff/src/rules/pylint/rules/await_outside_async.rs +++ b/crates/ruff/src/rules/pylint/rules/await_outside_async.rs @@ -19,6 +19,7 @@ impl Violation for AwaitOutsideAsync { /// PLE1142 pub fn await_outside_async(checker: &mut Checker, expr: &Expr) { if !checker + .ctx .current_scopes() .find_map(|scope| { if let ScopeKind::Function(FunctionDef { async_, .. }) = &scope.kind { diff --git a/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs b/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs index 5fc563ae6f7c37..a166691145e8af 100644 --- a/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs +++ b/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs @@ -28,9 +28,9 @@ impl Violation for ConsiderUsingSysExit { /// Return `true` if the `module` was imported using a star import (e.g., `from /// sys import *`). fn is_module_star_imported(checker: &Checker, module: &str) -> bool { - checker.current_scopes().any(|scope| { + checker.ctx.current_scopes().any(|scope| { scope.bindings.values().any(|index| { - if let BindingKind::StarImportation(_, name) = &checker.bindings[*index].kind { + if let BindingKind::StarImportation(_, name) = &checker.ctx.bindings[*index].kind { name.as_ref().map(|name| name == module).unwrap_or_default() } else { false @@ -42,11 +42,11 @@ fn is_module_star_imported(checker: &Checker, module: &str) -> bool { /// Return the appropriate `sys.exit` reference based on the current set of /// imports, or `None` is `sys.exit` hasn't been imported. fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -> Option { - checker.current_scopes().find_map(|scope| { + checker.ctx.current_scopes().find_map(|scope| { scope .bindings .values() - .find_map(|index| match &checker.bindings[*index].kind { + .find_map(|index| match &checker.ctx.bindings[*index].kind { // e.g. module=sys object=exit // `import sys` -> `sys.exit` // `import sys as sys2` -> `sys2.exit` @@ -107,7 +107,7 @@ pub fn consider_using_sys_exit(checker: &mut Checker, func: &Expr) { if name == "exit" && is_module_star_imported(checker, "sys") { continue; } - if !checker.is_builtin(name) { + if !checker.ctx.is_builtin(name) { continue; } let mut diagnostic = Diagnostic::new( diff --git a/crates/ruff/src/rules/pylint/rules/global_statement.rs b/crates/ruff/src/rules/pylint/rules/global_statement.rs index 39c2e5e312855c..1dd21983581aff 100644 --- a/crates/ruff/src/rules/pylint/rules/global_statement.rs +++ b/crates/ruff/src/rules/pylint/rules/global_statement.rs @@ -51,9 +51,9 @@ impl Violation for GlobalStatement { /// PLW0603 pub fn global_statement(checker: &mut Checker, name: &str) { - let scope = checker.current_scope(); + let scope = checker.ctx.current_scope(); if let Some(index) = scope.bindings.get(name) { - let binding = &checker.bindings[*index]; + let binding = &checker.ctx.bindings[*index]; if binding.kind.is_global() { let diagnostic = Diagnostic::new( GlobalStatement { diff --git a/crates/ruff/src/rules/pylint/rules/merge_isinstance.rs b/crates/ruff/src/rules/pylint/rules/merge_isinstance.rs index d1497b92adf1db..6d1d5b689d8a5d 100644 --- a/crates/ruff/src/rules/pylint/rules/merge_isinstance.rs +++ b/crates/ruff/src/rules/pylint/rules/merge_isinstance.rs @@ -27,7 +27,7 @@ impl Violation for ConsiderMergingIsinstance { /// PLR1701 pub fn merge_isinstance(checker: &mut Checker, expr: &Expr, op: &Boolop, values: &[Expr]) { - if !matches!(op, Boolop::Or) || !checker.is_builtin("isinstance") { + if !matches!(op, Boolop::Or) || !checker.ctx.is_builtin("isinstance") { return; } diff --git a/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs b/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs index 5f94572fa883a4..fba8f85dce0e02 100644 --- a/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs +++ b/crates/ruff/src/rules/pylint/rules/property_with_parameters.rs @@ -30,7 +30,7 @@ pub fn property_with_parameters( { return; } - if checker.is_builtin("property") + if checker.ctx.is_builtin("property") && args .args .iter() diff --git a/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs b/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs index cebe787add4479..bbfbfa1858bcec 100644 --- a/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs +++ b/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs @@ -21,7 +21,7 @@ impl Violation for UsedPriorGlobalDeclaration { } /// PLE0118 pub fn used_prior_global_declaration(checker: &mut Checker, name: &str, expr: &Expr) { - let globals = match &checker.current_scope().kind { + let globals = match &checker.ctx.current_scope().kind { ScopeKind::Class(class_def) => &class_def.globals, ScopeKind::Function(function_def) => &function_def.globals, _ => return, diff --git a/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs b/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs index 4b8244f6113e00..702a5e114b1ae7 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/convert_named_tuple_functional_to_class.rs @@ -55,9 +55,13 @@ fn match_named_tuple_assign<'a>( } = &value.node else { return None; }; - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["typing", "NamedTuple"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["typing", "NamedTuple"] + }) + { return None; } Some((typename, args, keywords, func)) diff --git a/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs b/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs index 0c7bedcefa66f0..0e81da92c01635 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/convert_typed_dict_functional_to_class.rs @@ -56,9 +56,13 @@ fn match_typed_dict_assign<'a>( } = &value.node else { return None; }; - if !checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["typing", "TypedDict"] - }) { + if !checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["typing", "TypedDict"] + }) + { return None; } Some((class_name, args, keywords, func)) diff --git a/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs index 6c2394eb119bbd..cc62c524ef7ff4 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/datetime_utc_alias.rs @@ -32,9 +32,13 @@ impl Violation for DatetimeTimezoneUTC { /// UP017 pub fn datetime_utc_alias(checker: &mut Checker, expr: &Expr) { - if checker.resolve_call_path(expr).map_or(false, |call_path| { - call_path.as_slice() == ["datetime", "timezone", "utc"] - }) { + if checker + .ctx + .resolve_call_path(expr) + .map_or(false, |call_path| { + call_path.as_slice() == ["datetime", "timezone", "utc"] + }) + { let straight_import = collect_call_path(expr).as_slice() == ["datetime", "timezone", "utc"]; let mut diagnostic = Diagnostic::new( DatetimeTimezoneUTC { straight_import }, diff --git a/crates/ruff/src/rules/pyupgrade/rules/functools_cache.rs b/crates/ruff/src/rules/pyupgrade/rules/functools_cache.rs index 6b826ac8a93f8a..bd28d0b8957179 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/functools_cache.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/functools_cache.rs @@ -36,9 +36,12 @@ pub fn functools_cache(checker: &mut Checker, decorator_list: &[Expr]) { // Look for, e.g., `import functools; @functools.lru_cache(maxsize=None)`. if args.is_empty() && keywords.len() == 1 - && checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["functools", "lru_cache"] - }) + && checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["functools", "lru_cache"] + }) { let KeywordData { arg, value } = &keywords[0].node; if arg.as_ref().map_or(false, |arg| arg == "maxsize") diff --git a/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs b/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs index 948baae5f8ddd3..2d457605b478e6 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/lru_cache_without_parameters.rs @@ -36,9 +36,12 @@ pub fn lru_cache_without_parameters(checker: &mut Checker, decorator_list: &[Exp // Look for, e.g., `import functools; @functools.lru_cache()`. if args.is_empty() && keywords.is_empty() - && checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["functools", "lru_cache"] - }) + && checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["functools", "lru_cache"] + }) { let mut diagnostic = Diagnostic::new( LRUCacheWithoutParameters, diff --git a/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs b/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs index 493568ee9eddfa..8e71124118763b 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/native_literals.rs @@ -58,7 +58,7 @@ pub fn native_literals( return; } - if (id == "str" || id == "bytes") && checker.is_builtin(id) { + if (id == "str" || id == "bytes") && checker.ctx.is_builtin(id) { let Some(arg) = args.get(0) else { let mut diagnostic = Diagnostic::new(NativeLiterals{literal_type:if id == "str" { LiteralType::Str diff --git a/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs index 14ac8049c0ad25..b21d4dab30d2aa 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/open_alias.rs @@ -24,6 +24,7 @@ impl AlwaysAutofixableViolation for OpenAlias { /// UP020 pub fn open_alias(checker: &mut Checker, expr: &Expr, func: &Expr) { if checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["io", "open"]) { diff --git a/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs index 376cb4f92fc9aa..aa51f4bf187c42 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/os_error_alias.rs @@ -35,8 +35,8 @@ const ERROR_MODULES: &[&str] = &["mmap", "select", "socket"]; fn corrected_name(checker: &Checker, original: &str) -> String { if ERROR_NAMES.contains(&original) - && checker.is_builtin(original) - && checker.is_builtin("OSError") + && checker.ctx.is_builtin(original) + && checker.ctx.is_builtin("OSError") { "OSError".to_string() } else { @@ -59,7 +59,7 @@ fn get_before_replace(elts: &[Expr]) -> Vec { fn check_module(checker: &Checker, expr: &Expr) -> (Vec, Vec) { let mut replacements: Vec = vec![]; let mut before_replace: Vec = vec![]; - if let Some(call_path) = checker.resolve_call_path(expr) { + if let Some(call_path) = checker.ctx.resolve_call_path(expr) { for module in ERROR_MODULES.iter() { if call_path.as_slice() == [module, "error"] { replacements.push("OSError".to_string()); diff --git a/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs b/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs index 7acc45c67d2b65..45b8eeeff1eaf7 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/outdated_version_block.rs @@ -162,17 +162,13 @@ fn fix_py2_block( // Delete the entire statement. If this is an `elif`, know it's the only child // of its parent, so avoid passing in the parent at all. Otherwise, // `delete_stmt` will erroneously include a `pass`. - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); - let defined_by = checker.current_stmt(); - let defined_in = checker.current_stmt_parent(); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); + let defined_by = checker.ctx.current_stmt(); + let defined_in = checker.ctx.current_stmt_parent(); return match delete_stmt( defined_by.into(), if block.starter == Tok::If { - defined_in.map(std::convert::Into::into) + defined_in.map(Into::into) } else { None }, @@ -331,9 +327,13 @@ pub fn outdated_version_block( return; }; - if !checker.resolve_call_path(left).map_or(false, |call_path| { - call_path.as_slice() == ["sys", "version_info"] - }) { + if !checker + .ctx + .resolve_call_path(left) + .map_or(false, |call_path| { + call_path.as_slice() == ["sys", "version_info"] + }) + { return; } diff --git a/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs b/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs index 204b7e5224f1c2..ad596783682f76 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/redundant_open_modes.rs @@ -176,7 +176,7 @@ fn create_remove_param_fix(locator: &Locator, expr: &Expr, mode_param: &Expr) -> /// UP015 pub fn redundant_open_modes(checker: &mut Checker, expr: &Expr) { // If `open` has been rebound, skip this check entirely. - if !checker.is_builtin(OPEN_FUNC_NAME) { + if !checker.ctx.is_builtin(OPEN_FUNC_NAME) { return; } let (mode_param, keywords): (Option<&Expr>, Vec) = match_open(expr); diff --git a/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs b/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs index 4b843ab25d949c..ae1c137eedb40a 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/replace_stdout_stderr.rs @@ -105,9 +105,13 @@ fn generate_fix( /// UP022 pub fn replace_stdout_stderr(checker: &mut Checker, expr: &Expr, func: &Expr, kwargs: &[Keyword]) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["subprocess", "run"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["subprocess", "run"] + }) + { // Find `stdout` and `stderr` kwargs. let Some(stdout) = find_keyword(kwargs, "stdout") else { return; @@ -118,11 +122,13 @@ pub fn replace_stdout_stderr(checker: &mut Checker, expr: &Expr, func: &Expr, kw // Verify that they're both set to `subprocess.PIPE`. if !checker + .ctx .resolve_call_path(&stdout.node.value) .map_or(false, |call_path| { call_path.as_slice() == ["subprocess", "PIPE"] }) || !checker + .ctx .resolve_call_path(&stderr.node.value) .map_or(false, |call_path| { call_path.as_slice() == ["subprocess", "PIPE"] diff --git a/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs b/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs index 28c6aa627db75b..7db1fbf039ae61 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/replace_universal_newlines.rs @@ -24,9 +24,13 @@ impl AlwaysAutofixableViolation for ReplaceUniversalNewlines { /// UP021 pub fn replace_universal_newlines(checker: &mut Checker, func: &Expr, kwargs: &[Keyword]) { - if checker.resolve_call_path(func).map_or(false, |call_path| { - call_path.as_slice() == ["subprocess", "run"] - }) { + if checker + .ctx + .resolve_call_path(func) + .map_or(false, |call_path| { + call_path.as_slice() == ["subprocess", "run"] + }) + { let Some(kwarg) = find_keyword(kwargs, "universal_newlines") else { return; }; let range = Range::new( kwarg.location, diff --git a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs index 5ff000fdb9bbfb..5718f59c67cdf7 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -37,12 +37,8 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp if !is_super_call_with_arguments(func, args) { return; } - let scope = checker.current_scope(); - let parents: Vec<&Stmt> = checker - .parents - .iter() - .map(std::convert::Into::into) - .collect(); + let scope = checker.ctx.current_scope(); + let parents: Vec<&Stmt> = checker.ctx.parents.iter().map(Into::into).collect(); // Check: are we in a Function scope? if !matches!(scope.kind, ScopeKind::Function { .. }) { diff --git a/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs b/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs index 6101973f975ddf..4abc74b3efdd1d 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/type_of_primitive.rs @@ -32,6 +32,7 @@ pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: return; } if !checker + .ctx .resolve_call_path(func) .map_or(false, |call_path| call_path.as_slice() == ["", "type"]) { diff --git a/crates/ruff/src/rules/pyupgrade/rules/typing_text_str_alias.rs b/crates/ruff/src/rules/pyupgrade/rules/typing_text_str_alias.rs index ca3ff04b800a6d..4c66830caa9b03 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/typing_text_str_alias.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/typing_text_str_alias.rs @@ -23,9 +23,13 @@ impl AlwaysAutofixableViolation for TypingTextStrAlias { /// UP019 pub fn typing_text_str_alias(checker: &mut Checker, expr: &Expr) { - if checker.resolve_call_path(expr).map_or(false, |call_path| { - call_path.as_slice() == ["typing", "Text"] - }) { + if checker + .ctx + .resolve_call_path(expr) + .map_or(false, |call_path| { + call_path.as_slice() == ["typing", "Text"] + }) + { let mut diagnostic = Diagnostic::new(TypingTextStrAlias, Range::from_located(expr)); if checker.patch(diagnostic.kind.rule()) { diagnostic.amend(Fix::replacement( diff --git a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs index 02b82dddf07e33..2b1dd48a20d0ef 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_builtin_import.rs @@ -104,13 +104,9 @@ pub fn unnecessary_builtin_import( ); if checker.patch(diagnostic.kind.rule()) { - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); - let defined_by = checker.current_stmt(); - let defined_in = checker.current_stmt_parent(); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); + let defined_by = checker.ctx.current_stmt(); + let defined_in = checker.ctx.current_stmt_parent(); let unused_imports: Vec = unused_imports .iter() .map(|alias| format!("{module}.{}", alias.node.name)) @@ -118,7 +114,7 @@ pub fn unnecessary_builtin_import( match autofix::helpers::remove_unused_imports( unused_imports.iter().map(String::as_str), defined_by.into(), - defined_in.map(std::convert::Into::into), + defined_in.map(Into::into), &deleted, checker.locator, checker.indexer, diff --git a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs index db203f6016a208..91c1391c99add6 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/unnecessary_future_import.rs @@ -84,13 +84,9 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo ); if checker.patch(diagnostic.kind.rule()) { - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); - let defined_by = checker.current_stmt(); - let defined_in = checker.current_stmt_parent(); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); + let defined_by = checker.ctx.current_stmt(); + let defined_in = checker.ctx.current_stmt_parent(); let unused_imports: Vec = unused_imports .iter() .map(|alias| format!("__future__.{}", alias.node.name)) @@ -98,7 +94,7 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo match autofix::helpers::remove_unused_imports( unused_imports.iter().map(String::as_str), defined_by.into(), - defined_in.map(std::convert::Into::into), + defined_in.map(Into::into), &deleted, checker.locator, checker.indexer, diff --git a/crates/ruff/src/rules/pyupgrade/rules/use_pep585_annotation.rs b/crates/ruff/src/rules/pyupgrade/rules/use_pep585_annotation.rs index 9c2defd14042be..fef21d098877c1 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/use_pep585_annotation.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/use_pep585_annotation.rs @@ -33,6 +33,7 @@ impl AlwaysAutofixableViolation for DeprecatedCollectionType { /// UP006 pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) { if let Some(binding) = checker + .ctx .resolve_call_path(expr) .and_then(|call_path| call_path.last().copied()) { @@ -44,7 +45,7 @@ pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr) { ); if checker.patch(diagnostic.kind.rule()) { let binding = binding.to_lowercase(); - if checker.is_builtin(&binding) { + if checker.ctx.is_builtin(&binding) { diagnostic.amend(Fix::replacement( binding, expr.location, diff --git a/crates/ruff/src/rules/pyupgrade/rules/use_pep604_annotation.rs b/crates/ruff/src/rules/pyupgrade/rules/use_pep604_annotation.rs index 3d709810ef780e..977378f3a6beb6 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/use_pep604_annotation.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/use_pep604_annotation.rs @@ -82,10 +82,10 @@ pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, s return; } - let Some(typing_member) = checker.resolve_call_path(value).as_ref().and_then(|call_path| { - if checker.match_typing_call_path(call_path, "Optional") { + let Some(typing_member) = checker.ctx.resolve_call_path(value).as_ref().and_then(|call_path| { + if checker.ctx.match_typing_call_path(call_path, "Optional") { Some(TypingMember::Optional) - } else if checker.match_typing_call_path(call_path, "Union") { + } else if checker.ctx.match_typing_call_path(call_path, "Union") { Some(TypingMember::Union) } else { None diff --git a/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs b/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs index 0c17b07e399ead..6b61112c78723d 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/useless_metaclass_type.rs @@ -48,16 +48,12 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr, return; }; if checker.patch(diagnostic.kind.rule()) { - let deleted: Vec<&Stmt> = checker - .deletions - .iter() - .map(std::convert::Into::into) - .collect(); - let defined_by = checker.current_stmt(); - let defined_in = checker.current_stmt_parent(); + let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect(); + let defined_by = checker.ctx.current_stmt(); + let defined_in = checker.ctx.current_stmt_parent(); match helpers::delete_stmt( defined_by.into(), - defined_in.map(std::convert::Into::into), + defined_in.map(Into::into), &deleted, checker.locator, checker.indexer, diff --git a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs index 3599c32a10cbad..21434ea8aa7d2d 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs @@ -63,7 +63,7 @@ pub fn useless_object_inheritance( bases: &[Expr], keywords: &[Keyword], ) { - let Some(mut diagnostic) = rule(name, bases, checker.current_scope(), &checker.bindings) else { + let Some(mut diagnostic) = rule(name, bases, checker.ctx.current_scope(), &checker.ctx.bindings) else { return; }; if checker.patch(diagnostic.kind.rule()) { diff --git a/crates/ruff/src/rules/tryceratops/rules/prefer_type_error.rs b/crates/ruff/src/rules/tryceratops/rules/prefer_type_error.rs index 28d598e59e0d82..5ca443bcd17bb3 100644 --- a/crates/ruff/src/rules/tryceratops/rules/prefer_type_error.rs +++ b/crates/ruff/src/rules/tryceratops/rules/prefer_type_error.rs @@ -66,11 +66,14 @@ fn has_control_flow(stmt: &Stmt) -> bool { /// Returns `true` if an [`Expr`] is a call to check types. fn check_type_check_call(checker: &mut Checker, call: &Expr) -> bool { - checker.resolve_call_path(call).map_or(false, |call_path| { - call_path.as_slice() == ["", "isinstance"] - || call_path.as_slice() == ["", "issubclass"] - || call_path.as_slice() == ["", "callable"] - }) + checker + .ctx + .resolve_call_path(call) + .map_or(false, |call_path| { + call_path.as_slice() == ["", "isinstance"] + || call_path.as_slice() == ["", "issubclass"] + || call_path.as_slice() == ["", "callable"] + }) } /// Returns `true` if an [`Expr`] is a test to check types (e.g. via isinstance) @@ -87,27 +90,30 @@ fn check_type_check_test(checker: &mut Checker, test: &Expr) -> bool { /// Returns `true` if `exc` is a reference to a builtin exception. fn is_builtin_exception(checker: &mut Checker, exc: &Expr) -> bool { - return checker.resolve_call_path(exc).map_or(false, |call_path| { - [ - "ArithmeticError", - "AssertionError", - "AttributeError", - "BufferError", - "EOFError", - "Exception", - "ImportError", - "LookupError", - "MemoryError", - "NameError", - "ReferenceError", - "RuntimeError", - "SyntaxError", - "SystemError", - "ValueError", - ] - .iter() - .any(|target| call_path.as_slice() == ["", target]) - }); + return checker + .ctx + .resolve_call_path(exc) + .map_or(false, |call_path| { + [ + "ArithmeticError", + "AssertionError", + "AttributeError", + "BufferError", + "EOFError", + "Exception", + "ImportError", + "LookupError", + "MemoryError", + "NameError", + "ReferenceError", + "RuntimeError", + "SyntaxError", + "SystemError", + "ValueError", + ] + .iter() + .any(|target| call_path.as_slice() == ["", target]) + }); } /// Returns `true` if an [`Expr`] is a reference to a builtin exception. diff --git a/crates/ruff/src/rules/tryceratops/rules/raise_vanilla_class.rs b/crates/ruff/src/rules/tryceratops/rules/raise_vanilla_class.rs index cd2b5d56808e68..155ed7c27bffcb 100644 --- a/crates/ruff/src/rules/tryceratops/rules/raise_vanilla_class.rs +++ b/crates/ruff/src/rules/tryceratops/rules/raise_vanilla_class.rs @@ -64,6 +64,7 @@ impl Violation for RaiseVanillaClass { /// TRY002 pub fn raise_vanilla_class(checker: &mut Checker, expr: &Expr) { if checker + .ctx .resolve_call_path(if let ExprKind::Call { func, .. } = &expr.node { func } else { diff --git a/crates/ruff/src/visibility.rs b/crates/ruff/src/visibility.rs index 1d9daa0017031e..1ecaec0b9f9467 100644 --- a/crates/ruff/src/visibility.rs +++ b/crates/ruff/src/visibility.rs @@ -32,6 +32,7 @@ pub struct VisibleScope { pub fn is_staticmethod(checker: &Checker, decorator_list: &[Expr]) -> bool { decorator_list.iter().any(|expr| { checker + .ctx .resolve_call_path(map_callable(expr)) .map_or(false, |call_path| { call_path.as_slice() == ["", "staticmethod"] @@ -43,6 +44,7 @@ pub fn is_staticmethod(checker: &Checker, decorator_list: &[Expr]) -> bool { pub fn is_classmethod(checker: &Checker, decorator_list: &[Expr]) -> bool { decorator_list.iter().any(|expr| { checker + .ctx .resolve_call_path(map_callable(expr)) .map_or(false, |call_path| { call_path.as_slice() == ["", "classmethod"] @@ -52,22 +54,27 @@ pub fn is_classmethod(checker: &Checker, decorator_list: &[Expr]) -> bool { /// Returns `true` if a function definition is an `@overload`. pub fn is_overload(checker: &Checker, decorator_list: &[Expr]) -> bool { - decorator_list - .iter() - .any(|expr| checker.match_typing_expr(map_callable(expr), "overload")) + decorator_list.iter().any(|expr| { + checker + .ctx + .match_typing_expr(map_callable(expr), "overload") + }) } /// Returns `true` if a function definition is an `@override` (PEP 698). pub fn is_override(checker: &Checker, decorator_list: &[Expr]) -> bool { - decorator_list - .iter() - .any(|expr| checker.match_typing_expr(map_callable(expr), "override")) + decorator_list.iter().any(|expr| { + checker + .ctx + .match_typing_expr(map_callable(expr), "override") + }) } /// Returns `true` if a function definition is an `@abstractmethod`. pub fn is_abstract(checker: &Checker, decorator_list: &[Expr]) -> bool { decorator_list.iter().any(|expr| { checker + .ctx .resolve_call_path(map_callable(expr)) .map_or(false, |call_path| { call_path.as_slice() == ["abc", "abstractmethod"] @@ -80,6 +87,7 @@ pub fn is_abstract(checker: &Checker, decorator_list: &[Expr]) -> bool { pub fn is_property(checker: &Checker, decorator_list: &[Expr]) -> bool { decorator_list.iter().any(|expr| { checker + .ctx .resolve_call_path(map_callable(expr)) .map_or(false, |call_path| { call_path.as_slice() == ["", "property"]