mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 21:04:51 +00:00
Implement F811 (RedefinedWhileUnused
) (#1137)
This commit is contained in:
parent
f9a16d9c44
commit
aaeab0ecf1
57 changed files with 882 additions and 186 deletions
|
@ -948,7 +948,7 @@ automatically convert your existing configuration.)
|
|||
Ruff can be used as a drop-in replacement for Flake8 when used (1) without or with a small number of
|
||||
plugins, (2) alongside Black, and (3) on Python 3 code.
|
||||
|
||||
Under those conditions, Ruff implements every rule in Flake8, with the exception of `F811`.
|
||||
Under those conditions, Ruff implements every rule in Flake8.
|
||||
|
||||
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
|
||||
natively, including:
|
||||
|
|
|
@ -135,7 +135,6 @@ fn tokenize_files_to_codes_mapping(value: &str) -> Vec<Token> {
|
|||
}
|
||||
|
||||
/// Parse a 'files-to-codes' mapping, mimicking Flake8's internal logic.
|
||||
///
|
||||
/// See: <https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45>
|
||||
pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair>> {
|
||||
if value.trim().is_empty() {
|
||||
|
|
11
resources/test/fixtures/pyflakes/F811_0.py
vendored
Normal file
11
resources/test/fixtures/pyflakes/F811_0.py
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
def foo(x):
|
||||
return x
|
||||
|
||||
|
||||
@foo
|
||||
def bar():
|
||||
pass
|
||||
|
||||
|
||||
def bar():
|
||||
pass
|
1
resources/test/fixtures/pyflakes/F811_1.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_1.py
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
import fu as FU, bar as FU
|
8
resources/test/fixtures/pyflakes/F811_10.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_10.py
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
"""Test that importing a module twice using a nested does not issue a warning."""
|
||||
try:
|
||||
if True:
|
||||
if True:
|
||||
import os
|
||||
except:
|
||||
import os
|
||||
os.path
|
9
resources/test/fixtures/pyflakes/F811_11.py
vendored
Normal file
9
resources/test/fixtures/pyflakes/F811_11.py
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
try:
|
||||
from aa import mixer
|
||||
except AttributeError:
|
||||
from bb import mixer
|
||||
except RuntimeError:
|
||||
from cc import mixer
|
||||
except:
|
||||
from dd import mixer
|
||||
mixer(123)
|
7
resources/test/fixtures/pyflakes/F811_12.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_12.py
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
try:
|
||||
from aa import mixer
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
from bb import mixer
|
||||
mixer(123)
|
8
resources/test/fixtures/pyflakes/F811_13.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_13.py
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
try:
|
||||
import funca
|
||||
except ImportError:
|
||||
from bb import funca
|
||||
from bb import funcb
|
||||
else:
|
||||
from bbb import funcb
|
||||
print(funca, funcb)
|
10
resources/test/fixtures/pyflakes/F811_14.py
vendored
Normal file
10
resources/test/fixtures/pyflakes/F811_14.py
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
try:
|
||||
import b
|
||||
except ImportError:
|
||||
b = Ellipsis
|
||||
from bb import a
|
||||
else:
|
||||
from aa import a
|
||||
finally:
|
||||
a = 42
|
||||
print(a, b)
|
5
resources/test/fixtures/pyflakes/F811_15.py
vendored
Normal file
5
resources/test/fixtures/pyflakes/F811_15.py
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import fu
|
||||
|
||||
|
||||
def fu():
|
||||
pass
|
9
resources/test/fixtures/pyflakes/F811_16.py
vendored
Normal file
9
resources/test/fixtures/pyflakes/F811_16.py
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""Test that shadowing a global with a nested function generates a warning."""
|
||||
|
||||
import fu
|
||||
|
||||
|
||||
def bar():
|
||||
def baz():
|
||||
def fu():
|
||||
pass
|
10
resources/test/fixtures/pyflakes/F811_17.py
vendored
Normal file
10
resources/test/fixtures/pyflakes/F811_17.py
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
"""Test that shadowing a global name with a nested function generates a warning."""
|
||||
import fu
|
||||
|
||||
|
||||
def bar():
|
||||
import fu
|
||||
|
||||
def baz():
|
||||
def fu():
|
||||
pass
|
14
resources/test/fixtures/pyflakes/F811_18.py
vendored
Normal file
14
resources/test/fixtures/pyflakes/F811_18.py
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
"""Test that a global import which is redefined locally, but used later in another scope does not generate a warning."""
|
||||
|
||||
import unittest, transport
|
||||
|
||||
|
||||
class GetTransportTestCase(unittest.TestCase):
|
||||
def test_get_transport(self):
|
||||
transport = 'transport'
|
||||
self.assertIsNotNone(transport)
|
||||
|
||||
|
||||
class TestTransportMethodArgs(unittest.TestCase):
|
||||
def test_send_defaults(self):
|
||||
transport.Transport()
|
7
resources/test/fixtures/pyflakes/F811_19.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_19.py
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
"""If an imported name is redefined by a class statement which also uses that name in the bases list, no warning is emitted."""
|
||||
|
||||
from fu import bar
|
||||
|
||||
|
||||
class bar(bar):
|
||||
pass
|
1
resources/test/fixtures/pyflakes/F811_2.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_2.py
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
from moo import fu as FU, bar as FU
|
14
resources/test/fixtures/pyflakes/F811_20.py
vendored
Normal file
14
resources/test/fixtures/pyflakes/F811_20.py
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
"""
|
||||
Test that shadowing a global with a class attribute does not produce a
|
||||
warning.
|
||||
"""
|
||||
|
||||
import fu
|
||||
|
||||
|
||||
class bar:
|
||||
# STOPSHIP: This errors.
|
||||
fu = 1
|
||||
|
||||
|
||||
print(fu)
|
1
resources/test/fixtures/pyflakes/F811_3.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_3.py
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
import fu; fu = 3
|
1
resources/test/fixtures/pyflakes/F811_4.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_4.py
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
import fu; fu, bar = 3
|
1
resources/test/fixtures/pyflakes/F811_5.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_5.py
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
import fu; [fu, bar] = 3
|
7
resources/test/fixtures/pyflakes/F811_6.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_6.py
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
"""Test that importing a module twice within an if block does raise a warning."""
|
||||
|
||||
i = 2
|
||||
if i == 1:
|
||||
import os
|
||||
import os
|
||||
os.path
|
8
resources/test/fixtures/pyflakes/F811_7.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_7.py
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
"""Test that importing a module twice in if-else blocks does not raise a warning."""
|
||||
|
||||
i = 2
|
||||
if i == 1:
|
||||
import os
|
||||
else:
|
||||
import os
|
||||
os.path
|
8
resources/test/fixtures/pyflakes/F811_8.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_8.py
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
"""Test that importing a module twice in a try block does raise a warning."""
|
||||
|
||||
try:
|
||||
import os
|
||||
import os
|
||||
except:
|
||||
pass
|
||||
os.path
|
7
resources/test/fixtures/pyflakes/F811_9.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_9.py
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
"""Test that importing a module twice in a try block does not raise a warning."""
|
||||
|
||||
try:
|
||||
import os
|
||||
except:
|
||||
import os
|
||||
os.path
|
105
src/ast/branch_detection.rs
Normal file
105
src/ast/branch_detection.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::ExcepthandlerKind::ExceptHandler;
|
||||
use rustpython_ast::Stmt;
|
||||
use rustpython_parser::ast::StmtKind;
|
||||
|
||||
use crate::ast::types::RefEquality;
|
||||
|
||||
/// Return the common ancestor of `left` and `right` below `stop`, or `None`.
|
||||
fn common_ancestor<'a>(
|
||||
left: &'a RefEquality<'a, Stmt>,
|
||||
right: &'a RefEquality<'a, Stmt>,
|
||||
stop: Option<&'a RefEquality<'a, Stmt>>,
|
||||
depths: &'a FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
child_to_parent: &'a FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
) -> Option<&'a RefEquality<'a, Stmt>> {
|
||||
if let Some(stop) = stop {
|
||||
if left == stop || right == stop {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if left == right {
|
||||
return Some(left);
|
||||
}
|
||||
|
||||
let left_depth = depths.get(left)?;
|
||||
let right_depth = depths.get(right)?;
|
||||
match left_depth.cmp(right_depth) {
|
||||
Ordering::Less => common_ancestor(
|
||||
left,
|
||||
child_to_parent.get(right)?,
|
||||
stop,
|
||||
depths,
|
||||
child_to_parent,
|
||||
),
|
||||
Ordering::Equal => common_ancestor(
|
||||
child_to_parent.get(left)?,
|
||||
child_to_parent.get(right)?,
|
||||
stop,
|
||||
depths,
|
||||
child_to_parent,
|
||||
),
|
||||
Ordering::Greater => common_ancestor(
|
||||
child_to_parent.get(left)?,
|
||||
right,
|
||||
stop,
|
||||
depths,
|
||||
child_to_parent,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the alternative branches for a given node.
|
||||
fn alternatives<'a>(node: &'a RefEquality<'a, Stmt>) -> Vec<Vec<RefEquality<'a, Stmt>>> {
|
||||
match &node.0.node {
|
||||
StmtKind::If { body, .. } => vec![body.iter().map(RefEquality).collect()],
|
||||
StmtKind::Try {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
..
|
||||
} => vec![body.iter().chain(orelse.iter()).map(RefEquality).collect()]
|
||||
.into_iter()
|
||||
.chain(handlers.iter().map(|handler| {
|
||||
let ExceptHandler { body, .. } = &handler.node;
|
||||
body.iter().map(RefEquality).collect()
|
||||
}))
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if `node` is a descendent of any of the nodes in `ancestors`.
|
||||
fn descendant_of<'a>(
|
||||
node: &RefEquality<'a, Stmt>,
|
||||
ancestors: &[RefEquality<'a, Stmt>],
|
||||
stop: &RefEquality<'a, Stmt>,
|
||||
depths: &FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
child_to_parent: &FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
) -> bool {
|
||||
ancestors.iter().any(|ancestor| {
|
||||
common_ancestor(node, ancestor, Some(stop), depths, child_to_parent).is_some()
|
||||
})
|
||||
}
|
||||
|
||||
/// Return `true` if `left` and `right` are on different branches of an `if` or
|
||||
/// `try` statement.
|
||||
pub fn different_forks<'a>(
|
||||
left: &RefEquality<'a, Stmt>,
|
||||
right: &RefEquality<'a, Stmt>,
|
||||
depths: &FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
child_to_parent: &FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
) -> bool {
|
||||
if let Some(ancestor) = common_ancestor(left, right, None, depths, child_to_parent) {
|
||||
for items in alternatives(ancestor) {
|
||||
let l = descendant_of(left, &items, ancestor, depths, child_to_parent);
|
||||
let r = descendant_of(right, &items, ancestor, depths, child_to_parent);
|
||||
if l ^ r {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod branch_detection;
|
||||
pub mod cast;
|
||||
pub mod function_type;
|
||||
pub mod helpers;
|
||||
|
|
103
src/ast/types.rs
103
src/ast/types.rs
|
@ -88,38 +88,117 @@ impl<'a> Scope<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BindingContext {
|
||||
pub defined_by: usize,
|
||||
pub defined_in: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BindingKind {
|
||||
Annotation,
|
||||
Argument,
|
||||
Assignment,
|
||||
// TODO(charlie): This seems to be a catch-all.
|
||||
Binding,
|
||||
LoopVar,
|
||||
Global,
|
||||
Nonlocal,
|
||||
Builtin,
|
||||
ClassDefinition,
|
||||
Definition,
|
||||
FunctionDefinition,
|
||||
Export(Vec<String>),
|
||||
FutureImportation,
|
||||
StarImportation(Option<usize>, Option<String>),
|
||||
Importation(String, String, BindingContext),
|
||||
FromImportation(String, String, BindingContext),
|
||||
SubmoduleImportation(String, String, BindingContext),
|
||||
Importation(String, String),
|
||||
FromImportation(String, String),
|
||||
SubmoduleImportation(String, String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Binding {
|
||||
pub struct Binding<'a> {
|
||||
pub kind: BindingKind,
|
||||
pub range: Range,
|
||||
/// The statement in which the `Binding` was defined.
|
||||
pub source: Option<RefEquality<'a, Stmt>>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used.
|
||||
pub used: Option<(usize, Range)>,
|
||||
/// A list of pointers to `Binding` instances that redefined this binding.
|
||||
pub redefined: Vec<usize>,
|
||||
}
|
||||
|
||||
// Pyflakes defines the following binding hierarchy (via inheritance):
|
||||
// Binding
|
||||
// ExportBinding
|
||||
// Annotation
|
||||
// Argument
|
||||
// Assignment
|
||||
// NamedExprAssignment
|
||||
// Definition
|
||||
// FunctionDefinition
|
||||
// ClassDefinition
|
||||
// Builtin
|
||||
// Importation
|
||||
// SubmoduleImportation
|
||||
// ImportationFrom
|
||||
// StarImportation
|
||||
// FutureImportation
|
||||
|
||||
impl<'a> Binding<'a> {
|
||||
pub fn is_definition(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
BindingKind::ClassDefinition
|
||||
| BindingKind::FunctionDefinition
|
||||
| BindingKind::Builtin
|
||||
| BindingKind::FutureImportation
|
||||
| BindingKind::StarImportation(..)
|
||||
| BindingKind::Importation(..)
|
||||
| BindingKind::FromImportation(..)
|
||||
| BindingKind::SubmoduleImportation(..)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn redefines(&self, existing: &'a Binding) -> bool {
|
||||
match &self.kind {
|
||||
BindingKind::Importation(_, full_name) | BindingKind::FromImportation(_, full_name) => {
|
||||
if let BindingKind::SubmoduleImportation(_, existing_full_name) = &existing.kind {
|
||||
return full_name == existing_full_name;
|
||||
}
|
||||
}
|
||||
BindingKind::SubmoduleImportation(_, full_name) => {
|
||||
if let BindingKind::Importation(_, existing_full_name)
|
||||
| BindingKind::FromImportation(_, existing_full_name)
|
||||
| BindingKind::SubmoduleImportation(_, existing_full_name) = &existing.kind
|
||||
{
|
||||
return full_name == existing_full_name;
|
||||
}
|
||||
}
|
||||
BindingKind::Annotation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::FutureImportation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::StarImportation(..) => {
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
existing.is_definition()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RefEquality<'a, T>(pub &'a T);
|
||||
|
||||
impl<'a, T> std::hash::Hash for RefEquality<'a, T> {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: std::hash::Hasher,
|
||||
{
|
||||
(self.0 as *const T).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
|
||||
fn eq(&self, other: &RefEquality<'b, T>) -> bool {
|
||||
std::ptr::eq(self.0, other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Eq for RefEquality<'a, T> {}
|
||||
|
|
293
src/check_ast.rs
293
src/check_ast.rs
|
@ -1,6 +1,5 @@
|
|||
//! Lint rules based on AST traversal.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
use log::error;
|
||||
|
@ -17,11 +16,10 @@ use crate::ast::helpers::{
|
|||
use crate::ast::operations::extract_all_names;
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
Binding, BindingContext, BindingKind, ClassDef, FunctionDef, Lambda, Node, Range, Scope,
|
||||
ScopeKind,
|
||||
Binding, BindingKind, ClassDef, FunctionDef, Lambda, Node, Range, RefEquality, Scope, ScopeKind,
|
||||
};
|
||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
||||
use crate::ast::{helpers, operations, visitor};
|
||||
use crate::ast::{branch_detection, cast, helpers, operations, visitor};
|
||||
use crate::checks::{Check, CheckCode, CheckKind, DeferralKeyword};
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
|
||||
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
|
@ -38,12 +36,12 @@ use crate::{
|
|||
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
|
||||
flake8_import_conventions, flake8_print, flake8_return, flake8_tidy_imports,
|
||||
flake8_unused_arguments, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks,
|
||||
pylint, pyupgrade,
|
||||
pylint, pyupgrade, visibility,
|
||||
};
|
||||
|
||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
|
||||
type DeferralContext = (Vec<usize>, Vec<usize>);
|
||||
type DeferralContext<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Checker<'a> {
|
||||
|
@ -58,23 +56,24 @@ pub struct Checker<'a> {
|
|||
definitions: Vec<(Definition<'a>, Visibility)>,
|
||||
// Edit tracking.
|
||||
// TODO(charlie): Instead of exposing deletions, wrap in a public API.
|
||||
pub(crate) deletions: FxHashSet<usize>,
|
||||
pub(crate) deletions: FxHashSet<RefEquality<'a, Stmt>>,
|
||||
// Import tracking.
|
||||
pub(crate) from_imports: FxHashMap<&'a str, FxHashSet<&'a str>>,
|
||||
pub(crate) import_aliases: FxHashMap<&'a str, &'a str>,
|
||||
// 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<&'a Stmt>,
|
||||
pub(crate) parent_stack: Vec<usize>,
|
||||
pub(crate) bindings: Vec<Binding>,
|
||||
pub(crate) parents: Vec<RefEquality<'a, Stmt>>,
|
||||
pub(crate) depths: FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
pub(crate) child_to_parent: FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
pub(crate) bindings: Vec<Binding<'a>>,
|
||||
scopes: Vec<Scope<'a>>,
|
||||
scope_stack: Vec<usize>,
|
||||
dead_scopes: Vec<usize>,
|
||||
deferred_string_type_definitions: Vec<(Range, &'a str, bool, DeferralContext)>,
|
||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext)>,
|
||||
deferred_functions: Vec<(&'a Stmt, DeferralContext, VisibleScope)>,
|
||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext)>,
|
||||
deferred_assignments: Vec<(usize, DeferralContext)>,
|
||||
deferred_string_type_definitions: Vec<(Range, &'a str, bool, DeferralContext<'a>)>,
|
||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext<'a>)>,
|
||||
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>,
|
||||
deferred_assignments: Vec<(usize, DeferralContext<'a>)>,
|
||||
// Internal, derivative state.
|
||||
visible_scope: VisibleScope,
|
||||
in_f_string: Option<Range>,
|
||||
|
@ -110,7 +109,8 @@ impl<'a> Checker<'a> {
|
|||
from_imports: FxHashMap::default(),
|
||||
import_aliases: FxHashMap::default(),
|
||||
parents: vec![],
|
||||
parent_stack: vec![],
|
||||
depths: FxHashMap::default(),
|
||||
child_to_parent: FxHashMap::default(),
|
||||
bindings: vec![],
|
||||
scopes: vec![],
|
||||
scope_stack: vec![],
|
||||
|
@ -223,11 +223,7 @@ where
|
|||
if !self.seen_import_boundary
|
||||
&& !helpers::is_assignment_to_a_dunder(node)
|
||||
&& !operations::in_nested_block(
|
||||
&mut self
|
||||
.parent_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|index| self.parents[*index]),
|
||||
&mut self.parents.iter().rev().map(|node| node.0),
|
||||
)
|
||||
{
|
||||
self.seen_import_boundary = true;
|
||||
|
@ -249,6 +245,8 @@ where
|
|||
kind: BindingKind::Global,
|
||||
used: usage,
|
||||
range: Range::from_located(stmt),
|
||||
source: None,
|
||||
redefined: vec![],
|
||||
});
|
||||
scope.values.insert(name, index);
|
||||
}
|
||||
|
@ -287,6 +285,8 @@ where
|
|||
kind: BindingKind::Nonlocal,
|
||||
used: usage,
|
||||
range: Range::from_located(stmt),
|
||||
source: None,
|
||||
redefined: vec![],
|
||||
});
|
||||
scope.values.insert(name, index);
|
||||
}
|
||||
|
@ -318,8 +318,7 @@ where
|
|||
if self.settings.enabled.contains(&CheckCode::F701) {
|
||||
if let Some(check) = pyflakes::checks::break_outside_loop(
|
||||
stmt,
|
||||
&self.parents,
|
||||
&self.parent_stack,
|
||||
&mut self.parents.iter().rev().map(|node| node.0).skip(1),
|
||||
) {
|
||||
self.add_check(check);
|
||||
}
|
||||
|
@ -329,8 +328,7 @@ where
|
|||
if self.settings.enabled.contains(&CheckCode::F702) {
|
||||
if let Some(check) = pyflakes::checks::continue_outside_loop(
|
||||
stmt,
|
||||
&self.parents,
|
||||
&self.parent_stack,
|
||||
&mut self.parents.iter().rev().map(|node| node.0).skip(1),
|
||||
) {
|
||||
self.add_check(check);
|
||||
}
|
||||
|
@ -500,9 +498,11 @@ where
|
|||
self.add_binding(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::Definition,
|
||||
kind: BindingKind::FunctionDefinition,
|
||||
used: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -608,10 +608,11 @@ where
|
|||
kind: BindingKind::SubmoduleImportation(
|
||||
name.to_string(),
|
||||
full_name.to_string(),
|
||||
self.binding_context(),
|
||||
),
|
||||
used: None,
|
||||
range: Range::from_located(alias),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
} else {
|
||||
|
@ -630,7 +631,6 @@ where
|
|||
kind: BindingKind::Importation(
|
||||
name.to_string(),
|
||||
full_name.to_string(),
|
||||
self.binding_context(),
|
||||
),
|
||||
// Treat explicit re-export as usage (e.g., `import applications
|
||||
// as applications`).
|
||||
|
@ -652,6 +652,8 @@ where
|
|||
None
|
||||
},
|
||||
range: Range::from_located(alias),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -795,6 +797,8 @@ where
|
|||
Range::from_located(alias),
|
||||
)),
|
||||
range: Range::from_located(alias),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -825,6 +829,8 @@ where
|
|||
kind: BindingKind::StarImportation(*level, module.clone()),
|
||||
used: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -872,11 +878,7 @@ where
|
|||
self.add_binding(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::FromImportation(
|
||||
name.to_string(),
|
||||
full_name,
|
||||
self.binding_context(),
|
||||
),
|
||||
kind: BindingKind::FromImportation(name.to_string(), full_name),
|
||||
// Treat explicit re-export as usage (e.g., `from .applications
|
||||
// import FastAPI as FastAPI`).
|
||||
used: if alias
|
||||
|
@ -897,6 +899,8 @@ where
|
|||
None
|
||||
},
|
||||
range,
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1143,7 +1147,7 @@ where
|
|||
|
||||
self.deferred_functions.push((
|
||||
stmt,
|
||||
(self.scope_stack.clone(), self.parent_stack.clone()),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
self.visible_scope.clone(),
|
||||
));
|
||||
}
|
||||
|
@ -1221,6 +1225,8 @@ where
|
|||
kind: BindingKind::ClassDefinition,
|
||||
used: None,
|
||||
range: Range::from_located(stmt),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
};
|
||||
|
@ -1256,13 +1262,13 @@ where
|
|||
Range::from_located(expr),
|
||||
value,
|
||||
self.in_annotation,
|
||||
(self.scope_stack.clone(), self.parent_stack.clone()),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
} else {
|
||||
self.deferred_type_definitions.push((
|
||||
expr,
|
||||
self.in_annotation,
|
||||
(self.scope_stack.clone(), self.parent_stack.clone()),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
}
|
||||
return;
|
||||
|
@ -1345,7 +1351,7 @@ where
|
|||
|
||||
self.check_builtin_shadowing(id, Range::from_located(expr), true);
|
||||
|
||||
self.handle_node_store(id, expr, self.current_parent());
|
||||
self.handle_node_store(id, expr);
|
||||
}
|
||||
ExprContext::Del => self.handle_node_delete(expr),
|
||||
}
|
||||
|
@ -2053,7 +2059,7 @@ where
|
|||
Range::from_located(expr),
|
||||
value,
|
||||
self.in_annotation,
|
||||
(self.scope_stack.clone(), self.parent_stack.clone()),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::S104) {
|
||||
|
@ -2136,7 +2142,7 @@ where
|
|||
match &expr.node {
|
||||
ExprKind::Lambda { .. } => {
|
||||
self.deferred_lambdas
|
||||
.push((expr, (self.scope_stack.clone(), self.parent_stack.clone())));
|
||||
.push((expr, (self.scope_stack.clone(), self.parents.clone())));
|
||||
}
|
||||
ExprKind::Call {
|
||||
func,
|
||||
|
@ -2387,7 +2393,6 @@ where
|
|||
ctx: ExprContext::Store,
|
||||
},
|
||||
),
|
||||
self.current_parent(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2402,7 +2407,6 @@ where
|
|||
ctx: ExprContext::Store,
|
||||
},
|
||||
),
|
||||
self.current_parent(),
|
||||
);
|
||||
|
||||
walk_excepthandler(self, excepthandler);
|
||||
|
@ -2484,6 +2488,8 @@ where
|
|||
kind: BindingKind::Argument,
|
||||
used: None,
|
||||
range: Range::from_located(arg),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -2510,14 +2516,20 @@ where
|
|||
|
||||
impl<'a> Checker<'a> {
|
||||
fn push_parent(&mut self, parent: &'a Stmt) {
|
||||
self.parent_stack.push(self.parents.len());
|
||||
self.parents.push(parent);
|
||||
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.parent_stack
|
||||
.pop()
|
||||
.expect("Attempted to pop without scope");
|
||||
self.parents.pop().expect("Attempted to pop without scope");
|
||||
}
|
||||
|
||||
fn push_scope(&mut self, scope: Scope<'a>) {
|
||||
|
@ -2535,12 +2547,15 @@ impl<'a> Checker<'a> {
|
|||
|
||||
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()) {
|
||||
let index = self.bindings.len();
|
||||
self.bindings.push(Binding {
|
||||
kind: BindingKind::Builtin,
|
||||
range: Range::default(),
|
||||
used: None,
|
||||
source: None,
|
||||
redefined: vec![],
|
||||
});
|
||||
scope.values.insert(builtin, index);
|
||||
}
|
||||
|
@ -2551,41 +2566,58 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
|
||||
pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> {
|
||||
self.scope_stack.iter().rev().map(|s| &self.scopes[*s])
|
||||
self.scope_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|index| &self.scopes[*index])
|
||||
}
|
||||
|
||||
pub fn current_parent(&self) -> &'a Stmt {
|
||||
self.parents[*(self.parent_stack.last().expect("No parent found"))]
|
||||
pub fn current_parent(&self) -> &RefEquality<'a, Stmt> {
|
||||
self.parents.iter().rev().next().expect("No parent found")
|
||||
}
|
||||
|
||||
pub fn binding_context(&self) -> BindingContext {
|
||||
let mut rev = self.parent_stack.iter().rev().fuse();
|
||||
let defined_by = *rev.next().expect("Expected to bind within a statement");
|
||||
let defined_in = rev.next().copied();
|
||||
BindingContext {
|
||||
defined_by,
|
||||
defined_in,
|
||||
}
|
||||
pub fn current_grandparent(&self) -> Option<&RefEquality<'a, Stmt>> {
|
||||
self.parents.iter().rev().nth(1)
|
||||
}
|
||||
|
||||
fn add_binding<'b>(&mut self, name: &'b str, binding: Binding)
|
||||
fn add_binding<'b>(&mut self, name: &'b str, binding: Binding<'a>)
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
if self.settings.enabled.contains(&CheckCode::F402) {
|
||||
let scope = self.current_scope();
|
||||
if let Some(index) = scope.values.get(&name) {
|
||||
let existing = &self.bindings[*index];
|
||||
if matches!(binding.kind, BindingKind::LoopVar)
|
||||
&& matches!(
|
||||
let index = self.bindings.len();
|
||||
|
||||
if let Some((stack_index, scope_index)) = self
|
||||
.scope_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find(|(_, scope_index)| self.scopes[**scope_index].values.contains_key(&name))
|
||||
{
|
||||
let existing_index = self.scopes[*scope_index].values.get(&name).unwrap();
|
||||
let existing = &self.bindings[*existing_index];
|
||||
let in_current_scope = stack_index == 0;
|
||||
if !matches!(existing.kind, BindingKind::Builtin)
|
||||
&& existing.source.as_ref().map_or(true, |left| {
|
||||
binding.source.as_ref().map_or(true, |right| {
|
||||
!branch_detection::different_forks(
|
||||
left,
|
||||
right,
|
||||
&self.depths,
|
||||
&self.child_to_parent,
|
||||
)
|
||||
})
|
||||
})
|
||||
{
|
||||
let existing_is_import = matches!(
|
||||
existing.kind,
|
||||
BindingKind::Importation(..)
|
||||
| BindingKind::FromImportation(..)
|
||||
| BindingKind::SubmoduleImportation(..)
|
||||
| BindingKind::StarImportation(..)
|
||||
| BindingKind::FutureImportation
|
||||
)
|
||||
{
|
||||
);
|
||||
if matches!(binding.kind, BindingKind::LoopVar) && existing_is_import {
|
||||
if self.settings.enabled.contains(&CheckCode::F402) {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::ImportShadowedByLoopVar(
|
||||
name.to_string(),
|
||||
|
@ -2594,6 +2626,29 @@ impl<'a> Checker<'a> {
|
|||
binding.range,
|
||||
));
|
||||
}
|
||||
} else if in_current_scope {
|
||||
if existing.used.is_none()
|
||||
&& binding.redefines(existing)
|
||||
&& (!self.settings.dummy_variable_rgx.is_match(name) || existing_is_import)
|
||||
&& !(matches!(existing.kind, BindingKind::FunctionDefinition)
|
||||
&& visibility::is_overload(
|
||||
self,
|
||||
cast::decorator_list(existing.source.as_ref().unwrap().0),
|
||||
))
|
||||
{
|
||||
if self.settings.enabled.contains(&CheckCode::F811) {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::RedefinedWhileUnused(
|
||||
name.to_string(),
|
||||
existing.range.location.row(),
|
||||
),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else if existing_is_import && binding.redefines(existing) {
|
||||
self.bindings[*existing_index].redefined.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2603,13 +2658,11 @@ impl<'a> Checker<'a> {
|
|||
let binding = match scope.values.get(&name) {
|
||||
None => binding,
|
||||
Some(index) => Binding {
|
||||
kind: binding.kind,
|
||||
range: binding.range,
|
||||
used: self.bindings[*index].used,
|
||||
..binding
|
||||
},
|
||||
};
|
||||
|
||||
let index = self.bindings.len();
|
||||
self.bindings.push(binding);
|
||||
|
||||
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
|
||||
|
@ -2646,9 +2699,9 @@ impl<'a> Checker<'a> {
|
|||
// import pyarrow as pa
|
||||
// import pyarrow.csv
|
||||
// print(pa.csv.read_csv("test.csv"))
|
||||
if let BindingKind::Importation(name, full_name, _)
|
||||
| BindingKind::FromImportation(name, full_name, _)
|
||||
| BindingKind::SubmoduleImportation(name, full_name, _) =
|
||||
if let BindingKind::Importation(name, full_name)
|
||||
| BindingKind::FromImportation(name, full_name)
|
||||
| BindingKind::SubmoduleImportation(name, full_name) =
|
||||
&self.bindings[*index].kind
|
||||
{
|
||||
let has_alias = full_name
|
||||
|
@ -2728,10 +2781,12 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_node_store<'b>(&mut self, id: &'b str, expr: &Expr, parent: &Stmt)
|
||||
fn handle_node_store<'b>(&mut self, id: &'b str, expr: &Expr)
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
let parent = self.current_parent().0;
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F823) {
|
||||
let scopes: Vec<&Scope> = self
|
||||
.scope_stack
|
||||
|
@ -2775,6 +2830,8 @@ impl<'a> Checker<'a> {
|
|||
kind: BindingKind::Annotation,
|
||||
used: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
return;
|
||||
|
@ -2791,6 +2848,8 @@ impl<'a> Checker<'a> {
|
|||
kind: BindingKind::LoopVar,
|
||||
used: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
return;
|
||||
|
@ -2803,6 +2862,8 @@ impl<'a> Checker<'a> {
|
|||
kind: BindingKind::Binding,
|
||||
used: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
return;
|
||||
|
@ -2822,6 +2883,8 @@ impl<'a> Checker<'a> {
|
|||
kind: BindingKind::Export(extract_all_names(parent, current, &self.bindings)),
|
||||
used: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
return;
|
||||
|
@ -2833,6 +2896,8 @@ impl<'a> Checker<'a> {
|
|||
kind: BindingKind::Assignment,
|
||||
used: None,
|
||||
range: Range::from_located(expr),
|
||||
source: Some(self.current_parent().clone()),
|
||||
redefined: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -2842,13 +2907,8 @@ impl<'a> Checker<'a> {
|
|||
'b: 'a,
|
||||
{
|
||||
if let ExprKind::Name { id, .. } = &expr.node {
|
||||
if operations::on_conditional_branch(
|
||||
&mut self
|
||||
.parent_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|index| self.parents[*index]),
|
||||
) {
|
||||
if operations::on_conditional_branch(&mut self.parents.iter().rev().map(|node| node.0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2892,7 +2952,7 @@ impl<'a> Checker<'a> {
|
|||
self.deferred_type_definitions.pop()
|
||||
{
|
||||
self.scope_stack = scopes;
|
||||
self.parent_stack = parents;
|
||||
self.parents = parents;
|
||||
self.in_annotation = in_annotation;
|
||||
self.in_type_definition = true;
|
||||
self.in_deferred_type_definition = true;
|
||||
|
@ -2925,7 +2985,7 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
for (expr, (in_annotation, (scopes, parents))) in allocator.iter().zip(stacks) {
|
||||
self.scope_stack = scopes;
|
||||
self.parent_stack = parents;
|
||||
self.parents = parents;
|
||||
self.in_annotation = in_annotation;
|
||||
self.in_type_definition = true;
|
||||
self.in_deferred_string_type_definition = true;
|
||||
|
@ -2938,7 +2998,7 @@ impl<'a> Checker<'a> {
|
|||
fn check_deferred_functions(&mut self) {
|
||||
while let Some((stmt, (scopes, parents), visibility)) = self.deferred_functions.pop() {
|
||||
self.scope_stack = scopes.clone();
|
||||
self.parent_stack = parents.clone();
|
||||
self.parents = parents.clone();
|
||||
self.visible_scope = visibility;
|
||||
|
||||
match &stmt.node {
|
||||
|
@ -2983,7 +3043,7 @@ impl<'a> Checker<'a> {
|
|||
fn check_deferred_lambdas(&mut self) {
|
||||
while let Some((expr, (scopes, parents))) = self.deferred_lambdas.pop() {
|
||||
self.scope_stack = scopes.clone();
|
||||
self.parent_stack = parents.clone();
|
||||
self.parents = parents.clone();
|
||||
|
||||
if let ExprKind::Lambda { args, body } = &expr.node {
|
||||
self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body })));
|
||||
|
@ -3038,6 +3098,7 @@ impl<'a> Checker<'a> {
|
|||
fn check_dead_scopes(&mut self) {
|
||||
if !self.settings.enabled.contains(&CheckCode::F401)
|
||||
&& !self.settings.enabled.contains(&CheckCode::F405)
|
||||
&& !self.settings.enabled.contains(&CheckCode::F811)
|
||||
&& !self.settings.enabled.contains(&CheckCode::F822)
|
||||
{
|
||||
return;
|
||||
|
@ -3072,6 +3133,41 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F811) {
|
||||
for (name, index) in &scope.values {
|
||||
let binding = &self.bindings[*index];
|
||||
|
||||
if matches!(
|
||||
binding.kind,
|
||||
BindingKind::Importation(..)
|
||||
| BindingKind::FromImportation(..)
|
||||
| BindingKind::SubmoduleImportation(..)
|
||||
| BindingKind::StarImportation(..)
|
||||
| BindingKind::FutureImportation
|
||||
) {
|
||||
// Skip used exports from `__all__`
|
||||
if binding.used.is_some()
|
||||
|| all_names
|
||||
.as_ref()
|
||||
.map(|names| names.contains(name))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for index in &binding.redefined {
|
||||
checks.push(Check::new(
|
||||
CheckKind::RedefinedWhileUnused(
|
||||
(*name).to_string(),
|
||||
binding.range.location.row(),
|
||||
),
|
||||
self.bindings[*index].range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F405) {
|
||||
if scope.import_starred {
|
||||
if let Some(all_binding) = all_binding {
|
||||
|
@ -3108,15 +3204,17 @@ impl<'a> Checker<'a> {
|
|||
// Collect all unused imports by location. (Multiple unused imports at the same
|
||||
// location indicates an `import from`.)
|
||||
type UnusedImport<'a> = (&'a String, &'a Range);
|
||||
type BindingContext<'a, 'b> =
|
||||
(&'a RefEquality<'b, Stmt>, Option<&'a RefEquality<'b, Stmt>>);
|
||||
|
||||
let mut unused: BTreeMap<(usize, Option<usize>), Vec<UnusedImport>> =
|
||||
BTreeMap::new();
|
||||
let mut unused: FxHashMap<BindingContext, Vec<UnusedImport>> = FxHashMap::default();
|
||||
|
||||
for (name, index) in &scope.values {
|
||||
let binding = &self.bindings[*index];
|
||||
let (BindingKind::Importation(_, full_name, context)
|
||||
| BindingKind::SubmoduleImportation(_, full_name, context)
|
||||
| BindingKind::FromImportation(_, full_name, context)) = &binding.kind else { continue; };
|
||||
|
||||
let (BindingKind::Importation(_, full_name)
|
||||
| BindingKind::SubmoduleImportation(_, full_name)
|
||||
| BindingKind::FromImportation(_, full_name)) = &binding.kind else { continue; };
|
||||
|
||||
// Skip used exports from `__all__`
|
||||
if binding.used.is_some()
|
||||
|
@ -3128,24 +3226,23 @@ impl<'a> Checker<'a> {
|
|||
continue;
|
||||
}
|
||||
|
||||
let defined_by = binding.source.as_ref().unwrap();
|
||||
let defined_in = self.child_to_parent.get(defined_by);
|
||||
unused
|
||||
.entry((context.defined_by, context.defined_in))
|
||||
.entry((defined_by, defined_in))
|
||||
.or_default()
|
||||
.push((full_name, &binding.range));
|
||||
}
|
||||
|
||||
for ((defined_by, defined_in), unused_imports) in unused {
|
||||
let child = self.parents[defined_by];
|
||||
let parent = defined_in.map(|defined_in| self.parents[defined_in]);
|
||||
let child = defined_by.0;
|
||||
let parent = defined_in.map(|defined_in| defined_in.0);
|
||||
|
||||
let ignore_init = self.settings.ignore_init_module_imports
|
||||
&& self.path.ends_with("__init__.py");
|
||||
let fix = if !ignore_init && self.patch(&CheckCode::F401) {
|
||||
let deleted: Vec<&Stmt> = self
|
||||
.deletions
|
||||
.iter()
|
||||
.map(|index| self.parents[*index])
|
||||
.collect();
|
||||
let deleted: Vec<&Stmt> =
|
||||
self.deletions.iter().map(|node| node.0).collect();
|
||||
match pyflakes::fixes::remove_unused_imports(
|
||||
self.locator,
|
||||
&unused_imports,
|
||||
|
@ -3155,7 +3252,7 @@ impl<'a> Checker<'a> {
|
|||
) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
self.deletions.insert(defined_by);
|
||||
self.deletions.insert(defined_by.clone());
|
||||
}
|
||||
Some(fix)
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ pub enum CheckCode {
|
|||
F706,
|
||||
F707,
|
||||
F722,
|
||||
F811,
|
||||
F821,
|
||||
F822,
|
||||
F823,
|
||||
|
@ -616,6 +617,7 @@ pub enum CheckKind {
|
|||
PercentFormatStarRequiresSequence,
|
||||
PercentFormatUnsupportedFormatCharacter(char),
|
||||
RaiseNotImplemented,
|
||||
RedefinedWhileUnused(String, usize),
|
||||
ReturnOutsideFunction,
|
||||
StringDotFormatExtraNamedArguments(Vec<String>),
|
||||
StringDotFormatExtraPositionalArguments(Vec<String>),
|
||||
|
@ -932,6 +934,7 @@ impl CheckCode {
|
|||
CheckCode::F706 => CheckKind::ReturnOutsideFunction,
|
||||
CheckCode::F707 => CheckKind::DefaultExceptNotLast,
|
||||
CheckCode::F722 => CheckKind::ForwardAnnotationSyntaxError("...".to_string()),
|
||||
CheckCode::F811 => CheckKind::RedefinedWhileUnused("...".to_string(), 1),
|
||||
CheckCode::F821 => CheckKind::UndefinedName("...".to_string()),
|
||||
CheckCode::F822 => CheckKind::UndefinedExport("...".to_string()),
|
||||
CheckCode::F823 => CheckKind::UndefinedLocal("...".to_string()),
|
||||
|
@ -1359,6 +1362,7 @@ impl CheckCode {
|
|||
CheckCode::F706 => CheckCategory::Pyflakes,
|
||||
CheckCode::F707 => CheckCategory::Pyflakes,
|
||||
CheckCode::F722 => CheckCategory::Pyflakes,
|
||||
CheckCode::F811 => CheckCategory::Pyflakes,
|
||||
CheckCode::F821 => CheckCategory::Pyflakes,
|
||||
CheckCode::F822 => CheckCategory::Pyflakes,
|
||||
CheckCode::F823 => CheckCategory::Pyflakes,
|
||||
|
@ -1508,6 +1512,7 @@ impl CheckKind {
|
|||
CheckKind::TypeComparison => &CheckCode::E721,
|
||||
CheckKind::UndefinedExport(_) => &CheckCode::F822,
|
||||
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
|
||||
CheckKind::RedefinedWhileUnused(..) => &CheckCode::F811,
|
||||
CheckKind::UndefinedName(_) => &CheckCode::F821,
|
||||
CheckKind::UnusedImport(..) => &CheckCode::F401,
|
||||
CheckKind::UnusedVariable(_) => &CheckCode::F841,
|
||||
|
@ -1846,6 +1851,9 @@ impl CheckKind {
|
|||
CheckKind::RaiseNotImplemented => {
|
||||
"`raise NotImplemented` should be `raise NotImplementedError`".to_string()
|
||||
}
|
||||
CheckKind::RedefinedWhileUnused(name, line) => {
|
||||
format!("Redefinition of unused `{name}` from line {line}")
|
||||
}
|
||||
CheckKind::ReturnOutsideFunction => {
|
||||
"`return` statement outside of a function/method".to_string()
|
||||
}
|
||||
|
|
|
@ -243,6 +243,8 @@ pub enum CheckCodePrefix {
|
|||
F72,
|
||||
F722,
|
||||
F8,
|
||||
F81,
|
||||
F811,
|
||||
F82,
|
||||
F821,
|
||||
F822,
|
||||
|
@ -1026,6 +1028,7 @@ impl CheckCodePrefix {
|
|||
CheckCode::F706,
|
||||
CheckCode::F707,
|
||||
CheckCode::F722,
|
||||
CheckCode::F811,
|
||||
CheckCode::F821,
|
||||
CheckCode::F822,
|
||||
CheckCode::F823,
|
||||
|
@ -1158,12 +1161,15 @@ impl CheckCodePrefix {
|
|||
CheckCodePrefix::F72 => vec![CheckCode::F722],
|
||||
CheckCodePrefix::F722 => vec![CheckCode::F722],
|
||||
CheckCodePrefix::F8 => vec![
|
||||
CheckCode::F811,
|
||||
CheckCode::F821,
|
||||
CheckCode::F822,
|
||||
CheckCode::F823,
|
||||
CheckCode::F831,
|
||||
CheckCode::F841,
|
||||
],
|
||||
CheckCodePrefix::F81 => vec![CheckCode::F811],
|
||||
CheckCodePrefix::F811 => vec![CheckCode::F811],
|
||||
CheckCodePrefix::F82 => vec![CheckCode::F821, CheckCode::F822, CheckCode::F823],
|
||||
CheckCodePrefix::F821 => vec![CheckCode::F821],
|
||||
CheckCodePrefix::F822 => vec![CheckCode::F822],
|
||||
|
@ -2033,6 +2039,8 @@ impl CheckCodePrefix {
|
|||
CheckCodePrefix::F72 => SuffixLength::Two,
|
||||
CheckCodePrefix::F722 => SuffixLength::Three,
|
||||
CheckCodePrefix::F8 => SuffixLength::One,
|
||||
CheckCodePrefix::F81 => SuffixLength::Two,
|
||||
CheckCodePrefix::F811 => SuffixLength::Three,
|
||||
CheckCodePrefix::F82 => SuffixLength::Two,
|
||||
CheckCodePrefix::F821 => SuffixLength::Three,
|
||||
CheckCodePrefix::F822 => SuffixLength::Three,
|
||||
|
|
|
@ -19,24 +19,14 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
|||
};
|
||||
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
if matches!(
|
||||
checker.parents[context.defined_by].node,
|
||||
StmtKind::Expr { .. }
|
||||
) {
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
.iter()
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
match helpers::remove_stmt(
|
||||
checker.parents[context.defined_by],
|
||||
context.defined_in.map(|index| checker.parents[index]),
|
||||
&deleted,
|
||||
) {
|
||||
let defined_by = checker.current_parent();
|
||||
let defined_in = checker.current_grandparent();
|
||||
if matches!(defined_by.0.node, StmtKind::Expr { .. }) {
|
||||
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
|
||||
match helpers::remove_stmt(defined_by.0, defined_in.map(|node| node.0), &deleted) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
checker.deletions.insert(context.defined_by);
|
||||
checker.deletions.insert(defined_by.clone());
|
||||
}
|
||||
check.amend(fix);
|
||||
}
|
||||
|
|
|
@ -554,12 +554,13 @@ pub fn starred_expressions(
|
|||
}
|
||||
|
||||
/// F701
|
||||
pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]) -> Option<Check> {
|
||||
pub fn break_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Check> {
|
||||
let mut allowed: bool = false;
|
||||
let mut parent = stmt;
|
||||
for index in parent_stack.iter().rev() {
|
||||
let child = parent;
|
||||
parent = parents[*index];
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
|
@ -569,7 +570,6 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
|
@ -577,6 +577,7 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
|
@ -590,16 +591,13 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
|||
}
|
||||
|
||||
/// F702
|
||||
pub fn continue_outside_loop(
|
||||
stmt: &Stmt,
|
||||
parents: &[&Stmt],
|
||||
parent_stack: &[usize],
|
||||
pub fn continue_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Check> {
|
||||
let mut allowed: bool = false;
|
||||
let mut parent = stmt;
|
||||
for index in parent_stack.iter().rev() {
|
||||
let child = parent;
|
||||
parent = parents[*index];
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
|
@ -609,7 +607,6 @@ pub fn continue_outside_loop(
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
|
@ -617,6 +614,7 @@ pub fn continue_outside_loop(
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
|
|
|
@ -66,6 +66,27 @@ mod tests {
|
|||
#[test_case(CheckCode::F706, Path::new("F706.py"); "F706")]
|
||||
#[test_case(CheckCode::F707, Path::new("F707.py"); "F707")]
|
||||
#[test_case(CheckCode::F722, Path::new("F722.py"); "F722")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_0.py"); "F811_0")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_1.py"); "F811_1")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_2.py"); "F811_2")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_3.py"); "F811_3")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_4.py"); "F811_4")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_5.py"); "F811_5")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_6.py"); "F811_6")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_7.py"); "F811_7")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_8.py"); "F811_8")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_9.py"); "F811_9")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_10.py"); "F811_10")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_11.py"); "F811_11")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_12.py"); "F811_12")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_13.py"); "F811_13")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_14.py"); "F811_14")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_15.py"); "F811_15")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_16.py"); "F811_16")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_17.py"); "F811_17")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_18.py"); "F811_18")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_19.py"); "F811_19")]
|
||||
#[test_case(CheckCode::F811, Path::new("F811_20.py"); "F811_20")]
|
||||
#[test_case(CheckCode::F821, Path::new("F821_0.py"); "F821_0")]
|
||||
#[test_case(CheckCode::F821, Path::new("F821_1.py"); "F821_1")]
|
||||
#[test_case(CheckCode::F821, Path::new("F821_2.py"); "F821_2")]
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- bar
|
||||
- 6
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 0
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- FU
|
||||
- 1
|
||||
location:
|
||||
row: 1
|
||||
column: 17
|
||||
end_location:
|
||||
row: 1
|
||||
column: 26
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- mixer
|
||||
- 2
|
||||
location:
|
||||
row: 6
|
||||
column: 19
|
||||
end_location:
|
||||
row: 6
|
||||
column: 24
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- fu
|
||||
- 1
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- fu
|
||||
- 3
|
||||
location:
|
||||
row: 8
|
||||
column: 8
|
||||
end_location:
|
||||
row: 10
|
||||
column: 0
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- fu
|
||||
- 2
|
||||
location:
|
||||
row: 6
|
||||
column: 11
|
||||
end_location:
|
||||
row: 6
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- fu
|
||||
- 6
|
||||
location:
|
||||
row: 9
|
||||
column: 8
|
||||
end_location:
|
||||
row: 11
|
||||
column: 0
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- FU
|
||||
- 1
|
||||
location:
|
||||
row: 1
|
||||
column: 26
|
||||
end_location:
|
||||
row: 1
|
||||
column: 35
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- fu
|
||||
- 1
|
||||
location:
|
||||
row: 1
|
||||
column: 11
|
||||
end_location:
|
||||
row: 1
|
||||
column: 13
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- fu
|
||||
- 1
|
||||
location:
|
||||
row: 1
|
||||
column: 11
|
||||
end_location:
|
||||
row: 1
|
||||
column: 13
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- fu
|
||||
- 1
|
||||
location:
|
||||
row: 1
|
||||
column: 12
|
||||
end_location:
|
||||
row: 1
|
||||
column: 14
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- os
|
||||
- 5
|
||||
location:
|
||||
row: 6
|
||||
column: 11
|
||||
end_location:
|
||||
row: 6
|
||||
column: 13
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
RedefinedWhileUnused:
|
||||
- os
|
||||
- 4
|
||||
location:
|
||||
row: 5
|
||||
column: 11
|
||||
end_location:
|
||||
row: 5
|
||||
column: 13
|
||||
fix: ~
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/pyflakes/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
|
@ -30,13 +30,13 @@ fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -
|
|||
// e.g. module=sys object=exit
|
||||
// `import sys` -> `sys.exit`
|
||||
// `import sys as sys2` -> `sys2.exit`
|
||||
BindingKind::Importation(name, full_name, _) if full_name == module => {
|
||||
BindingKind::Importation(name, full_name) if full_name == module => {
|
||||
Some(format!("{name}.{member}"))
|
||||
}
|
||||
// e.g. module=os.path object=join
|
||||
// `from os.path import join` -> `join`
|
||||
// `from os.path import join as join2` -> `join2`
|
||||
BindingKind::FromImportation(name, full_name, _)
|
||||
BindingKind::FromImportation(name, full_name)
|
||||
if full_name == &format!("{module}.{member}") =>
|
||||
{
|
||||
Some(name.to_string())
|
||||
|
@ -50,7 +50,7 @@ fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -
|
|||
}
|
||||
// e.g. module=os.path object=join
|
||||
// `import os.path ` -> `os.path.join`
|
||||
BindingKind::SubmoduleImportation(_, full_name, _) if full_name == module => {
|
||||
BindingKind::SubmoduleImportation(_, full_name) if full_name == module => {
|
||||
Some(format!("{full_name}.{member}"))
|
||||
}
|
||||
// Non-imports.
|
||||
|
|
|
@ -13,11 +13,7 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp
|
|||
return;
|
||||
}
|
||||
let scope = checker.current_scope();
|
||||
let parents: Vec<&Stmt> = checker
|
||||
.parent_stack
|
||||
.iter()
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
let parents: Vec<&Stmt> = checker.parents.iter().map(|node| node.0).collect();
|
||||
let Some(mut check) = checks::super_args(scope, &parents, expr, func, args) else {
|
||||
return;
|
||||
};
|
||||
|
|
|
@ -62,23 +62,21 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
|
|||
),
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
.iter()
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
|
||||
let defined_by = checker.current_parent();
|
||||
let defined_in = checker.current_grandparent();
|
||||
match fixes::remove_unnecessary_future_import(
|
||||
checker.locator,
|
||||
&removable_index,
|
||||
checker.parents[context.defined_by],
|
||||
context.defined_in.map(|index| checker.parents[index]),
|
||||
defined_by.0,
|
||||
defined_in.map(|node| node.0),
|
||||
&deleted,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
checker.deletions.insert(context.defined_by);
|
||||
checker.deletions.insert(defined_by.clone());
|
||||
}
|
||||
check.amend(fix);
|
||||
}
|
||||
|
|
|
@ -13,21 +13,13 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr,
|
|||
return;
|
||||
};
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
.iter()
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
|
||||
match helpers::remove_stmt(
|
||||
checker.parents[context.defined_by],
|
||||
context.defined_in.map(|index| checker.parents[index]),
|
||||
&deleted,
|
||||
) {
|
||||
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
|
||||
let defined_by = checker.current_parent();
|
||||
let defined_in = checker.current_grandparent();
|
||||
match helpers::remove_stmt(defined_by.0, defined_in.map(|node| node.0), &deleted) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
checker.deletions.insert(context.defined_by);
|
||||
checker.deletions.insert(defined_by.clone());
|
||||
}
|
||||
check.amend(fix);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue