mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
Move binding and scope tracking into a separate ast::Context
struct (#3298)
This commit is contained in:
parent
376ef929b1
commit
40d3b40c14
128 changed files with 1425 additions and 1174 deletions
292
crates/ruff/src/ast/context.rs
Normal file
292
crates/ruff/src/ast/context.rs
Normal file
|
@ -0,0 +1,292 @@
|
|||
use std::path::Path;
|
||||
|
||||
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, Exceptions};
|
||||
use crate::ast::types::{Binding, BindingKind, CallPath, ExecutionContext, RefEquality, Scope};
|
||||
use crate::resolver::is_interface_definition_path;
|
||||
use crate::visibility::{module_visibility, Modifier, VisibleScope};
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Context<'a> {
|
||||
pub typing_modules: &'a [String],
|
||||
pub module_path: Option<Vec<String>>,
|
||||
// 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<RefEquality<'a, Stmt>>,
|
||||
pub depths: FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
pub child_to_parent: FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
// A stack of all bindings created in any scope, at any point in execution.
|
||||
pub bindings: Vec<Binding<'a>>,
|
||||
// Map from binding index to indexes of bindings that redefine it in other scopes.
|
||||
pub redefinitions: IntMap<usize, Vec<usize>>,
|
||||
pub exprs: Vec<RefEquality<'a, Expr>>,
|
||||
pub scopes: Vec<Scope<'a>>,
|
||||
pub scope_stack: Vec<usize>,
|
||||
pub dead_scopes: Vec<(usize, Vec<usize>)>,
|
||||
// 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 handled_exceptions: Vec<Exceptions>,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(
|
||||
typing_modules: &'a [String],
|
||||
path: &'a Path,
|
||||
module_path: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
typing_modules,
|
||||
module_path,
|
||||
parents: Vec::default(),
|
||||
depths: FxHashMap::default(),
|
||||
child_to_parent: FxHashMap::default(),
|
||||
bindings: Vec::default(),
|
||||
redefinitions: IntMap::default(),
|
||||
exprs: Vec::default(),
|
||||
scopes: Vec::default(),
|
||||
scope_stack: Vec::default(),
|
||||
dead_scopes: Vec::default(),
|
||||
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_path(path),
|
||||
handled_exceptions: Vec::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.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<CallPath<'a>>
|
||||
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<Item = &Scope> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use rustpython_parser::ast::Expr;
|
||||
|
||||
use crate::ast::context::Context;
|
||||
use crate::ast::helpers::{map_callable, to_call_path};
|
||||
use crate::ast::types::{Scope, ScopeKind};
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
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: &Context,
|
||||
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| {
|
||||
call_path.as_slice() == ["", "staticmethod"]
|
||||
|| staticmethod_decorators
|
||||
|
@ -43,7 +42,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])
|
||||
|
@ -51,7 +50,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| {
|
||||
call_path.as_slice() == ["", "classmethod"] ||
|
||||
classmethod_decorators
|
||||
.iter()
|
||||
|
|
|
@ -13,10 +13,10 @@ use rustpython_parser::ast::{
|
|||
use rustpython_parser::{lexer, Mode, StringKind, Tok};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::ast::context::Context;
|
||||
use crate::ast::types::{Binding, BindingKind, CallPath, Range};
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::source_code::{Generator, Indexer, Locator, Stylist};
|
||||
|
||||
/// Create an `Expr` with default location from an `ExprKind`.
|
||||
|
@ -99,17 +99,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: &Context, 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: &Context, expr: &Expr) -> bool {
|
||||
any_over_expr(expr, &|expr| {
|
||||
// Accept empty initializers.
|
||||
if let ExprKind::Call {
|
||||
|
@ -125,7 +124,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: &Context, 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"]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
pub mod branch_detection;
|
||||
pub mod cast;
|
||||
pub mod comparable;
|
||||
pub mod context;
|
||||
pub mod function_type;
|
||||
pub mod hashable;
|
||||
pub mod helpers;
|
||||
|
|
|
@ -3,11 +3,11 @@ use rustc_hash::FxHashMap;
|
|||
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Located, Stmt, StmtKind};
|
||||
use rustpython_parser::{lexer, Mode, Tok};
|
||||
|
||||
use crate::ast::context::Context;
|
||||
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;
|
||||
|
||||
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: &Context,
|
||||
stmt: &Stmt,
|
||||
scope: &Scope,
|
||||
) -> (Vec<String>, AllNamesFlags) {
|
||||
|
@ -38,7 +38,7 @@ pub fn extract_all_names(
|
|||
}
|
||||
|
||||
fn extract_elts<'a>(
|
||||
checker: &'a Checker,
|
||||
ctx: &'a Context,
|
||||
expr: &'a Expr,
|
||||
) -> (Option<&'a Vec<Expr>>, 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);
|
||||
|
|
23
crates/ruff/src/checkers/ast/deferred.rs
Normal file
23
crates/ruff/src/checkers/ast/deferred.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use rustpython_parser::ast::{Expr, Stmt};
|
||||
|
||||
use crate::ast::types::RefEquality;
|
||||
use crate::checkers::ast::AnnotationContext;
|
||||
use crate::docstrings::definition::Definition;
|
||||
use crate::visibility::{Visibility, VisibleScope};
|
||||
use crate::Range;
|
||||
|
||||
type Context<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
|
||||
|
||||
/// A collection of AST nodes that are deferred for later analysis.
|
||||
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
|
||||
/// module-level definitions have been analyzed.
|
||||
#[derive(Default)]
|
||||
pub struct Deferred<'a> {
|
||||
pub definitions: Vec<(Definition<'a>, Visibility, Context<'a>)>,
|
||||
pub string_type_definitions: Vec<(Range, &'a str, AnnotationContext, Context<'a>)>,
|
||||
pub type_definitions: Vec<(&'a Expr, AnnotationContext, Context<'a>)>,
|
||||
pub functions: Vec<(&'a Stmt, Context<'a>, VisibleScope)>,
|
||||
pub lambdas: Vec<(&'a Expr, Context<'a>)>,
|
||||
pub for_loops: Vec<(&'a Stmt, Context<'a>)>,
|
||||
pub assignments: Vec<Context<'a>>,
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -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"])
|
||||
{
|
||||
|
|
|
@ -33,7 +33,7 @@ pub fn overloaded_name(checker: &Checker, definition: &Definition) -> Option<Str
|
|||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
if visibility::is_overload(&checker.ctx, cast::decorator_list(stmt)) {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
|
@ -51,7 +51,7 @@ pub fn is_overload_impl(checker: &Checker, definition: &Definition, overloaded_n
|
|||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
if visibility::is_overload(&checker.ctx, cast::decorator_list(stmt)) {
|
||||
false
|
||||
} else {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
|
|
|
@ -441,7 +441,7 @@ fn check_dynamically_typed<F>(
|
|||
) 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),
|
||||
|
@ -482,7 +482,8 @@ pub fn definition(
|
|||
.skip(
|
||||
// If this is a non-static method, skip `cls` or `self`.
|
||||
usize::from(
|
||||
is_method && !visibility::is_staticmethod(checker, cast::decorator_list(stmt)),
|
||||
is_method
|
||||
&& !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)),
|
||||
),
|
||||
)
|
||||
{
|
||||
|
@ -580,10 +581,10 @@ pub fn definition(
|
|||
}
|
||||
|
||||
// ANN101, ANN102
|
||||
if is_method && !visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||
if is_method && !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)) {
|
||||
if let Some(arg) = args.posonlyargs.first().or_else(|| args.args.first()) {
|
||||
if arg.node.annotation.is_none() {
|
||||
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) {
|
||||
if checker.settings.rules.enabled(&Rule::MissingTypeCls) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
MissingTypeCls {
|
||||
|
@ -619,7 +620,7 @@ pub fn definition(
|
|||
// (explicitly or implicitly).
|
||||
checker.settings.flake8_annotations.suppress_none_returning && is_none_returning(body)
|
||||
) {
|
||||
if is_method && visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if is_method && visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
|
@ -632,7 +633,8 @@ pub fn definition(
|
|||
helpers::identifier_range(stmt, checker.locator),
|
||||
));
|
||||
}
|
||||
} else if is_method && visibility::is_staticmethod(checker, cast::decorator_list(stmt))
|
||||
} else if is_method
|
||||
&& visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt))
|
||||
{
|
||||
if checker
|
||||
.settings
|
||||
|
|
|
@ -26,13 +26,19 @@ 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.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"]
|
||||
})
|
||||
|
|
|
@ -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"])
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -37,9 +37,13 @@ pub fn jinja2_autoescape_false(
|
|||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) {
|
||||
if checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
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) {
|
||||
|
|
|
@ -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| {
|
||||
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() {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -34,11 +34,15 @@ pub fn request_without_timeout(
|
|||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) {
|
||||
if checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
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 {
|
||||
|
|
|
@ -25,9 +25,13 @@ pub fn snmp_insecure_version(
|
|||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) {
|
||||
if checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
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 {
|
||||
|
|
|
@ -27,9 +27,13 @@ pub fn snmp_weak_cryptography(
|
|||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) {
|
||||
if checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
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(
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"])
|
||||
})
|
||||
|
@ -106,7 +108,7 @@ pub fn abstract_base_class(
|
|||
continue;
|
||||
};
|
||||
|
||||
let has_abstract_decorator = is_abstract(checker, decorator_list);
|
||||
let has_abstract_decorator = is_abstract(&checker.ctx, decorator_list);
|
||||
has_abstract_method |= has_abstract_decorator;
|
||||
|
||||
if !checker
|
||||
|
@ -117,7 +119,10 @@ pub fn abstract_base_class(
|
|||
continue;
|
||||
}
|
||||
|
||||
if !has_abstract_decorator && is_empty_body(body) && !is_overload(checker, decorator_list) {
|
||||
if !has_abstract_decorator
|
||||
&& is_empty_body(body)
|
||||
&& !is_overload(&checker.ctx, decorator_list)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
EmptyMethodWithoutAbstractDecorator {
|
||||
name: format!("{name}.{method_name}"),
|
||||
|
|
|
@ -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"])
|
||||
{
|
||||
|
|
|
@ -19,7 +19,10 @@ impl Violation for CachedInstanceMethod {
|
|||
}
|
||||
|
||||
fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(expr)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["functools", "lru_cache"]
|
||||
|| call_path.as_slice() == ["functools", "cache"]
|
||||
})
|
||||
|
@ -27,7 +30,7 @@ fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
|
|||
|
||||
/// 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 {
|
||||
|
|
|
@ -38,7 +38,10 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
IMMUTABLE_FUNCS
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
|
|
@ -67,7 +67,10 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
MUTABLE_FUNCS
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
@ -89,16 +92,20 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(value)
|
||||
.map_or(false, |call_path| {
|
||||
if IMMUTABLE_GENERIC_TYPES
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -22,7 +22,10 @@ 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| {
|
||||
&& checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["contextlib", "suppress"]
|
||||
})
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 { .. }) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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| {
|
||||
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| {
|
||||
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| {
|
||||
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| {
|
||||
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| {
|
||||
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| {
|
||||
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| {
|
||||
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| {
|
||||
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| {
|
||||
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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -4,14 +4,20 @@ 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| {
|
||||
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| {
|
||||
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"]
|
||||
})
|
||||
|
@ -19,7 +25,7 @@ pub fn is_model_form(checker: &Checker, base: &Expr) -> bool {
|
|||
|
||||
/// 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;
|
||||
|
|
|
@ -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| {
|
||||
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"])
|
||||
}
|
||||
|
|
|
@ -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,7 +194,10 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["sys", "exc_info"]
|
||||
})
|
||||
} else {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,13 @@ pub fn bad_version_info_comparison(
|
|||
return;
|
||||
};
|
||||
|
||||
if !checker.resolve_call_path(left).map_or(false, |call_path| {
|
||||
if !checker
|
||||
.ctx
|
||||
.resolve_call_path(left)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["sys", "version_info"]
|
||||
}) {
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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| {
|
||||
if !checker
|
||||
.ctx
|
||||
.resolve_call_path(left)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["sys", "platform"]
|
||||
}) {
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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| {
|
||||
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"]
|
||||
|
|
|
@ -45,7 +45,10 @@ impl Violation for RaisesWithoutException {
|
|||
}
|
||||
|
||||
fn is_pytest_raises(checker: &Checker, func: &Expr) -> bool {
|
||||
checker.resolve_call_path(func).map_or(false, |call_path| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["pytest", "raises"]
|
||||
})
|
||||
}
|
||||
|
@ -136,7 +139,10 @@ 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| {
|
||||
if let Some(call_path) = checker
|
||||
.ctx
|
||||
.resolve_call_path(exception)
|
||||
.and_then(|call_path| {
|
||||
let is_broad_exception = checker
|
||||
.settings
|
||||
.flake8_pytest_style
|
||||
|
@ -154,7 +160,8 @@ fn exception_needs_match(checker: &mut Checker, exception: &Expr) {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
})
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
RaisesTooBroad {
|
||||
exception: call_path,
|
||||
|
|
|
@ -192,7 +192,10 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
NORETURN_FUNCS
|
||||
.iter()
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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| {
|
||||
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| {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -96,7 +96,7 @@ pub fn name_or_parent_is_banned<T>(
|
|||
|
||||
/// 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
|
||||
|
|
|
@ -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| {
|
||||
if checker
|
||||
.ctx
|
||||
.resolve_call_path(test)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["typing", "TYPE_CHECKING"]
|
||||
}) {
|
||||
})
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -178,7 +178,7 @@ pub fn unused_arguments(
|
|||
..
|
||||
}) => {
|
||||
match function_type::classify(
|
||||
checker,
|
||||
&checker.ctx,
|
||||
parent,
|
||||
name,
|
||||
decorator_list,
|
||||
|
@ -190,7 +190,7 @@ pub fn unused_arguments(
|
|||
.settings
|
||||
.rules
|
||||
.enabled(Argumentable::Function.rule_code())
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
&& !visibility::is_overload(&checker.ctx, decorator_list)
|
||||
{
|
||||
function(
|
||||
&Argumentable::Function,
|
||||
|
@ -217,9 +217,9 @@ pub fn unused_arguments(
|
|||
|| visibility::is_init(name)
|
||||
|| visibility::is_new(name)
|
||||
|| visibility::is_call(name))
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
&& !visibility::is_abstract(&checker.ctx, decorator_list)
|
||||
&& !visibility::is_override(&checker.ctx, decorator_list)
|
||||
&& !visibility::is_overload(&checker.ctx, decorator_list)
|
||||
{
|
||||
method(
|
||||
&Argumentable::Method,
|
||||
|
@ -246,9 +246,9 @@ pub fn unused_arguments(
|
|||
|| visibility::is_init(name)
|
||||
|| visibility::is_new(name)
|
||||
|| visibility::is_call(name))
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
&& !visibility::is_abstract(&checker.ctx, decorator_list)
|
||||
&& !visibility::is_override(&checker.ctx, decorator_list)
|
||||
&& !visibility::is_overload(&checker.ctx, decorator_list)
|
||||
{
|
||||
method(
|
||||
&Argumentable::ClassMethod,
|
||||
|
@ -275,9 +275,9 @@ pub fn unused_arguments(
|
|||
|| visibility::is_init(name)
|
||||
|| visibility::is_new(name)
|
||||
|| visibility::is_call(name))
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
&& !visibility::is_overload(checker, decorator_list)
|
||||
&& !visibility::is_abstract(&checker.ctx, decorator_list)
|
||||
&& !visibility::is_override(&checker.ctx, decorator_list)
|
||||
&& !visibility::is_overload(&checker.ctx, decorator_list)
|
||||
{
|
||||
function(
|
||||
&Argumentable::StaticMethod,
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -29,7 +29,10 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["collections", "namedtuple"]
|
||||
|| call_path.as_slice() == ["typing", "NamedTuple"]
|
||||
})
|
||||
|
@ -42,7 +45,10 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["typing", "TypedDict"]
|
||||
})
|
||||
}
|
||||
|
@ -54,7 +60,10 @@ 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| {
|
||||
checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| {
|
||||
call_path.as_slice() == ["typing", "TypeVar"]
|
||||
|| call_path.as_slice() == ["typing", "NewType"]
|
||||
})
|
||||
|
|
|
@ -64,7 +64,7 @@ pub fn invalid_first_argument_name_for_class_method(
|
|||
) -> Option<Diagnostic> {
|
||||
if !matches!(
|
||||
function_type::classify(
|
||||
checker,
|
||||
&checker.ctx,
|
||||
scope,
|
||||
name,
|
||||
decorator_list,
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn invalid_first_argument_name_for_method(
|
|||
) -> Option<Diagnostic> {
|
||||
if !matches!(
|
||||
function_type::classify(
|
||||
checker,
|
||||
&checker.ctx,
|
||||
scope,
|
||||
name,
|
||||
decorator_list,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -27,7 +27,7 @@ pub fn if_needed(checker: &mut Checker, docstring: &Docstring) {
|
|||
) = docstring.kind else {
|
||||
return
|
||||
};
|
||||
if !is_overload(checker, cast::decorator_list(stmt)) {
|
||||
if !is_overload(&checker.ctx, cast::decorator_list(stmt)) {
|
||||
return;
|
||||
}
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
|
|
@ -36,7 +36,11 @@ pub fn non_imperative_mood(
|
|||
.collect::<Vec<CallPath>>();
|
||||
|
||||
if is_test(cast::name(parent))
|
||||
|| is_property(checker, cast::decorator_list(parent), &property_decorators)
|
||||
|| is_property(
|
||||
&checker.ctx,
|
||||
cast::decorator_list(parent),
|
||||
&property_decorators,
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ pub fn not_missing(
|
|||
false
|
||||
}
|
||||
DefinitionKind::Function(stmt) | DefinitionKind::NestedFunction(stmt) => {
|
||||
if is_overload(checker, cast::decorator_list(stmt)) {
|
||||
if is_overload(&checker.ctx, cast::decorator_list(stmt)) {
|
||||
true
|
||||
} else {
|
||||
if checker.settings.rules.enabled(&Rule::PublicFunction) {
|
||||
|
@ -151,8 +151,8 @@ pub fn not_missing(
|
|||
}
|
||||
}
|
||||
DefinitionKind::Method(stmt) => {
|
||||
if is_overload(checker, cast::decorator_list(stmt))
|
||||
|| is_override(checker, cast::decorator_list(stmt))
|
||||
if is_overload(&checker.ctx, cast::decorator_list(stmt))
|
||||
|| is_override(&checker.ctx, cast::decorator_list(stmt))
|
||||
{
|
||||
true
|
||||
} else if is_init(cast::name(stmt)) {
|
||||
|
|
|
@ -820,7 +820,7 @@ fn missing_args(checker: &mut Checker, docstring: &Docstring, docstrings_args: &
|
|||
// If this is a non-static method, skip `cls` or `self`.
|
||||
usize::from(
|
||||
matches!(docstring.kind, DefinitionKind::Method(_))
|
||||
&& !is_staticmethod(checker, cast::decorator_list(parent)),
|
||||
&& !is_staticmethod(&checker.ctx, cast::decorator_list(parent)),
|
||||
),
|
||||
)
|
||||
{
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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| {
|
||||
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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<String> {
|
||||
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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue