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
|
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.
|
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
|
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
|
||||||
natively, including:
|
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.
|
/// Parse a 'files-to-codes' mapping, mimicking Flake8's internal logic.
|
||||||
///
|
|
||||||
/// See: <https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45>
|
/// 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>> {
|
pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair>> {
|
||||||
if value.trim().is_empty() {
|
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 cast;
|
||||||
pub mod function_type;
|
pub mod function_type;
|
||||||
pub mod helpers;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum BindingKind {
|
pub enum BindingKind {
|
||||||
Annotation,
|
Annotation,
|
||||||
Argument,
|
Argument,
|
||||||
Assignment,
|
Assignment,
|
||||||
// TODO(charlie): This seems to be a catch-all.
|
|
||||||
Binding,
|
Binding,
|
||||||
LoopVar,
|
LoopVar,
|
||||||
Global,
|
Global,
|
||||||
Nonlocal,
|
Nonlocal,
|
||||||
Builtin,
|
Builtin,
|
||||||
ClassDefinition,
|
ClassDefinition,
|
||||||
Definition,
|
FunctionDefinition,
|
||||||
Export(Vec<String>),
|
Export(Vec<String>),
|
||||||
FutureImportation,
|
FutureImportation,
|
||||||
StarImportation(Option<usize>, Option<String>),
|
StarImportation(Option<usize>, Option<String>),
|
||||||
Importation(String, String, BindingContext),
|
Importation(String, String),
|
||||||
FromImportation(String, String, BindingContext),
|
FromImportation(String, String),
|
||||||
SubmoduleImportation(String, String, BindingContext),
|
SubmoduleImportation(String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Binding {
|
pub struct Binding<'a> {
|
||||||
pub kind: BindingKind,
|
pub kind: BindingKind,
|
||||||
pub range: Range,
|
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
|
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||||
/// the binding was last used.
|
/// the binding was last used.
|
||||||
pub used: Option<(usize, Range)>,
|
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.
|
//! Lint rules based on AST traversal.
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
|
@ -17,11 +16,10 @@ use crate::ast::helpers::{
|
||||||
use crate::ast::operations::extract_all_names;
|
use crate::ast::operations::extract_all_names;
|
||||||
use crate::ast::relocate::relocate_expr;
|
use crate::ast::relocate::relocate_expr;
|
||||||
use crate::ast::types::{
|
use crate::ast::types::{
|
||||||
Binding, BindingContext, BindingKind, ClassDef, FunctionDef, Lambda, Node, Range, Scope,
|
Binding, BindingKind, ClassDef, FunctionDef, Lambda, Node, Range, RefEquality, Scope, ScopeKind,
|
||||||
ScopeKind,
|
|
||||||
};
|
};
|
||||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
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::checks::{Check, CheckCode, CheckKind, DeferralKeyword};
|
||||||
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
|
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
|
||||||
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
|
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_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
|
||||||
flake8_import_conventions, flake8_print, flake8_return, flake8_tidy_imports,
|
flake8_import_conventions, flake8_print, flake8_return, flake8_tidy_imports,
|
||||||
flake8_unused_arguments, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks,
|
flake8_unused_arguments, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks,
|
||||||
pylint, pyupgrade,
|
pylint, pyupgrade, visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
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)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Checker<'a> {
|
pub struct Checker<'a> {
|
||||||
|
@ -58,23 +56,24 @@ pub struct Checker<'a> {
|
||||||
definitions: Vec<(Definition<'a>, Visibility)>,
|
definitions: Vec<(Definition<'a>, Visibility)>,
|
||||||
// Edit tracking.
|
// Edit tracking.
|
||||||
// TODO(charlie): Instead of exposing deletions, wrap in a public API.
|
// 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.
|
// Import tracking.
|
||||||
pub(crate) from_imports: FxHashMap<&'a str, FxHashSet<&'a str>>,
|
pub(crate) from_imports: FxHashMap<&'a str, FxHashSet<&'a str>>,
|
||||||
pub(crate) import_aliases: FxHashMap<&'a str, &'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
|
// Retain all scopes and parent nodes, along with a stack of indexes to track which are active
|
||||||
// at various points in time.
|
// at various points in time.
|
||||||
pub(crate) parents: Vec<&'a Stmt>,
|
pub(crate) parents: Vec<RefEquality<'a, Stmt>>,
|
||||||
pub(crate) parent_stack: Vec<usize>,
|
pub(crate) depths: FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||||
pub(crate) bindings: Vec<Binding>,
|
pub(crate) child_to_parent: FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||||
|
pub(crate) bindings: Vec<Binding<'a>>,
|
||||||
scopes: Vec<Scope<'a>>,
|
scopes: Vec<Scope<'a>>,
|
||||||
scope_stack: Vec<usize>,
|
scope_stack: Vec<usize>,
|
||||||
dead_scopes: Vec<usize>,
|
dead_scopes: Vec<usize>,
|
||||||
deferred_string_type_definitions: Vec<(Range, &'a str, bool, DeferralContext)>,
|
deferred_string_type_definitions: Vec<(Range, &'a str, bool, DeferralContext<'a>)>,
|
||||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext)>,
|
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext<'a>)>,
|
||||||
deferred_functions: Vec<(&'a Stmt, DeferralContext, VisibleScope)>,
|
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
||||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext)>,
|
deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>,
|
||||||
deferred_assignments: Vec<(usize, DeferralContext)>,
|
deferred_assignments: Vec<(usize, DeferralContext<'a>)>,
|
||||||
// Internal, derivative state.
|
// Internal, derivative state.
|
||||||
visible_scope: VisibleScope,
|
visible_scope: VisibleScope,
|
||||||
in_f_string: Option<Range>,
|
in_f_string: Option<Range>,
|
||||||
|
@ -110,7 +109,8 @@ impl<'a> Checker<'a> {
|
||||||
from_imports: FxHashMap::default(),
|
from_imports: FxHashMap::default(),
|
||||||
import_aliases: FxHashMap::default(),
|
import_aliases: FxHashMap::default(),
|
||||||
parents: vec![],
|
parents: vec![],
|
||||||
parent_stack: vec![],
|
depths: FxHashMap::default(),
|
||||||
|
child_to_parent: FxHashMap::default(),
|
||||||
bindings: vec![],
|
bindings: vec![],
|
||||||
scopes: vec![],
|
scopes: vec![],
|
||||||
scope_stack: vec![],
|
scope_stack: vec![],
|
||||||
|
@ -223,11 +223,7 @@ where
|
||||||
if !self.seen_import_boundary
|
if !self.seen_import_boundary
|
||||||
&& !helpers::is_assignment_to_a_dunder(node)
|
&& !helpers::is_assignment_to_a_dunder(node)
|
||||||
&& !operations::in_nested_block(
|
&& !operations::in_nested_block(
|
||||||
&mut self
|
&mut self.parents.iter().rev().map(|node| node.0),
|
||||||
.parent_stack
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|index| self.parents[*index]),
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.seen_import_boundary = true;
|
self.seen_import_boundary = true;
|
||||||
|
@ -249,6 +245,8 @@ where
|
||||||
kind: BindingKind::Global,
|
kind: BindingKind::Global,
|
||||||
used: usage,
|
used: usage,
|
||||||
range: Range::from_located(stmt),
|
range: Range::from_located(stmt),
|
||||||
|
source: None,
|
||||||
|
redefined: vec![],
|
||||||
});
|
});
|
||||||
scope.values.insert(name, index);
|
scope.values.insert(name, index);
|
||||||
}
|
}
|
||||||
|
@ -287,6 +285,8 @@ where
|
||||||
kind: BindingKind::Nonlocal,
|
kind: BindingKind::Nonlocal,
|
||||||
used: usage,
|
used: usage,
|
||||||
range: Range::from_located(stmt),
|
range: Range::from_located(stmt),
|
||||||
|
source: None,
|
||||||
|
redefined: vec![],
|
||||||
});
|
});
|
||||||
scope.values.insert(name, index);
|
scope.values.insert(name, index);
|
||||||
}
|
}
|
||||||
|
@ -318,8 +318,7 @@ where
|
||||||
if self.settings.enabled.contains(&CheckCode::F701) {
|
if self.settings.enabled.contains(&CheckCode::F701) {
|
||||||
if let Some(check) = pyflakes::checks::break_outside_loop(
|
if let Some(check) = pyflakes::checks::break_outside_loop(
|
||||||
stmt,
|
stmt,
|
||||||
&self.parents,
|
&mut self.parents.iter().rev().map(|node| node.0).skip(1),
|
||||||
&self.parent_stack,
|
|
||||||
) {
|
) {
|
||||||
self.add_check(check);
|
self.add_check(check);
|
||||||
}
|
}
|
||||||
|
@ -329,8 +328,7 @@ where
|
||||||
if self.settings.enabled.contains(&CheckCode::F702) {
|
if self.settings.enabled.contains(&CheckCode::F702) {
|
||||||
if let Some(check) = pyflakes::checks::continue_outside_loop(
|
if let Some(check) = pyflakes::checks::continue_outside_loop(
|
||||||
stmt,
|
stmt,
|
||||||
&self.parents,
|
&mut self.parents.iter().rev().map(|node| node.0).skip(1),
|
||||||
&self.parent_stack,
|
|
||||||
) {
|
) {
|
||||||
self.add_check(check);
|
self.add_check(check);
|
||||||
}
|
}
|
||||||
|
@ -500,9 +498,11 @@ where
|
||||||
self.add_binding(
|
self.add_binding(
|
||||||
name,
|
name,
|
||||||
Binding {
|
Binding {
|
||||||
kind: BindingKind::Definition,
|
kind: BindingKind::FunctionDefinition,
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(stmt),
|
range: Range::from_located(stmt),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -608,10 +608,11 @@ where
|
||||||
kind: BindingKind::SubmoduleImportation(
|
kind: BindingKind::SubmoduleImportation(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
full_name.to_string(),
|
full_name.to_string(),
|
||||||
self.binding_context(),
|
|
||||||
),
|
),
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(alias),
|
range: Range::from_located(alias),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -630,7 +631,6 @@ where
|
||||||
kind: BindingKind::Importation(
|
kind: BindingKind::Importation(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
full_name.to_string(),
|
full_name.to_string(),
|
||||||
self.binding_context(),
|
|
||||||
),
|
),
|
||||||
// Treat explicit re-export as usage (e.g., `import applications
|
// Treat explicit re-export as usage (e.g., `import applications
|
||||||
// as applications`).
|
// as applications`).
|
||||||
|
@ -652,6 +652,8 @@ where
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
range: Range::from_located(alias),
|
range: Range::from_located(alias),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -795,6 +797,8 @@ where
|
||||||
Range::from_located(alias),
|
Range::from_located(alias),
|
||||||
)),
|
)),
|
||||||
range: 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()),
|
kind: BindingKind::StarImportation(*level, module.clone()),
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(stmt),
|
range: Range::from_located(stmt),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -872,11 +878,7 @@ where
|
||||||
self.add_binding(
|
self.add_binding(
|
||||||
name,
|
name,
|
||||||
Binding {
|
Binding {
|
||||||
kind: BindingKind::FromImportation(
|
kind: BindingKind::FromImportation(name.to_string(), full_name),
|
||||||
name.to_string(),
|
|
||||||
full_name,
|
|
||||||
self.binding_context(),
|
|
||||||
),
|
|
||||||
// Treat explicit re-export as usage (e.g., `from .applications
|
// Treat explicit re-export as usage (e.g., `from .applications
|
||||||
// import FastAPI as FastAPI`).
|
// import FastAPI as FastAPI`).
|
||||||
used: if alias
|
used: if alias
|
||||||
|
@ -897,6 +899,8 @@ where
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
range,
|
range,
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1143,7 +1147,7 @@ where
|
||||||
|
|
||||||
self.deferred_functions.push((
|
self.deferred_functions.push((
|
||||||
stmt,
|
stmt,
|
||||||
(self.scope_stack.clone(), self.parent_stack.clone()),
|
(self.scope_stack.clone(), self.parents.clone()),
|
||||||
self.visible_scope.clone(),
|
self.visible_scope.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -1221,6 +1225,8 @@ where
|
||||||
kind: BindingKind::ClassDefinition,
|
kind: BindingKind::ClassDefinition,
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(stmt),
|
range: Range::from_located(stmt),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1256,13 +1262,13 @@ where
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
value,
|
value,
|
||||||
self.in_annotation,
|
self.in_annotation,
|
||||||
(self.scope_stack.clone(), self.parent_stack.clone()),
|
(self.scope_stack.clone(), self.parents.clone()),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
self.deferred_type_definitions.push((
|
self.deferred_type_definitions.push((
|
||||||
expr,
|
expr,
|
||||||
self.in_annotation,
|
self.in_annotation,
|
||||||
(self.scope_stack.clone(), self.parent_stack.clone()),
|
(self.scope_stack.clone(), self.parents.clone()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -1345,7 +1351,7 @@ where
|
||||||
|
|
||||||
self.check_builtin_shadowing(id, Range::from_located(expr), true);
|
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),
|
ExprContext::Del => self.handle_node_delete(expr),
|
||||||
}
|
}
|
||||||
|
@ -2053,7 +2059,7 @@ where
|
||||||
Range::from_located(expr),
|
Range::from_located(expr),
|
||||||
value,
|
value,
|
||||||
self.in_annotation,
|
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) {
|
if self.settings.enabled.contains(&CheckCode::S104) {
|
||||||
|
@ -2136,7 +2142,7 @@ where
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExprKind::Lambda { .. } => {
|
ExprKind::Lambda { .. } => {
|
||||||
self.deferred_lambdas
|
self.deferred_lambdas
|
||||||
.push((expr, (self.scope_stack.clone(), self.parent_stack.clone())));
|
.push((expr, (self.scope_stack.clone(), self.parents.clone())));
|
||||||
}
|
}
|
||||||
ExprKind::Call {
|
ExprKind::Call {
|
||||||
func,
|
func,
|
||||||
|
@ -2387,7 +2393,6 @@ where
|
||||||
ctx: ExprContext::Store,
|
ctx: ExprContext::Store,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
self.current_parent(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2402,7 +2407,6 @@ where
|
||||||
ctx: ExprContext::Store,
|
ctx: ExprContext::Store,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
self.current_parent(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
walk_excepthandler(self, excepthandler);
|
walk_excepthandler(self, excepthandler);
|
||||||
|
@ -2484,6 +2488,8 @@ where
|
||||||
kind: BindingKind::Argument,
|
kind: BindingKind::Argument,
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(arg),
|
range: Range::from_located(arg),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2510,14 +2516,20 @@ where
|
||||||
|
|
||||||
impl<'a> Checker<'a> {
|
impl<'a> Checker<'a> {
|
||||||
fn push_parent(&mut self, parent: &'a Stmt) {
|
fn push_parent(&mut self, parent: &'a Stmt) {
|
||||||
self.parent_stack.push(self.parents.len());
|
let num_existing = self.parents.len();
|
||||||
self.parents.push(parent);
|
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) {
|
fn pop_parent(&mut self) {
|
||||||
self.parent_stack
|
self.parents.pop().expect("Attempted to pop without scope");
|
||||||
.pop()
|
|
||||||
.expect("Attempted to pop without scope");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_scope(&mut self, scope: Scope<'a>) {
|
fn push_scope(&mut self, scope: Scope<'a>) {
|
||||||
|
@ -2535,12 +2547,15 @@ impl<'a> Checker<'a> {
|
||||||
|
|
||||||
fn bind_builtins(&mut self) {
|
fn bind_builtins(&mut self) {
|
||||||
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
|
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
|
||||||
|
|
||||||
for builtin in BUILTINS.iter().chain(MAGIC_GLOBALS.iter()) {
|
for builtin in BUILTINS.iter().chain(MAGIC_GLOBALS.iter()) {
|
||||||
let index = self.bindings.len();
|
let index = self.bindings.len();
|
||||||
self.bindings.push(Binding {
|
self.bindings.push(Binding {
|
||||||
kind: BindingKind::Builtin,
|
kind: BindingKind::Builtin,
|
||||||
range: Range::default(),
|
range: Range::default(),
|
||||||
used: None,
|
used: None,
|
||||||
|
source: None,
|
||||||
|
redefined: vec![],
|
||||||
});
|
});
|
||||||
scope.values.insert(builtin, index);
|
scope.values.insert(builtin, index);
|
||||||
}
|
}
|
||||||
|
@ -2551,41 +2566,58 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> {
|
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 {
|
pub fn current_parent(&self) -> &RefEquality<'a, Stmt> {
|
||||||
self.parents[*(self.parent_stack.last().expect("No parent found"))]
|
self.parents.iter().rev().next().expect("No parent found")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn binding_context(&self) -> BindingContext {
|
pub fn current_grandparent(&self) -> Option<&RefEquality<'a, Stmt>> {
|
||||||
let mut rev = self.parent_stack.iter().rev().fuse();
|
self.parents.iter().rev().nth(1)
|
||||||
let defined_by = *rev.next().expect("Expected to bind within a statement");
|
|
||||||
let defined_in = rev.next().copied();
|
|
||||||
BindingContext {
|
|
||||||
defined_by,
|
|
||||||
defined_in,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_binding<'b>(&mut self, name: &'b str, binding: Binding)
|
fn add_binding<'b>(&mut self, name: &'b str, binding: Binding<'a>)
|
||||||
where
|
where
|
||||||
'b: 'a,
|
'b: 'a,
|
||||||
{
|
{
|
||||||
if self.settings.enabled.contains(&CheckCode::F402) {
|
let index = self.bindings.len();
|
||||||
let scope = self.current_scope();
|
|
||||||
if let Some(index) = scope.values.get(&name) {
|
if let Some((stack_index, scope_index)) = self
|
||||||
let existing = &self.bindings[*index];
|
.scope_stack
|
||||||
if matches!(binding.kind, BindingKind::LoopVar)
|
.iter()
|
||||||
&& matches!(
|
.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,
|
existing.kind,
|
||||||
BindingKind::Importation(..)
|
BindingKind::Importation(..)
|
||||||
| BindingKind::FromImportation(..)
|
| BindingKind::FromImportation(..)
|
||||||
| BindingKind::SubmoduleImportation(..)
|
| BindingKind::SubmoduleImportation(..)
|
||||||
| BindingKind::StarImportation(..)
|
| BindingKind::StarImportation(..)
|
||||||
| BindingKind::FutureImportation
|
| BindingKind::FutureImportation
|
||||||
)
|
);
|
||||||
{
|
if matches!(binding.kind, BindingKind::LoopVar) && existing_is_import {
|
||||||
|
if self.settings.enabled.contains(&CheckCode::F402) {
|
||||||
self.add_check(Check::new(
|
self.add_check(Check::new(
|
||||||
CheckKind::ImportShadowedByLoopVar(
|
CheckKind::ImportShadowedByLoopVar(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
|
@ -2594,6 +2626,29 @@ impl<'a> Checker<'a> {
|
||||||
binding.range,
|
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) {
|
let binding = match scope.values.get(&name) {
|
||||||
None => binding,
|
None => binding,
|
||||||
Some(index) => Binding {
|
Some(index) => Binding {
|
||||||
kind: binding.kind,
|
|
||||||
range: binding.range,
|
|
||||||
used: self.bindings[*index].used,
|
used: self.bindings[*index].used,
|
||||||
|
..binding
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let index = self.bindings.len();
|
|
||||||
self.bindings.push(binding);
|
self.bindings.push(binding);
|
||||||
|
|
||||||
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
|
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 as pa
|
||||||
// import pyarrow.csv
|
// import pyarrow.csv
|
||||||
// print(pa.csv.read_csv("test.csv"))
|
// print(pa.csv.read_csv("test.csv"))
|
||||||
if let BindingKind::Importation(name, full_name, _)
|
if let BindingKind::Importation(name, full_name)
|
||||||
| BindingKind::FromImportation(name, full_name, _)
|
| BindingKind::FromImportation(name, full_name)
|
||||||
| BindingKind::SubmoduleImportation(name, full_name, _) =
|
| BindingKind::SubmoduleImportation(name, full_name) =
|
||||||
&self.bindings[*index].kind
|
&self.bindings[*index].kind
|
||||||
{
|
{
|
||||||
let has_alias = full_name
|
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
|
where
|
||||||
'b: 'a,
|
'b: 'a,
|
||||||
{
|
{
|
||||||
|
let parent = self.current_parent().0;
|
||||||
|
|
||||||
if self.settings.enabled.contains(&CheckCode::F823) {
|
if self.settings.enabled.contains(&CheckCode::F823) {
|
||||||
let scopes: Vec<&Scope> = self
|
let scopes: Vec<&Scope> = self
|
||||||
.scope_stack
|
.scope_stack
|
||||||
|
@ -2775,6 +2830,8 @@ impl<'a> Checker<'a> {
|
||||||
kind: BindingKind::Annotation,
|
kind: BindingKind::Annotation,
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(expr),
|
range: Range::from_located(expr),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -2791,6 +2848,8 @@ impl<'a> Checker<'a> {
|
||||||
kind: BindingKind::LoopVar,
|
kind: BindingKind::LoopVar,
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(expr),
|
range: Range::from_located(expr),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -2803,6 +2862,8 @@ impl<'a> Checker<'a> {
|
||||||
kind: BindingKind::Binding,
|
kind: BindingKind::Binding,
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(expr),
|
range: Range::from_located(expr),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -2822,6 +2883,8 @@ impl<'a> Checker<'a> {
|
||||||
kind: BindingKind::Export(extract_all_names(parent, current, &self.bindings)),
|
kind: BindingKind::Export(extract_all_names(parent, current, &self.bindings)),
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(expr),
|
range: Range::from_located(expr),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -2833,6 +2896,8 @@ impl<'a> Checker<'a> {
|
||||||
kind: BindingKind::Assignment,
|
kind: BindingKind::Assignment,
|
||||||
used: None,
|
used: None,
|
||||||
range: Range::from_located(expr),
|
range: Range::from_located(expr),
|
||||||
|
source: Some(self.current_parent().clone()),
|
||||||
|
redefined: vec![],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2842,13 +2907,8 @@ impl<'a> Checker<'a> {
|
||||||
'b: 'a,
|
'b: 'a,
|
||||||
{
|
{
|
||||||
if let ExprKind::Name { id, .. } = &expr.node {
|
if let ExprKind::Name { id, .. } = &expr.node {
|
||||||
if operations::on_conditional_branch(
|
if operations::on_conditional_branch(&mut self.parents.iter().rev().map(|node| node.0))
|
||||||
&mut self
|
{
|
||||||
.parent_stack
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|index| self.parents[*index]),
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2892,7 +2952,7 @@ impl<'a> Checker<'a> {
|
||||||
self.deferred_type_definitions.pop()
|
self.deferred_type_definitions.pop()
|
||||||
{
|
{
|
||||||
self.scope_stack = scopes;
|
self.scope_stack = scopes;
|
||||||
self.parent_stack = parents;
|
self.parents = parents;
|
||||||
self.in_annotation = in_annotation;
|
self.in_annotation = in_annotation;
|
||||||
self.in_type_definition = true;
|
self.in_type_definition = true;
|
||||||
self.in_deferred_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) {
|
for (expr, (in_annotation, (scopes, parents))) in allocator.iter().zip(stacks) {
|
||||||
self.scope_stack = scopes;
|
self.scope_stack = scopes;
|
||||||
self.parent_stack = parents;
|
self.parents = parents;
|
||||||
self.in_annotation = in_annotation;
|
self.in_annotation = in_annotation;
|
||||||
self.in_type_definition = true;
|
self.in_type_definition = true;
|
||||||
self.in_deferred_string_type_definition = true;
|
self.in_deferred_string_type_definition = true;
|
||||||
|
@ -2938,7 +2998,7 @@ impl<'a> Checker<'a> {
|
||||||
fn check_deferred_functions(&mut self) {
|
fn check_deferred_functions(&mut self) {
|
||||||
while let Some((stmt, (scopes, parents), visibility)) = self.deferred_functions.pop() {
|
while let Some((stmt, (scopes, parents), visibility)) = self.deferred_functions.pop() {
|
||||||
self.scope_stack = scopes.clone();
|
self.scope_stack = scopes.clone();
|
||||||
self.parent_stack = parents.clone();
|
self.parents = parents.clone();
|
||||||
self.visible_scope = visibility;
|
self.visible_scope = visibility;
|
||||||
|
|
||||||
match &stmt.node {
|
match &stmt.node {
|
||||||
|
@ -2983,7 +3043,7 @@ impl<'a> Checker<'a> {
|
||||||
fn check_deferred_lambdas(&mut self) {
|
fn check_deferred_lambdas(&mut self) {
|
||||||
while let Some((expr, (scopes, parents))) = self.deferred_lambdas.pop() {
|
while let Some((expr, (scopes, parents))) = self.deferred_lambdas.pop() {
|
||||||
self.scope_stack = scopes.clone();
|
self.scope_stack = scopes.clone();
|
||||||
self.parent_stack = parents.clone();
|
self.parents = parents.clone();
|
||||||
|
|
||||||
if let ExprKind::Lambda { args, body } = &expr.node {
|
if let ExprKind::Lambda { args, body } = &expr.node {
|
||||||
self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body })));
|
self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body })));
|
||||||
|
@ -3038,6 +3098,7 @@ impl<'a> Checker<'a> {
|
||||||
fn check_dead_scopes(&mut self) {
|
fn check_dead_scopes(&mut self) {
|
||||||
if !self.settings.enabled.contains(&CheckCode::F401)
|
if !self.settings.enabled.contains(&CheckCode::F401)
|
||||||
&& !self.settings.enabled.contains(&CheckCode::F405)
|
&& !self.settings.enabled.contains(&CheckCode::F405)
|
||||||
|
&& !self.settings.enabled.contains(&CheckCode::F811)
|
||||||
&& !self.settings.enabled.contains(&CheckCode::F822)
|
&& !self.settings.enabled.contains(&CheckCode::F822)
|
||||||
{
|
{
|
||||||
return;
|
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 self.settings.enabled.contains(&CheckCode::F405) {
|
||||||
if scope.import_starred {
|
if scope.import_starred {
|
||||||
if let Some(all_binding) = all_binding {
|
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
|
// Collect all unused imports by location. (Multiple unused imports at the same
|
||||||
// location indicates an `import from`.)
|
// location indicates an `import from`.)
|
||||||
type UnusedImport<'a> = (&'a String, &'a Range);
|
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>> =
|
let mut unused: FxHashMap<BindingContext, Vec<UnusedImport>> = FxHashMap::default();
|
||||||
BTreeMap::new();
|
|
||||||
|
|
||||||
for (name, index) in &scope.values {
|
for (name, index) in &scope.values {
|
||||||
let binding = &self.bindings[*index];
|
let binding = &self.bindings[*index];
|
||||||
let (BindingKind::Importation(_, full_name, context)
|
|
||||||
| BindingKind::SubmoduleImportation(_, full_name, context)
|
let (BindingKind::Importation(_, full_name)
|
||||||
| BindingKind::FromImportation(_, full_name, context)) = &binding.kind else { continue; };
|
| BindingKind::SubmoduleImportation(_, full_name)
|
||||||
|
| BindingKind::FromImportation(_, full_name)) = &binding.kind else { continue; };
|
||||||
|
|
||||||
// Skip used exports from `__all__`
|
// Skip used exports from `__all__`
|
||||||
if binding.used.is_some()
|
if binding.used.is_some()
|
||||||
|
@ -3128,24 +3226,23 @@ impl<'a> Checker<'a> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let defined_by = binding.source.as_ref().unwrap();
|
||||||
|
let defined_in = self.child_to_parent.get(defined_by);
|
||||||
unused
|
unused
|
||||||
.entry((context.defined_by, context.defined_in))
|
.entry((defined_by, defined_in))
|
||||||
.or_default()
|
.or_default()
|
||||||
.push((full_name, &binding.range));
|
.push((full_name, &binding.range));
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((defined_by, defined_in), unused_imports) in unused {
|
for ((defined_by, defined_in), unused_imports) in unused {
|
||||||
let child = self.parents[defined_by];
|
let child = defined_by.0;
|
||||||
let parent = defined_in.map(|defined_in| self.parents[defined_in]);
|
let parent = defined_in.map(|defined_in| defined_in.0);
|
||||||
|
|
||||||
let ignore_init = self.settings.ignore_init_module_imports
|
let ignore_init = self.settings.ignore_init_module_imports
|
||||||
&& self.path.ends_with("__init__.py");
|
&& self.path.ends_with("__init__.py");
|
||||||
let fix = if !ignore_init && self.patch(&CheckCode::F401) {
|
let fix = if !ignore_init && self.patch(&CheckCode::F401) {
|
||||||
let deleted: Vec<&Stmt> = self
|
let deleted: Vec<&Stmt> =
|
||||||
.deletions
|
self.deletions.iter().map(|node| node.0).collect();
|
||||||
.iter()
|
|
||||||
.map(|index| self.parents[*index])
|
|
||||||
.collect();
|
|
||||||
match pyflakes::fixes::remove_unused_imports(
|
match pyflakes::fixes::remove_unused_imports(
|
||||||
self.locator,
|
self.locator,
|
||||||
&unused_imports,
|
&unused_imports,
|
||||||
|
@ -3155,7 +3252,7 @@ impl<'a> Checker<'a> {
|
||||||
) {
|
) {
|
||||||
Ok(fix) => {
|
Ok(fix) => {
|
||||||
if fix.content.is_empty() || fix.content == "pass" {
|
if fix.content.is_empty() || fix.content == "pass" {
|
||||||
self.deletions.insert(defined_by);
|
self.deletions.insert(defined_by.clone());
|
||||||
}
|
}
|
||||||
Some(fix)
|
Some(fix)
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ pub enum CheckCode {
|
||||||
F706,
|
F706,
|
||||||
F707,
|
F707,
|
||||||
F722,
|
F722,
|
||||||
|
F811,
|
||||||
F821,
|
F821,
|
||||||
F822,
|
F822,
|
||||||
F823,
|
F823,
|
||||||
|
@ -616,6 +617,7 @@ pub enum CheckKind {
|
||||||
PercentFormatStarRequiresSequence,
|
PercentFormatStarRequiresSequence,
|
||||||
PercentFormatUnsupportedFormatCharacter(char),
|
PercentFormatUnsupportedFormatCharacter(char),
|
||||||
RaiseNotImplemented,
|
RaiseNotImplemented,
|
||||||
|
RedefinedWhileUnused(String, usize),
|
||||||
ReturnOutsideFunction,
|
ReturnOutsideFunction,
|
||||||
StringDotFormatExtraNamedArguments(Vec<String>),
|
StringDotFormatExtraNamedArguments(Vec<String>),
|
||||||
StringDotFormatExtraPositionalArguments(Vec<String>),
|
StringDotFormatExtraPositionalArguments(Vec<String>),
|
||||||
|
@ -932,6 +934,7 @@ impl CheckCode {
|
||||||
CheckCode::F706 => CheckKind::ReturnOutsideFunction,
|
CheckCode::F706 => CheckKind::ReturnOutsideFunction,
|
||||||
CheckCode::F707 => CheckKind::DefaultExceptNotLast,
|
CheckCode::F707 => CheckKind::DefaultExceptNotLast,
|
||||||
CheckCode::F722 => CheckKind::ForwardAnnotationSyntaxError("...".to_string()),
|
CheckCode::F722 => CheckKind::ForwardAnnotationSyntaxError("...".to_string()),
|
||||||
|
CheckCode::F811 => CheckKind::RedefinedWhileUnused("...".to_string(), 1),
|
||||||
CheckCode::F821 => CheckKind::UndefinedName("...".to_string()),
|
CheckCode::F821 => CheckKind::UndefinedName("...".to_string()),
|
||||||
CheckCode::F822 => CheckKind::UndefinedExport("...".to_string()),
|
CheckCode::F822 => CheckKind::UndefinedExport("...".to_string()),
|
||||||
CheckCode::F823 => CheckKind::UndefinedLocal("...".to_string()),
|
CheckCode::F823 => CheckKind::UndefinedLocal("...".to_string()),
|
||||||
|
@ -1359,6 +1362,7 @@ impl CheckCode {
|
||||||
CheckCode::F706 => CheckCategory::Pyflakes,
|
CheckCode::F706 => CheckCategory::Pyflakes,
|
||||||
CheckCode::F707 => CheckCategory::Pyflakes,
|
CheckCode::F707 => CheckCategory::Pyflakes,
|
||||||
CheckCode::F722 => CheckCategory::Pyflakes,
|
CheckCode::F722 => CheckCategory::Pyflakes,
|
||||||
|
CheckCode::F811 => CheckCategory::Pyflakes,
|
||||||
CheckCode::F821 => CheckCategory::Pyflakes,
|
CheckCode::F821 => CheckCategory::Pyflakes,
|
||||||
CheckCode::F822 => CheckCategory::Pyflakes,
|
CheckCode::F822 => CheckCategory::Pyflakes,
|
||||||
CheckCode::F823 => CheckCategory::Pyflakes,
|
CheckCode::F823 => CheckCategory::Pyflakes,
|
||||||
|
@ -1508,6 +1512,7 @@ impl CheckKind {
|
||||||
CheckKind::TypeComparison => &CheckCode::E721,
|
CheckKind::TypeComparison => &CheckCode::E721,
|
||||||
CheckKind::UndefinedExport(_) => &CheckCode::F822,
|
CheckKind::UndefinedExport(_) => &CheckCode::F822,
|
||||||
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
|
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
|
||||||
|
CheckKind::RedefinedWhileUnused(..) => &CheckCode::F811,
|
||||||
CheckKind::UndefinedName(_) => &CheckCode::F821,
|
CheckKind::UndefinedName(_) => &CheckCode::F821,
|
||||||
CheckKind::UnusedImport(..) => &CheckCode::F401,
|
CheckKind::UnusedImport(..) => &CheckCode::F401,
|
||||||
CheckKind::UnusedVariable(_) => &CheckCode::F841,
|
CheckKind::UnusedVariable(_) => &CheckCode::F841,
|
||||||
|
@ -1846,6 +1851,9 @@ impl CheckKind {
|
||||||
CheckKind::RaiseNotImplemented => {
|
CheckKind::RaiseNotImplemented => {
|
||||||
"`raise NotImplemented` should be `raise NotImplementedError`".to_string()
|
"`raise NotImplemented` should be `raise NotImplementedError`".to_string()
|
||||||
}
|
}
|
||||||
|
CheckKind::RedefinedWhileUnused(name, line) => {
|
||||||
|
format!("Redefinition of unused `{name}` from line {line}")
|
||||||
|
}
|
||||||
CheckKind::ReturnOutsideFunction => {
|
CheckKind::ReturnOutsideFunction => {
|
||||||
"`return` statement outside of a function/method".to_string()
|
"`return` statement outside of a function/method".to_string()
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,6 +243,8 @@ pub enum CheckCodePrefix {
|
||||||
F72,
|
F72,
|
||||||
F722,
|
F722,
|
||||||
F8,
|
F8,
|
||||||
|
F81,
|
||||||
|
F811,
|
||||||
F82,
|
F82,
|
||||||
F821,
|
F821,
|
||||||
F822,
|
F822,
|
||||||
|
@ -1026,6 +1028,7 @@ impl CheckCodePrefix {
|
||||||
CheckCode::F706,
|
CheckCode::F706,
|
||||||
CheckCode::F707,
|
CheckCode::F707,
|
||||||
CheckCode::F722,
|
CheckCode::F722,
|
||||||
|
CheckCode::F811,
|
||||||
CheckCode::F821,
|
CheckCode::F821,
|
||||||
CheckCode::F822,
|
CheckCode::F822,
|
||||||
CheckCode::F823,
|
CheckCode::F823,
|
||||||
|
@ -1158,12 +1161,15 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::F72 => vec![CheckCode::F722],
|
CheckCodePrefix::F72 => vec![CheckCode::F722],
|
||||||
CheckCodePrefix::F722 => vec![CheckCode::F722],
|
CheckCodePrefix::F722 => vec![CheckCode::F722],
|
||||||
CheckCodePrefix::F8 => vec![
|
CheckCodePrefix::F8 => vec![
|
||||||
|
CheckCode::F811,
|
||||||
CheckCode::F821,
|
CheckCode::F821,
|
||||||
CheckCode::F822,
|
CheckCode::F822,
|
||||||
CheckCode::F823,
|
CheckCode::F823,
|
||||||
CheckCode::F831,
|
CheckCode::F831,
|
||||||
CheckCode::F841,
|
CheckCode::F841,
|
||||||
],
|
],
|
||||||
|
CheckCodePrefix::F81 => vec![CheckCode::F811],
|
||||||
|
CheckCodePrefix::F811 => vec![CheckCode::F811],
|
||||||
CheckCodePrefix::F82 => vec![CheckCode::F821, CheckCode::F822, CheckCode::F823],
|
CheckCodePrefix::F82 => vec![CheckCode::F821, CheckCode::F822, CheckCode::F823],
|
||||||
CheckCodePrefix::F821 => vec![CheckCode::F821],
|
CheckCodePrefix::F821 => vec![CheckCode::F821],
|
||||||
CheckCodePrefix::F822 => vec![CheckCode::F822],
|
CheckCodePrefix::F822 => vec![CheckCode::F822],
|
||||||
|
@ -2033,6 +2039,8 @@ impl CheckCodePrefix {
|
||||||
CheckCodePrefix::F72 => SuffixLength::Two,
|
CheckCodePrefix::F72 => SuffixLength::Two,
|
||||||
CheckCodePrefix::F722 => SuffixLength::Three,
|
CheckCodePrefix::F722 => SuffixLength::Three,
|
||||||
CheckCodePrefix::F8 => SuffixLength::One,
|
CheckCodePrefix::F8 => SuffixLength::One,
|
||||||
|
CheckCodePrefix::F81 => SuffixLength::Two,
|
||||||
|
CheckCodePrefix::F811 => SuffixLength::Three,
|
||||||
CheckCodePrefix::F82 => SuffixLength::Two,
|
CheckCodePrefix::F82 => SuffixLength::Two,
|
||||||
CheckCodePrefix::F821 => SuffixLength::Three,
|
CheckCodePrefix::F821 => SuffixLength::Three,
|
||||||
CheckCodePrefix::F822 => 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()) {
|
if checker.patch(check.kind.code()) {
|
||||||
let context = checker.binding_context();
|
let defined_by = checker.current_parent();
|
||||||
if matches!(
|
let defined_in = checker.current_grandparent();
|
||||||
checker.parents[context.defined_by].node,
|
if matches!(defined_by.0.node, StmtKind::Expr { .. }) {
|
||||||
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) {
|
||||||
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,
|
|
||||||
) {
|
|
||||||
Ok(fix) => {
|
Ok(fix) => {
|
||||||
if fix.content.is_empty() || fix.content == "pass" {
|
if fix.content.is_empty() || fix.content == "pass" {
|
||||||
checker.deletions.insert(context.defined_by);
|
checker.deletions.insert(defined_by.clone());
|
||||||
}
|
}
|
||||||
check.amend(fix);
|
check.amend(fix);
|
||||||
}
|
}
|
||||||
|
|
|
@ -554,12 +554,13 @@ pub fn starred_expressions(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// F701
|
/// 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 allowed: bool = false;
|
||||||
let mut parent = stmt;
|
let mut child = stmt;
|
||||||
for index in parent_stack.iter().rev() {
|
for parent in parents {
|
||||||
let child = parent;
|
|
||||||
parent = parents[*index];
|
|
||||||
match &parent.node {
|
match &parent.node {
|
||||||
StmtKind::For { orelse, .. }
|
StmtKind::For { orelse, .. }
|
||||||
| StmtKind::AsyncFor { orelse, .. }
|
| StmtKind::AsyncFor { orelse, .. }
|
||||||
|
@ -569,7 +570,6 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StmtKind::FunctionDef { .. }
|
StmtKind::FunctionDef { .. }
|
||||||
| StmtKind::AsyncFunctionDef { .. }
|
| StmtKind::AsyncFunctionDef { .. }
|
||||||
| StmtKind::ClassDef { .. } => {
|
| StmtKind::ClassDef { .. } => {
|
||||||
|
@ -577,6 +577,7 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
child = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowed {
|
if allowed {
|
||||||
|
@ -590,16 +591,13 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// F702
|
/// F702
|
||||||
pub fn continue_outside_loop(
|
pub fn continue_outside_loop<'a>(
|
||||||
stmt: &Stmt,
|
stmt: &'a Stmt,
|
||||||
parents: &[&Stmt],
|
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||||
parent_stack: &[usize],
|
|
||||||
) -> Option<Check> {
|
) -> Option<Check> {
|
||||||
let mut allowed: bool = false;
|
let mut allowed: bool = false;
|
||||||
let mut parent = stmt;
|
let mut child = stmt;
|
||||||
for index in parent_stack.iter().rev() {
|
for parent in parents {
|
||||||
let child = parent;
|
|
||||||
parent = parents[*index];
|
|
||||||
match &parent.node {
|
match &parent.node {
|
||||||
StmtKind::For { orelse, .. }
|
StmtKind::For { orelse, .. }
|
||||||
| StmtKind::AsyncFor { orelse, .. }
|
| StmtKind::AsyncFor { orelse, .. }
|
||||||
|
@ -609,7 +607,6 @@ pub fn continue_outside_loop(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StmtKind::FunctionDef { .. }
|
StmtKind::FunctionDef { .. }
|
||||||
| StmtKind::AsyncFunctionDef { .. }
|
| StmtKind::AsyncFunctionDef { .. }
|
||||||
| StmtKind::ClassDef { .. } => {
|
| StmtKind::ClassDef { .. } => {
|
||||||
|
@ -617,6 +614,7 @@ pub fn continue_outside_loop(
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
child = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowed {
|
if allowed {
|
||||||
|
|
|
@ -66,6 +66,27 @@ mod tests {
|
||||||
#[test_case(CheckCode::F706, Path::new("F706.py"); "F706")]
|
#[test_case(CheckCode::F706, Path::new("F706.py"); "F706")]
|
||||||
#[test_case(CheckCode::F707, Path::new("F707.py"); "F707")]
|
#[test_case(CheckCode::F707, Path::new("F707.py"); "F707")]
|
||||||
#[test_case(CheckCode::F722, Path::new("F722.py"); "F722")]
|
#[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_0.py"); "F821_0")]
|
||||||
#[test_case(CheckCode::F821, Path::new("F821_1.py"); "F821_1")]
|
#[test_case(CheckCode::F821, Path::new("F821_1.py"); "F821_1")]
|
||||||
#[test_case(CheckCode::F821, Path::new("F821_2.py"); "F821_2")]
|
#[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
|
// e.g. module=sys object=exit
|
||||||
// `import sys` -> `sys.exit`
|
// `import sys` -> `sys.exit`
|
||||||
// `import sys as sys2` -> `sys2.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}"))
|
Some(format!("{name}.{member}"))
|
||||||
}
|
}
|
||||||
// e.g. module=os.path object=join
|
// e.g. module=os.path object=join
|
||||||
// `from os.path import join` -> `join`
|
// `from os.path import join` -> `join`
|
||||||
// `from os.path import join as join2` -> `join2`
|
// `from os.path import join as join2` -> `join2`
|
||||||
BindingKind::FromImportation(name, full_name, _)
|
BindingKind::FromImportation(name, full_name)
|
||||||
if full_name == &format!("{module}.{member}") =>
|
if full_name == &format!("{module}.{member}") =>
|
||||||
{
|
{
|
||||||
Some(name.to_string())
|
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
|
// e.g. module=os.path object=join
|
||||||
// `import os.path ` -> `os.path.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}"))
|
Some(format!("{full_name}.{member}"))
|
||||||
}
|
}
|
||||||
// Non-imports.
|
// Non-imports.
|
||||||
|
|
|
@ -13,11 +13,7 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let scope = checker.current_scope();
|
let scope = checker.current_scope();
|
||||||
let parents: Vec<&Stmt> = checker
|
let parents: Vec<&Stmt> = checker.parents.iter().map(|node| node.0).collect();
|
||||||
.parent_stack
|
|
||||||
.iter()
|
|
||||||
.map(|index| checker.parents[*index])
|
|
||||||
.collect();
|
|
||||||
let Some(mut check) = checks::super_args(scope, &parents, expr, func, args) else {
|
let Some(mut check) = checks::super_args(scope, &parents, expr, func, args) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,23 +62,21 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
|
||||||
),
|
),
|
||||||
Range::from_located(stmt),
|
Range::from_located(stmt),
|
||||||
);
|
);
|
||||||
|
|
||||||
if checker.patch(check.kind.code()) {
|
if checker.patch(check.kind.code()) {
|
||||||
let context = checker.binding_context();
|
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
|
||||||
let deleted: Vec<&Stmt> = checker
|
let defined_by = checker.current_parent();
|
||||||
.deletions
|
let defined_in = checker.current_grandparent();
|
||||||
.iter()
|
|
||||||
.map(|index| checker.parents[*index])
|
|
||||||
.collect();
|
|
||||||
match fixes::remove_unnecessary_future_import(
|
match fixes::remove_unnecessary_future_import(
|
||||||
checker.locator,
|
checker.locator,
|
||||||
&removable_index,
|
&removable_index,
|
||||||
checker.parents[context.defined_by],
|
defined_by.0,
|
||||||
context.defined_in.map(|index| checker.parents[index]),
|
defined_in.map(|node| node.0),
|
||||||
&deleted,
|
&deleted,
|
||||||
) {
|
) {
|
||||||
Ok(fix) => {
|
Ok(fix) => {
|
||||||
if fix.content.is_empty() || fix.content == "pass" {
|
if fix.content.is_empty() || fix.content == "pass" {
|
||||||
checker.deletions.insert(context.defined_by);
|
checker.deletions.insert(defined_by.clone());
|
||||||
}
|
}
|
||||||
check.amend(fix);
|
check.amend(fix);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,21 +13,13 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr,
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if checker.patch(check.kind.code()) {
|
if checker.patch(check.kind.code()) {
|
||||||
let context = checker.binding_context();
|
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
|
||||||
let deleted: Vec<&Stmt> = checker
|
let defined_by = checker.current_parent();
|
||||||
.deletions
|
let defined_in = checker.current_grandparent();
|
||||||
.iter()
|
match helpers::remove_stmt(defined_by.0, defined_in.map(|node| node.0), &deleted) {
|
||||||
.map(|index| checker.parents[*index])
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
match helpers::remove_stmt(
|
|
||||||
checker.parents[context.defined_by],
|
|
||||||
context.defined_in.map(|index| checker.parents[index]),
|
|
||||||
&deleted,
|
|
||||||
) {
|
|
||||||
Ok(fix) => {
|
Ok(fix) => {
|
||||||
if fix.content.is_empty() || fix.content == "pass" {
|
if fix.content.is_empty() || fix.content == "pass" {
|
||||||
checker.deletions.insert(context.defined_by);
|
checker.deletions.insert(defined_by.clone());
|
||||||
}
|
}
|
||||||
check.amend(fix);
|
check.amend(fix);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue