Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1b711d9c6 | ||
|
|
35f593846e | ||
|
|
c384fa513b | ||
|
|
022ff64d29 | ||
|
|
5a06fb28fd | ||
|
|
46750a3e17 | ||
|
|
9cc902b802 | ||
|
|
c2a36ebd1e | ||
|
|
34ca225393 | ||
|
|
38c30905e6 | ||
|
|
2774194b03 | ||
|
|
71ebd39f35 | ||
|
|
a2f78ba2c7 | ||
|
|
b51a080a44 | ||
|
|
6a1d7d8a1c |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1907,7 +1907,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.64"
|
||||
version = "0.0.67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.64"
|
||||
version = "0.0.67"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
12
README.md
12
README.md
@@ -215,7 +215,8 @@ ruff also implements some of the most popular Flake8 plugins natively, including
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/) (partial)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/) (11/16)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (3/32)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (partial)
|
||||
|
||||
Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
@@ -278,6 +279,9 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||
| A001 | BuiltinVariableShadowing | Variable `...` is shadowing a python builtin | | |
|
||||
| A002 | BuiltinArgumentShadowing | Argument `...` is shadowing a python builtin | | |
|
||||
| A003 | BuiltinAttributeShadowing | Class attribute `...` is shadowing a python builtin | | |
|
||||
| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | | 🛠 |
|
||||
| B014 | DuplicateHandlerException | Exception handler with duplicate exception `Exception` | | |
|
||||
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | | |
|
||||
| C400 | UnnecessaryGeneratorList | Unnecessary generator - rewrite as a list comprehension | | |
|
||||
| C401 | UnnecessaryGeneratorSet | Unnecessary generator - rewrite as a set comprehension | | |
|
||||
| C402 | UnnecessaryGeneratorDict | Unnecessary generator - rewrite as a dict comprehension | | |
|
||||
@@ -286,17 +290,19 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||
| C405 | UnnecessaryLiteralSet | Unnecessary <list/tuple> literal - rewrite as a set literal | | |
|
||||
| C406 | UnnecessaryLiteralDict | Unnecessary <list/tuple> literal - rewrite as a dict literal | | |
|
||||
| C408 | UnnecessaryCollectionCall | Unnecessary <dict/list/tuple> call - rewrite as a literal | | |
|
||||
| C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary <list/tuple> literal passed to tuple() - remove the outer call to tuple() | | |
|
||||
| C410 | UnnecessaryLiteralWithinListCall | Unnecessary <list/tuple> literal passed to list() - rewrite as a list literal | | |
|
||||
| C415 | UnnecessarySubscriptReversal | Unnecessary subscript reversal of iterable within <reversed/set/sorted>() | | |
|
||||
| SPR001 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | | 🛠 |
|
||||
| T201 | PrintFound | `print` found | | 🛠 |
|
||||
| T203 | PPrintFound | `pprint` found | | 🛠 |
|
||||
| U001 | UselessMetaclassType | `__metaclass__ = type` is implied | | 🛠 |
|
||||
| U002 | UnnecessaryAbspath | `abspath(__file__)` is unnecessary in Python 3.9 and later | | 🛠 |
|
||||
| U003 | TypeOfPrimitive | Use `str` instead of `type(...)` | | 🛠 |
|
||||
| U004 | UselessObjectInheritance | Class `...` inherits from object | | 🛠 |
|
||||
| U005 | NoAssertEquals | `assertEquals` is deprecated, use `assertEqual` instead | | 🛠 |
|
||||
| U005 | DeprecatedUnittestAlias | `assertEquals` is deprecated, use `assertEqual` instead | | 🛠 |
|
||||
| U006 | UsePEP585Annotation | Use `list` instead of `List` for type annotations | | 🛠 |
|
||||
| U007 | UsePEP604Annotation | Use `X \| Y` for type annotations | | 🛠 |
|
||||
| U008 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | | 🛠 |
|
||||
| M001 | UnusedNOQA | Unused `noqa` directive | | 🛠 |
|
||||
|
||||
## Integrations
|
||||
|
||||
10
resources/test/fixtures/B011.py
vendored
Normal file
10
resources/test/fixtures/B011.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
"""
|
||||
Should emit:
|
||||
B011 - on line 8
|
||||
B011 - on line 10
|
||||
"""
|
||||
|
||||
assert 1 != 2
|
||||
assert False
|
||||
assert 1 != 2, "message"
|
||||
assert False, "message"
|
||||
76
resources/test/fixtures/B014.py
vendored
Normal file
76
resources/test/fixtures/B014.py
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Should emit:
|
||||
B014 - on lines 11, 17, 28, 42, 49, 56, and 74.
|
||||
"""
|
||||
|
||||
import binascii
|
||||
import re
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception, TypeError):
|
||||
# TypeError is a subclass of Exception, so it doesn't add anything
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (OSError, OSError) as err:
|
||||
# Duplicate exception types are useless
|
||||
pass
|
||||
|
||||
|
||||
class MyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (MyError, MyError):
|
||||
# Detect duplicate non-builtin errors
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (MyError, Exception) as e:
|
||||
# Don't assume that we're all subclasses of Exception
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (MyError, BaseException) as e:
|
||||
# But we *can* assume that everything is a subclass of BaseException
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (re.error, re.error):
|
||||
# Duplicate exception types as attributes
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (IOError, EnvironmentError, OSError):
|
||||
# Detect if a primary exception and any its aliases are present.
|
||||
#
|
||||
# Since Python 3.3, IOError, EnvironmentError, WindowsError, mmap.error,
|
||||
# socket.error and select.error are aliases of OSError. See PEP 3151 for
|
||||
# more info.
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (MyException, NotImplemented):
|
||||
# NotImplemented is not an exception, let's not crash on it.
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, binascii.Error):
|
||||
# binascii.Error is a subclass of ValueError.
|
||||
pass
|
||||
38
resources/test/fixtures/B025.py
vendored
Normal file
38
resources/test/fixtures/B025.py
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
Should emit:
|
||||
B025 - on lines 15, 22, 31
|
||||
"""
|
||||
|
||||
import pickle
|
||||
|
||||
try:
|
||||
a = 1
|
||||
except ValueError:
|
||||
a = 2
|
||||
finally:
|
||||
a = 3
|
||||
|
||||
try:
|
||||
a = 1
|
||||
except ValueError:
|
||||
a = 2
|
||||
except ValueError:
|
||||
a = 2
|
||||
|
||||
try:
|
||||
a = 1
|
||||
except pickle.PickleError:
|
||||
a = 2
|
||||
except ValueError:
|
||||
a = 2
|
||||
except pickle.PickleError:
|
||||
a = 2
|
||||
|
||||
try:
|
||||
a = 1
|
||||
except (ValueError, TypeError):
|
||||
a = 2
|
||||
except ValueError:
|
||||
a = 2
|
||||
except (OSError, TypeError):
|
||||
a = 2
|
||||
3
resources/test/fixtures/C409.py
vendored
Normal file
3
resources/test/fixtures/C409.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
t1 = tuple([1, 2])
|
||||
t2 = tuple((1, 2))
|
||||
t3 = tuple([])
|
||||
4
resources/test/fixtures/C410.py
vendored
Normal file
4
resources/test/fixtures/C410.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
l1 = list([1, 2])
|
||||
l2 = list((1, 2))
|
||||
l3 = list([])
|
||||
l4 = list(())
|
||||
5
resources/test/fixtures/F401_1.py
vendored
Normal file
5
resources/test/fixtures/F401_1.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Access a sub-importation via an alias."""
|
||||
import pyarrow as pa
|
||||
import pyarrow.csv
|
||||
|
||||
print(pa.csv.read_csv("test.csv"))
|
||||
12
resources/test/fixtures/F401_2.py
vendored
Normal file
12
resources/test/fixtures/F401_2.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
"""Test: referencing an import via TypeAlias."""
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
|
||||
CustomInt: TypeAlias = "np.int8 | np.int16"
|
||||
14
resources/test/fixtures/F401_3.py
vendored
Normal file
14
resources/test/fixtures/F401_3.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Test: referencing an import via TypeAlias (with future annotations)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
|
||||
CustomInt: TypeAlias = np.int8 | np.int16
|
||||
14
resources/test/fixtures/F401_4.py
vendored
Normal file
14
resources/test/fixtures/F401_4.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Test: referencing an import via TypeAlias (with future annotations and quotes)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
|
||||
CustomInt: TypeAlias = "np.int8 | np.int16"
|
||||
9
resources/test/fixtures/F821.py
vendored
9
resources/test/fixtures/F821.py
vendored
@@ -122,3 +122,12 @@ class PEP593Test:
|
||||
dict["foo", "bar"], # Expected to fail as undefined.
|
||||
123,
|
||||
]
|
||||
|
||||
|
||||
def in_ipython_notebook() -> bool:
|
||||
try:
|
||||
# autoimported by notebooks
|
||||
get_ipython() # type: ignore[name-defined]
|
||||
except NameError:
|
||||
return False # not in notebook
|
||||
return True
|
||||
|
||||
3
resources/test/fixtures/M001.py
vendored
3
resources/test/fixtures/M001.py
vendored
@@ -15,6 +15,9 @@ def f() -> None:
|
||||
# Invalid
|
||||
d = 1 # noqa: F841, E501
|
||||
|
||||
# Invalid (and unimplemented)
|
||||
d = 1 # noqa: F841, W191
|
||||
|
||||
|
||||
# Valid
|
||||
_ = """Lorem ipsum dolor sit amet.
|
||||
|
||||
13
resources/test/fixtures/U005.py
vendored
13
resources/test/fixtures/U005.py
vendored
@@ -1,3 +1,10 @@
|
||||
self.assertEquals (1, 2)
|
||||
self.assertEquals(1, 2)
|
||||
self.assertEqual(3, 4)
|
||||
import unittest
|
||||
|
||||
|
||||
class Suite(unittest.TestCase):
|
||||
def test(self) -> None:
|
||||
self.assertEquals (1, 2)
|
||||
self.assertEquals(1, 2)
|
||||
self.assertEqual(3, 4)
|
||||
self.failUnlessAlmostEqual(1, 1.1)
|
||||
self.assertNotRegexpMatches("a", "b")
|
||||
|
||||
@@ -9,6 +9,7 @@ use rustpython_parser::ast::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::types::{
|
||||
Binding, BindingKind, CheckLocator, FunctionScope, Range, Scope, ScopeKind,
|
||||
};
|
||||
@@ -361,23 +362,6 @@ pub fn check_duplicate_arguments(arguments: &Arguments) -> Vec<Check> {
|
||||
checks
|
||||
}
|
||||
|
||||
/// Check AssertEquals compliance.
|
||||
pub fn check_assert_equals(expr: &Expr) -> Option<Check> {
|
||||
if let ExprKind::Attribute { value, attr, .. } = &expr.node {
|
||||
if attr == "assertEquals" {
|
||||
if let ExprKind::Name { id, .. } = &value.node {
|
||||
if id == "self" {
|
||||
return Some(Check::new(
|
||||
CheckKind::NoAssertEquals,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum DictionaryKey<'a> {
|
||||
Constant(&'a Constant),
|
||||
@@ -764,17 +748,7 @@ pub fn check_builtin_shadowing(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if a call is an argumented `super` invocation.
|
||||
pub fn is_super_call_with_arguments(func: &Expr, args: &Vec<Expr>) -> bool {
|
||||
// Check: is this a `super` call?
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
id == "super" && !args.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// flakes8-comprehensions
|
||||
// flake8-comprehensions
|
||||
/// Check `list(generator)` compliance.
|
||||
pub fn unnecessary_generator_list(expr: &Expr, func: &Expr, args: &Vec<Expr>) -> Option<Check> {
|
||||
if args.len() == 1 {
|
||||
@@ -986,6 +960,66 @@ pub fn unnecessary_collection_call(
|
||||
None
|
||||
}
|
||||
|
||||
pub fn unnecessary_literal_within_tuple_call(
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
) -> Option<Check> {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "tuple" {
|
||||
if let Some(arg) = args.first() {
|
||||
match &arg.node {
|
||||
ExprKind::Tuple { .. } => {
|
||||
return Some(Check::new(
|
||||
CheckKind::UnnecessaryLiteralWithinTupleCall("tuple".to_string()),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
ExprKind::List { .. } => {
|
||||
return Some(Check::new(
|
||||
CheckKind::UnnecessaryLiteralWithinTupleCall("list".to_string()),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn unnecessary_literal_within_list_call(
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
) -> Option<Check> {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "list" {
|
||||
if let Some(arg) = args.first() {
|
||||
match &arg.node {
|
||||
ExprKind::Tuple { .. } => {
|
||||
return Some(Check::new(
|
||||
CheckKind::UnnecessaryLiteralWithinListCall("tuple".to_string()),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
ExprKind::List { .. } => {
|
||||
return Some(Check::new(
|
||||
CheckKind::UnnecessaryLiteralWithinListCall("list".to_string()),
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn unnecessary_subscript_reversal(expr: &Expr, func: &Expr, args: &[Expr]) -> Option<Check> {
|
||||
if let Some(first_arg) = args.first() {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
@@ -1033,7 +1067,7 @@ pub fn check_super_args(
|
||||
func: &Expr,
|
||||
args: &Vec<Expr>,
|
||||
) -> Option<Check> {
|
||||
if !is_super_call_with_arguments(func, args) {
|
||||
if !helpers::is_super_call_with_arguments(func, args) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,42 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustpython_ast::{Expr, ExprKind, StmtKind};
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, StmtKind};
|
||||
|
||||
use crate::python::typing;
|
||||
|
||||
static DUNDER_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"__[^\s]+__").unwrap());
|
||||
fn compose_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
|
||||
match &expr.node {
|
||||
ExprKind::Call { func, .. } => {
|
||||
compose_call_path_inner(func, parts);
|
||||
}
|
||||
ExprKind::Attribute { value, attr, .. } => {
|
||||
compose_call_path_inner(value, parts);
|
||||
parts.push(attr);
|
||||
}
|
||||
ExprKind::Name { id, .. } => {
|
||||
parts.push(id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compose_call_path(expr: &Expr) -> Option<String> {
|
||||
let mut segments = vec![];
|
||||
compose_call_path_inner(expr, &mut segments);
|
||||
if segments.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(segments.join("."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_name_or_attr(expr: &Expr, target: &str) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Attribute { attr, .. } => target == attr,
|
||||
ExprKind::Name { id, .. } => target == id,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SubscriptKind {
|
||||
AnnotatedSubscript,
|
||||
@@ -35,6 +67,8 @@ pub fn match_annotated_subscript(expr: &Expr) -> Option<SubscriptKind> {
|
||||
}
|
||||
}
|
||||
|
||||
static DUNDER_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"__[^\s]+__").unwrap());
|
||||
|
||||
pub fn is_assignment_to_a_dunder(node: &StmtKind) -> bool {
|
||||
// Check whether it's an assignment to a dunder, with or without a type annotation.
|
||||
// This is what pycodestyle (as of 2.9.1) does.
|
||||
@@ -65,10 +99,35 @@ pub fn is_assignment_to_a_dunder(node: &StmtKind) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_name_or_attr(expr: &Expr, target: &str) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Attribute { attr, .. } => target == attr,
|
||||
ExprKind::Name { id, .. } => target == id,
|
||||
_ => false,
|
||||
/// Extract the names of all handled exceptions.
|
||||
pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<String> {
|
||||
let mut handler_names = vec![];
|
||||
for handler in handlers {
|
||||
match &handler.node {
|
||||
ExcepthandlerKind::ExceptHandler { type_, .. } => {
|
||||
if let Some(type_) = type_ {
|
||||
if let ExprKind::Tuple { elts, .. } = &type_.node {
|
||||
for type_ in elts {
|
||||
if let Some(name) = compose_call_path(type_) {
|
||||
handler_names.push(name);
|
||||
}
|
||||
}
|
||||
} else if let Some(name) = compose_call_path(type_) {
|
||||
handler_names.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handler_names
|
||||
}
|
||||
|
||||
/// Returns `true` if a call is an argumented `super` invocation.
|
||||
pub fn is_super_call_with_arguments(func: &Expr, args: &Vec<Expr>) -> bool {
|
||||
// Check: is this a `super` call?
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
id == "super" && !args.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
118
src/check_ast.rs
118
src/check_ast.rs
@@ -10,7 +10,7 @@ use rustpython_parser::ast::{
|
||||
};
|
||||
use rustpython_parser::parser;
|
||||
|
||||
use crate::ast::helpers::{match_name_or_attr, SubscriptKind};
|
||||
use crate::ast::helpers::{extract_handler_names, match_name_or_attr, SubscriptKind};
|
||||
use crate::ast::operations::{extract_all_names, SourceCodeLocator};
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
@@ -60,6 +60,7 @@ pub struct Checker<'a> {
|
||||
seen_docstring: bool,
|
||||
futures_allowed: bool,
|
||||
annotations_future_enabled: bool,
|
||||
except_handlers: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -74,25 +75,26 @@ impl<'a> Checker<'a> {
|
||||
autofix,
|
||||
path,
|
||||
locator: SourceCodeLocator::new(content),
|
||||
checks: vec![],
|
||||
parents: vec![],
|
||||
parent_stack: vec![],
|
||||
scopes: vec![],
|
||||
scope_stack: vec![],
|
||||
dead_scopes: vec![],
|
||||
deferred_string_annotations: vec![],
|
||||
deferred_annotations: vec![],
|
||||
deferred_functions: vec![],
|
||||
deferred_lambdas: vec![],
|
||||
deferred_assignments: vec![],
|
||||
in_f_string: None,
|
||||
in_annotation: false,
|
||||
in_literal: false,
|
||||
seen_non_import: false,
|
||||
seen_docstring: false,
|
||||
futures_allowed: true,
|
||||
annotations_future_enabled: false,
|
||||
checks: Default::default(),
|
||||
deletions: Default::default(),
|
||||
parents: Default::default(),
|
||||
parent_stack: Default::default(),
|
||||
scopes: Default::default(),
|
||||
scope_stack: Default::default(),
|
||||
dead_scopes: Default::default(),
|
||||
deferred_string_annotations: Default::default(),
|
||||
deferred_annotations: Default::default(),
|
||||
deferred_functions: Default::default(),
|
||||
deferred_lambdas: Default::default(),
|
||||
deferred_assignments: Default::default(),
|
||||
in_f_string: None,
|
||||
in_annotation: Default::default(),
|
||||
in_literal: Default::default(),
|
||||
seen_non_import: Default::default(),
|
||||
seen_docstring: Default::default(),
|
||||
futures_allowed: true,
|
||||
annotations_future_enabled: Default::default(),
|
||||
except_handlers: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,10 +548,13 @@ where
|
||||
plugins::if_tuple(self, stmt, test);
|
||||
}
|
||||
}
|
||||
StmtKind::Assert { test, .. } => {
|
||||
StmtKind::Assert { test, msg } => {
|
||||
if self.settings.enabled.contains(&CheckCode::F631) {
|
||||
plugins::assert_tuple(self, stmt, test);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::B011) {
|
||||
plugins::assert_false(self, stmt, test, msg);
|
||||
}
|
||||
}
|
||||
StmtKind::Try { handlers, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::F707) {
|
||||
@@ -557,6 +562,11 @@ where
|
||||
self.checks.push(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::B014)
|
||||
|| self.settings.enabled.contains(&CheckCode::B025)
|
||||
{
|
||||
plugins::duplicate_exceptions(self, stmt, handlers);
|
||||
}
|
||||
}
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::E731) {
|
||||
@@ -601,6 +611,27 @@ where
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
StmtKind::Try {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => {
|
||||
self.except_handlers.push(extract_handler_names(handlers));
|
||||
for stmt in body {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
self.except_handlers.pop();
|
||||
for excepthandler in handlers {
|
||||
self.visit_excepthandler(excepthandler)
|
||||
}
|
||||
for stmt in orelse {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
for stmt in finalbody {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
};
|
||||
|
||||
@@ -633,12 +664,20 @@ where
|
||||
let prev_in_annotation = self.in_annotation;
|
||||
|
||||
if self.in_annotation && self.annotations_future_enabled {
|
||||
self.deferred_annotations.push((
|
||||
expr,
|
||||
self.scope_stack.clone(),
|
||||
self.parent_stack.clone(),
|
||||
));
|
||||
visitor::walk_expr(self, expr);
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} = &expr.node
|
||||
{
|
||||
self.deferred_string_annotations
|
||||
.push((Range::from_located(expr), value));
|
||||
} else {
|
||||
self.deferred_annotations.push((
|
||||
expr,
|
||||
self.scope_stack.clone(),
|
||||
self.parent_stack.clone(),
|
||||
));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -718,11 +757,11 @@ where
|
||||
..
|
||||
} => {
|
||||
if self.settings.enabled.contains(&CheckCode::U005) {
|
||||
plugins::assert_equals(self, func);
|
||||
plugins::deprecated_unittest_alias(self, func);
|
||||
}
|
||||
|
||||
// flake8-super
|
||||
if self.settings.enabled.contains(&CheckCode::SPR001) {
|
||||
if self.settings.enabled.contains(&CheckCode::U008) {
|
||||
plugins::super_call_with_parameters(self, expr, func, args);
|
||||
}
|
||||
|
||||
@@ -788,6 +827,21 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::C409) {
|
||||
if let Some(check) =
|
||||
checks::unnecessary_literal_within_tuple_call(expr, func, args)
|
||||
{
|
||||
self.checks.push(check);
|
||||
};
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::C410) {
|
||||
if let Some(check) =
|
||||
checks::unnecessary_literal_within_list_call(expr, func, args)
|
||||
{
|
||||
self.checks.push(check);
|
||||
};
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::C415) {
|
||||
if let Some(check) = checks::unnecessary_subscript_reversal(expr, func, args) {
|
||||
self.checks.push(check);
|
||||
@@ -1463,6 +1517,14 @@ impl<'a> Checker<'a> {
|
||||
if self.path.ends_with("__init__.py") && id == "__path__" {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid flagging if NameError is handled.
|
||||
if let Some(handler_names) = self.except_handlers.last() {
|
||||
if handler_names.contains(&"NameError".to_string()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.checks.push(Check::new(
|
||||
CheckKind::UndefinedName(id.clone()),
|
||||
self.locate_check(Range::from_located(expr)),
|
||||
|
||||
@@ -184,15 +184,15 @@ pub fn check_lines(
|
||||
let mut valid_codes = vec![];
|
||||
for code in codes {
|
||||
if !matches.contains(&code) {
|
||||
invalid_codes.push(code);
|
||||
invalid_codes.push(code.to_string());
|
||||
} else {
|
||||
valid_codes.push(code);
|
||||
valid_codes.push(code.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if !invalid_codes.is_empty() {
|
||||
let mut check = Check::new(
|
||||
CheckKind::UnusedNOQA(Some(invalid_codes.join(", "))),
|
||||
CheckKind::UnusedNOQA(Some(invalid_codes)),
|
||||
Range {
|
||||
location: Location::new(row + 1, start + 1),
|
||||
end_location: Location::new(row + 1, end + 1),
|
||||
|
||||
117
src/checks.rs
117
src/checks.rs
@@ -1,6 +1,7 @@
|
||||
use itertools::Itertools;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
use strum_macros::{AsRefStr, EnumIter, EnumString};
|
||||
|
||||
use crate::ast::checks::Primitive;
|
||||
@@ -120,6 +121,10 @@ pub enum CheckCode {
|
||||
A001,
|
||||
A002,
|
||||
A003,
|
||||
// flake8-bugbear
|
||||
B011,
|
||||
B014,
|
||||
B025,
|
||||
// flake8-comprehensions
|
||||
C400,
|
||||
C401,
|
||||
@@ -129,9 +134,9 @@ pub enum CheckCode {
|
||||
C405,
|
||||
C406,
|
||||
C408,
|
||||
C409,
|
||||
C410,
|
||||
C415,
|
||||
// flake8-super
|
||||
SPR001,
|
||||
// flake8-print
|
||||
T201,
|
||||
T203,
|
||||
@@ -143,6 +148,7 @@ pub enum CheckCode {
|
||||
U005,
|
||||
U006,
|
||||
U007,
|
||||
U008,
|
||||
// Meta
|
||||
M001,
|
||||
}
|
||||
@@ -211,7 +217,11 @@ pub enum CheckKind {
|
||||
BuiltinVariableShadowing(String),
|
||||
BuiltinArgumentShadowing(String),
|
||||
BuiltinAttributeShadowing(String),
|
||||
// flakes8-comprehensions
|
||||
// flake8-bugbear
|
||||
DoNotAssertFalse,
|
||||
DuplicateHandlerException(String),
|
||||
DuplicateTryBlockException(String),
|
||||
// flake8-comprehensions
|
||||
UnnecessaryGeneratorList,
|
||||
UnnecessaryGeneratorSet,
|
||||
UnnecessaryGeneratorDict,
|
||||
@@ -220,9 +230,9 @@ pub enum CheckKind {
|
||||
UnnecessaryLiteralSet(String),
|
||||
UnnecessaryLiteralDict(String),
|
||||
UnnecessaryCollectionCall(String),
|
||||
UnnecessaryLiteralWithinTupleCall(String),
|
||||
UnnecessaryLiteralWithinListCall(String),
|
||||
UnnecessarySubscriptReversal(String),
|
||||
// flake8-super
|
||||
SuperCallWithParameters,
|
||||
// flake8-print
|
||||
PrintFound,
|
||||
PPrintFound,
|
||||
@@ -230,12 +240,13 @@ pub enum CheckKind {
|
||||
TypeOfPrimitive(Primitive),
|
||||
UnnecessaryAbspath,
|
||||
UselessMetaclassType,
|
||||
NoAssertEquals,
|
||||
DeprecatedUnittestAlias(String, String),
|
||||
UselessObjectInheritance(String),
|
||||
UsePEP585Annotation(String),
|
||||
UsePEP604Annotation,
|
||||
SuperCallWithParameters,
|
||||
// Meta
|
||||
UnusedNOQA(Option<String>),
|
||||
UnusedNOQA(Option<Vec<String>>),
|
||||
}
|
||||
|
||||
impl CheckCode {
|
||||
@@ -303,6 +314,10 @@ impl CheckCode {
|
||||
CheckCode::A001 => CheckKind::BuiltinVariableShadowing("...".to_string()),
|
||||
CheckCode::A002 => CheckKind::BuiltinArgumentShadowing("...".to_string()),
|
||||
CheckCode::A003 => CheckKind::BuiltinAttributeShadowing("...".to_string()),
|
||||
// flake8-bugbear
|
||||
CheckCode::B011 => CheckKind::DoNotAssertFalse,
|
||||
CheckCode::B014 => CheckKind::DuplicateHandlerException("Exception".to_string()),
|
||||
CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
|
||||
// flake8-comprehensions
|
||||
CheckCode::C400 => CheckKind::UnnecessaryGeneratorList,
|
||||
CheckCode::C401 => CheckKind::UnnecessaryGeneratorSet,
|
||||
@@ -314,11 +329,15 @@ impl CheckCode {
|
||||
CheckCode::C408 => {
|
||||
CheckKind::UnnecessaryCollectionCall("<dict/list/tuple>".to_string())
|
||||
}
|
||||
CheckCode::C409 => {
|
||||
CheckKind::UnnecessaryLiteralWithinTupleCall("<list/tuple>".to_string())
|
||||
}
|
||||
CheckCode::C410 => {
|
||||
CheckKind::UnnecessaryLiteralWithinListCall("<list/tuple>".to_string())
|
||||
}
|
||||
CheckCode::C415 => {
|
||||
CheckKind::UnnecessarySubscriptReversal("<reversed/set/sorted>".to_string())
|
||||
}
|
||||
// flake8-super
|
||||
CheckCode::SPR001 => CheckKind::SuperCallWithParameters,
|
||||
// flake8-print
|
||||
CheckCode::T201 => CheckKind::PrintFound,
|
||||
CheckCode::T203 => CheckKind::PPrintFound,
|
||||
@@ -327,9 +346,13 @@ impl CheckCode {
|
||||
CheckCode::U002 => CheckKind::UnnecessaryAbspath,
|
||||
CheckCode::U003 => CheckKind::TypeOfPrimitive(Primitive::Str),
|
||||
CheckCode::U004 => CheckKind::UselessObjectInheritance("...".to_string()),
|
||||
CheckCode::U005 => CheckKind::NoAssertEquals,
|
||||
CheckCode::U005 => CheckKind::DeprecatedUnittestAlias(
|
||||
"assertEquals".to_string(),
|
||||
"assertEqual".to_string(),
|
||||
),
|
||||
CheckCode::U006 => CheckKind::UsePEP585Annotation("List".to_string()),
|
||||
CheckCode::U007 => CheckKind::UsePEP604Annotation,
|
||||
CheckCode::U008 => CheckKind::SuperCallWithParameters,
|
||||
// Meta
|
||||
CheckCode::M001 => CheckKind::UnusedNOQA(None),
|
||||
}
|
||||
@@ -389,6 +412,10 @@ impl CheckKind {
|
||||
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
|
||||
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
|
||||
CheckKind::BuiltinAttributeShadowing(_) => &CheckCode::A003,
|
||||
// flake8-bugbear
|
||||
CheckKind::DoNotAssertFalse => &CheckCode::B011,
|
||||
CheckKind::DuplicateHandlerException(_) => &CheckCode::B014,
|
||||
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
|
||||
// flake8-comprehensions
|
||||
CheckKind::UnnecessaryGeneratorList => &CheckCode::C400,
|
||||
CheckKind::UnnecessaryGeneratorSet => &CheckCode::C401,
|
||||
@@ -398,9 +425,9 @@ impl CheckKind {
|
||||
CheckKind::UnnecessaryLiteralSet(_) => &CheckCode::C405,
|
||||
CheckKind::UnnecessaryLiteralDict(_) => &CheckCode::C406,
|
||||
CheckKind::UnnecessaryCollectionCall(_) => &CheckCode::C408,
|
||||
CheckKind::UnnecessaryLiteralWithinTupleCall(..) => &CheckCode::C409,
|
||||
CheckKind::UnnecessaryLiteralWithinListCall(..) => &CheckCode::C410,
|
||||
CheckKind::UnnecessarySubscriptReversal(_) => &CheckCode::C415,
|
||||
// flake8-super
|
||||
CheckKind::SuperCallWithParameters => &CheckCode::SPR001,
|
||||
// flake8-print
|
||||
CheckKind::PrintFound => &CheckCode::T201,
|
||||
CheckKind::PPrintFound => &CheckCode::T203,
|
||||
@@ -408,10 +435,11 @@ impl CheckKind {
|
||||
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
|
||||
CheckKind::UnnecessaryAbspath => &CheckCode::U002,
|
||||
CheckKind::UselessMetaclassType => &CheckCode::U001,
|
||||
CheckKind::NoAssertEquals => &CheckCode::U005,
|
||||
CheckKind::DeprecatedUnittestAlias(_, _) => &CheckCode::U005,
|
||||
CheckKind::UsePEP585Annotation(_) => &CheckCode::U006,
|
||||
CheckKind::UsePEP604Annotation => &CheckCode::U007,
|
||||
CheckKind::UselessObjectInheritance(_) => &CheckCode::U004,
|
||||
CheckKind::SuperCallWithParameters => &CheckCode::U008,
|
||||
// Meta
|
||||
CheckKind::UnusedNOQA(_) => &CheckCode::M001,
|
||||
}
|
||||
@@ -560,6 +588,17 @@ impl CheckKind {
|
||||
CheckKind::BuiltinAttributeShadowing(name) => {
|
||||
format!("Class attribute `{name}` is shadowing a python builtin")
|
||||
}
|
||||
// flake8-bugbear
|
||||
CheckKind::DoNotAssertFalse => {
|
||||
"Do not `assert False` (`python -O` removes these calls), raise `AssertionError()`"
|
||||
.to_string()
|
||||
}
|
||||
CheckKind::DuplicateHandlerException(name) => {
|
||||
format!("Exception handler with duplicate exception `{name}`")
|
||||
}
|
||||
CheckKind::DuplicateTryBlockException(name) => {
|
||||
format!("try-except block with duplicate exception `{name}`")
|
||||
}
|
||||
// flake8-comprehensions
|
||||
CheckKind::UnnecessaryGeneratorList => {
|
||||
"Unnecessary generator - rewrite as a list comprehension".to_string()
|
||||
@@ -585,13 +624,31 @@ impl CheckKind {
|
||||
CheckKind::UnnecessaryCollectionCall(obj_type) => {
|
||||
format!("Unnecessary {obj_type} call - rewrite as a literal")
|
||||
}
|
||||
CheckKind::UnnecessaryLiteralWithinTupleCall(literal) => {
|
||||
if literal == "list" {
|
||||
format!(
|
||||
"Unnecessary {literal} literal passed to tuple() - rewrite as a tuple literal"
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Unnecessary {literal} literal passed to tuple() - remove the outer call to tuple()"
|
||||
)
|
||||
}
|
||||
}
|
||||
CheckKind::UnnecessaryLiteralWithinListCall(literal) => {
|
||||
if literal == "list" {
|
||||
format!(
|
||||
"Unnecessary {literal} literal passed to list() - remove the outer call to list()"
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Unnecessary {literal} literal passed to list() - rewrite as a list literal"
|
||||
)
|
||||
}
|
||||
}
|
||||
CheckKind::UnnecessarySubscriptReversal(func) => {
|
||||
format!("Unnecessary subscript reversal of iterable within {func}()")
|
||||
}
|
||||
// flake8-super
|
||||
CheckKind::SuperCallWithParameters => {
|
||||
"Use `super()` instead of `super(__class__, self)`".to_string()
|
||||
}
|
||||
// flake8-print
|
||||
CheckKind::PrintFound => "`print` found".to_string(),
|
||||
CheckKind::PPrintFound => "`pprint` found".to_string(),
|
||||
@@ -603,8 +660,8 @@ impl CheckKind {
|
||||
"`abspath(__file__)` is unnecessary in Python 3.9 and later".to_string()
|
||||
}
|
||||
CheckKind::UselessMetaclassType => "`__metaclass__ = type` is implied".to_string(),
|
||||
CheckKind::NoAssertEquals => {
|
||||
"`assertEquals` is deprecated, use `assertEqual` instead".to_string()
|
||||
CheckKind::DeprecatedUnittestAlias(alias, target) => {
|
||||
format!("`{}` is deprecated, use `{}` instead", alias, target)
|
||||
}
|
||||
CheckKind::UselessObjectInheritance(name) => {
|
||||
format!("Class `{name}` inherits from object")
|
||||
@@ -617,10 +674,25 @@ impl CheckKind {
|
||||
)
|
||||
}
|
||||
CheckKind::UsePEP604Annotation => "Use `X | Y` for type annotations".to_string(),
|
||||
CheckKind::SuperCallWithParameters => {
|
||||
"Use `super()` instead of `super(__class__, self)`".to_string()
|
||||
}
|
||||
// Meta
|
||||
CheckKind::UnusedNOQA(code) => match code {
|
||||
CheckKind::UnusedNOQA(codes) => match codes {
|
||||
None => "Unused `noqa` directive".to_string(),
|
||||
Some(code) => format!("Unused `noqa` directive for: {code}"),
|
||||
Some(codes) => {
|
||||
let codes = codes
|
||||
.iter()
|
||||
.map(|code| {
|
||||
if CheckCode::from_str(code).is_ok() {
|
||||
code.to_string()
|
||||
} else {
|
||||
format!("{code} (not implemented)")
|
||||
}
|
||||
})
|
||||
.join(", ");
|
||||
format!("Unused `noqa` directive for: {codes}")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -629,7 +701,8 @@ impl CheckKind {
|
||||
pub fn fixable(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
CheckKind::NoAssertEquals
|
||||
CheckKind::DeprecatedUnittestAlias(_, _)
|
||||
| CheckKind::DoNotAssertFalse
|
||||
| CheckKind::PPrintFound
|
||||
| CheckKind::PrintFound
|
||||
| CheckKind::SuperCallWithParameters
|
||||
|
||||
@@ -115,7 +115,7 @@ impl SourceGenerator {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unparse_stmt<U>(&mut self, ast: &Stmt<U>) -> fmt::Result {
|
||||
pub fn unparse_stmt<U>(&mut self, ast: &Stmt<U>) -> fmt::Result {
|
||||
macro_rules! statement {
|
||||
($body:block) => {{
|
||||
self.newline()?;
|
||||
|
||||
190
src/linter.rs
190
src/linter.rs
@@ -376,9 +376,57 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f401() -> Result<()> {
|
||||
fn f401_0() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/F401.py"),
|
||||
Path::new("./resources/test/fixtures/F401_0.py"),
|
||||
&settings::Settings::for_rule(CheckCode::F401),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f401_1() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/F401_1.py"),
|
||||
&settings::Settings::for_rule(CheckCode::F401),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f401_2() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/F401_2.py"),
|
||||
&settings::Settings::for_rule(CheckCode::F401),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f401_3() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/F401_3.py"),
|
||||
&settings::Settings::for_rule(CheckCode::F401),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f401_4() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/F401_4.py"),
|
||||
&settings::Settings::for_rule(CheckCode::F401),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
@@ -714,42 +762,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn m001() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/M001.py"),
|
||||
&settings::Settings::for_rules(vec![CheckCode::M001, CheckCode::E501, CheckCode::F841]),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/__init__.py"),
|
||||
&settings::Settings::for_rules(vec![CheckCode::F821, CheckCode::F822]),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotations() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/future_annotations.py"),
|
||||
&settings::Settings::for_rules(vec![CheckCode::F401, CheckCode::F821]),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn e999() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
@@ -798,6 +810,42 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn b011() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/B011.py"),
|
||||
&settings::Settings::for_rule(CheckCode::B011),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn b014() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/B014.py"),
|
||||
&settings::Settings::for_rule(CheckCode::B014),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn b025() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/B025.py"),
|
||||
&settings::Settings::for_rule(CheckCode::B025),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c400() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
@@ -894,6 +942,30 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c409() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/C409.py"),
|
||||
&settings::Settings::for_rule(CheckCode::C409),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c410() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/C410.py"),
|
||||
&settings::Settings::for_rule(CheckCode::C410),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c415() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
@@ -907,10 +979,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spr001() -> Result<()> {
|
||||
fn u008() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/SPR001.py"),
|
||||
&settings::Settings::for_rule(CheckCode::SPR001),
|
||||
Path::new("./resources/test/fixtures/U008.py"),
|
||||
&settings::Settings::for_rule(CheckCode::U008),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
@@ -1025,4 +1097,40 @@ mod tests {
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn m001() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/M001.py"),
|
||||
&settings::Settings::for_rules(vec![CheckCode::M001, CheckCode::E501, CheckCode::F841]),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/__init__.py"),
|
||||
&settings::Settings::for_rules(vec![CheckCode::F821, CheckCode::F822]),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotations() -> Result<()> {
|
||||
let mut checks = check_path(
|
||||
Path::new("./resources/test/fixtures/future_annotations.py"),
|
||||
&settings::Settings::for_rules(vec![CheckCode::F401, CheckCode::F821]),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
pub use assert_equals::assert_equals;
|
||||
pub use assert_false::assert_false;
|
||||
pub use assert_tuple::assert_tuple;
|
||||
pub use deprecated_unittest_alias::deprecated_unittest_alias;
|
||||
pub use duplicate_exceptions::duplicate_exceptions;
|
||||
pub use if_tuple::if_tuple;
|
||||
pub use invalid_print_syntax::invalid_print_syntax;
|
||||
pub use print_call::print_call;
|
||||
@@ -11,8 +13,10 @@ pub use use_pep604_annotation::use_pep604_annotation;
|
||||
pub use useless_metaclass_type::useless_metaclass_type;
|
||||
pub use useless_object_inheritance::useless_object_inheritance;
|
||||
|
||||
mod assert_equals;
|
||||
mod assert_false;
|
||||
mod assert_tuple;
|
||||
mod deprecated_unittest_alias;
|
||||
mod duplicate_exceptions;
|
||||
mod if_tuple;
|
||||
mod invalid_print_syntax;
|
||||
mod print_call;
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
use rustpython_ast::{Expr, Location};
|
||||
|
||||
use crate::ast::checks;
|
||||
use crate::autofix::fixer;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::Fix;
|
||||
|
||||
pub fn assert_equals(checker: &mut Checker, expr: &Expr) {
|
||||
if let Some(mut check) = checks::check_assert_equals(expr) {
|
||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
check.amend(Fix {
|
||||
content: "assertEqual".to_string(),
|
||||
location: Location::new(expr.location.row(), expr.location.column() + 1),
|
||||
end_location: Location::new(
|
||||
expr.location.row(),
|
||||
expr.location.column() + 1 + "assertEquals".len(),
|
||||
),
|
||||
applied: false,
|
||||
});
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
61
src/plugins/assert_false.rs
Normal file
61
src/plugins/assert_false.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::fixer;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind, Fix};
|
||||
use crate::code_gen::SourceGenerator;
|
||||
|
||||
fn assertion_error(msg: &Option<Box<Expr>>) -> Stmt {
|
||||
Stmt::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
StmtKind::Raise {
|
||||
exc: Some(Box::new(Expr::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExprKind::Call {
|
||||
func: Box::new(Expr::new(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExprKind::Name {
|
||||
id: "AssertionError".to_string(),
|
||||
ctx: ExprContext::Load,
|
||||
},
|
||||
)),
|
||||
args: if let Some(msg) = msg {
|
||||
vec![*msg.clone()]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
keywords: vec![],
|
||||
},
|
||||
))),
|
||||
cause: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: &Option<Box<Expr>>) {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(false),
|
||||
..
|
||||
} = &test.node
|
||||
{
|
||||
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
|
||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
let mut generator = SourceGenerator::new();
|
||||
if let Ok(()) = generator.unparse_stmt(&assertion_error(msg)) {
|
||||
if let Ok(content) = generator.generate() {
|
||||
check.amend(Fix {
|
||||
content,
|
||||
location: stmt.location,
|
||||
end_location: stmt.end_location,
|
||||
applied: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
56
src/plugins/deprecated_unittest_alias.rs
Normal file
56
src/plugins/deprecated_unittest_alias.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rustpython_ast::{Expr, ExprKind, Location};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::fixer;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind, Fix};
|
||||
|
||||
static DEPRECATED_ALIASES: Lazy<BTreeMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
BTreeMap::from([
|
||||
("failUnlessEqual", "assertEqual"),
|
||||
("assertEquals", "assertEqual"),
|
||||
("failIfEqual", "assertNotEqual"),
|
||||
("assertNotEquals", "assertNotEqual"),
|
||||
("failUnless", "assertTrue"),
|
||||
("assert_", "assertTrue"),
|
||||
("failIf", "assertFalse"),
|
||||
("failUnlessRaises", "assertRaises"),
|
||||
("failUnlessAlmostEqual", "assertAlmostEqual"),
|
||||
("assertAlmostEquals", "assertAlmostEqual"),
|
||||
("failIfAlmostEqual", "assertNotAlmostEqual"),
|
||||
("assertNotAlmostEquals", "assertNotAlmostEqual"),
|
||||
("assertRegexpMatches", "assertRegex"),
|
||||
("assertNotRegexpMatches", "assertNotRegex"),
|
||||
("assertRaisesRegexp", "assertRaisesRegex"),
|
||||
])
|
||||
});
|
||||
|
||||
pub fn deprecated_unittest_alias(checker: &mut Checker, expr: &Expr) {
|
||||
if let ExprKind::Attribute { value, attr, .. } = &expr.node {
|
||||
if let Some(target) = DEPRECATED_ALIASES.get(attr.as_str()) {
|
||||
if let ExprKind::Name { id, .. } = &value.node {
|
||||
if id == "self" {
|
||||
let mut check = Check::new(
|
||||
CheckKind::DeprecatedUnittestAlias(attr.to_string(), target.to_string()),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
check.amend(Fix {
|
||||
content: format!("self.{}", target),
|
||||
location: Location::new(expr.location.row(), expr.location.column()),
|
||||
end_location: Location::new(
|
||||
expr.end_location.row(),
|
||||
expr.end_location.column(),
|
||||
),
|
||||
applied: false,
|
||||
});
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/plugins/duplicate_exceptions.rs
Normal file
82
src/plugins/duplicate_exceptions.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use itertools::Itertools;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::types::{CheckLocator, Range};
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
|
||||
pub fn duplicate_handler_exceptions(
|
||||
checker: &mut Checker,
|
||||
stmt: &Stmt,
|
||||
elts: &Vec<Expr>,
|
||||
) -> BTreeSet<String> {
|
||||
let mut seen: BTreeSet<String> = Default::default();
|
||||
let mut duplicates: BTreeSet<String> = Default::default();
|
||||
for type_ in elts {
|
||||
if let Some(name) = helpers::compose_call_path(type_) {
|
||||
if seen.contains(&name) {
|
||||
duplicates.insert(name);
|
||||
} else {
|
||||
seen.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::B014) {
|
||||
// TODO(charlie): Handle "BaseException" and redundant exception aliases.
|
||||
for duplicate in duplicates.into_iter().sorted() {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DuplicateHandlerException(duplicate),
|
||||
checker.locate_check(Range::from_located(stmt)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
seen
|
||||
}
|
||||
|
||||
pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Excepthandler]) {
|
||||
let mut seen: BTreeSet<String> = Default::default();
|
||||
let mut duplicates: BTreeSet<String> = Default::default();
|
||||
for handler in handlers {
|
||||
match &handler.node {
|
||||
ExcepthandlerKind::ExceptHandler { type_, .. } => {
|
||||
if let Some(type_) = type_ {
|
||||
match &type_.node {
|
||||
ExprKind::Attribute { .. } | ExprKind::Name { .. } => {
|
||||
if let Some(name) = helpers::compose_call_path(type_) {
|
||||
if seen.contains(&name) {
|
||||
duplicates.insert(name);
|
||||
} else {
|
||||
seen.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
for name in duplicate_handler_exceptions(checker, stmt, elts) {
|
||||
if seen.contains(&name) {
|
||||
duplicates.insert(name);
|
||||
} else {
|
||||
seen.insert(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.settings.enabled.contains(&CheckCode::B025) {
|
||||
for duplicate in duplicates.into_iter().sorted() {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DuplicateTryBlockException(duplicate),
|
||||
checker.locate_check(Range::from_located(stmt)),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::{Expr, Stmt};
|
||||
|
||||
use crate::ast::checks;
|
||||
use crate::ast::{checks, helpers};
|
||||
use crate::autofix::{fixer, fixes};
|
||||
use crate::check_ast::Checker;
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn super_call_with_parameters(
|
||||
) {
|
||||
// Only bother going through the super check at all if we're in a `super` call.
|
||||
// (We check this in `check_super_args` too, so this is just an optimization.)
|
||||
if checks::is_super_call_with_arguments(func, args) {
|
||||
if helpers::is_super_call_with_arguments(func, args) {
|
||||
let scope = checker.current_scope();
|
||||
let parents: Vec<&Stmt> = checker
|
||||
.parent_stack
|
||||
|
||||
37
src/snapshots/ruff__linter__tests__b011.snap
Normal file
37
src/snapshots/ruff__linter__tests__b011.snap
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: DoNotAssertFalse
|
||||
location:
|
||||
row: 8
|
||||
column: 8
|
||||
end_location:
|
||||
row: 8
|
||||
column: 13
|
||||
fix:
|
||||
content: raise AssertionError()
|
||||
location:
|
||||
row: 8
|
||||
column: 1
|
||||
end_location:
|
||||
row: 8
|
||||
column: 13
|
||||
applied: false
|
||||
- kind: DoNotAssertFalse
|
||||
location:
|
||||
row: 10
|
||||
column: 8
|
||||
end_location:
|
||||
row: 10
|
||||
column: 13
|
||||
fix:
|
||||
content: "raise AssertionError(\"message\")"
|
||||
location:
|
||||
row: 10
|
||||
column: 1
|
||||
end_location:
|
||||
row: 10
|
||||
column: 24
|
||||
applied: false
|
||||
|
||||
32
src/snapshots/ruff__linter__tests__b014.snap
Normal file
32
src/snapshots/ruff__linter__tests__b014.snap
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
DuplicateHandlerException: OSError
|
||||
location:
|
||||
row: 15
|
||||
column: 1
|
||||
end_location:
|
||||
row: 22
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateHandlerException: MyError
|
||||
location:
|
||||
row: 26
|
||||
column: 1
|
||||
end_location:
|
||||
row: 33
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateHandlerException: re.error
|
||||
location:
|
||||
row: 47
|
||||
column: 1
|
||||
end_location:
|
||||
row: 54
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
41
src/snapshots/ruff__linter__tests__b025.snap
Normal file
41
src/snapshots/ruff__linter__tests__b025.snap
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
DuplicateTryBlockException: ValueError
|
||||
location:
|
||||
row: 15
|
||||
column: 1
|
||||
end_location:
|
||||
row: 22
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateTryBlockException: pickle.PickleError
|
||||
location:
|
||||
row: 22
|
||||
column: 1
|
||||
end_location:
|
||||
row: 31
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateTryBlockException: TypeError
|
||||
location:
|
||||
row: 31
|
||||
column: 1
|
||||
end_location:
|
||||
row: 39
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
DuplicateTryBlockException: ValueError
|
||||
location:
|
||||
row: 31
|
||||
column: 1
|
||||
end_location:
|
||||
row: 39
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
32
src/snapshots/ruff__linter__tests__c409.snap
Normal file
32
src/snapshots/ruff__linter__tests__c409.snap
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnnecessaryLiteralWithinTupleCall: list
|
||||
location:
|
||||
row: 1
|
||||
column: 6
|
||||
end_location:
|
||||
row: 1
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
UnnecessaryLiteralWithinTupleCall: tuple
|
||||
location:
|
||||
row: 2
|
||||
column: 6
|
||||
end_location:
|
||||
row: 2
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
UnnecessaryLiteralWithinTupleCall: list
|
||||
location:
|
||||
row: 3
|
||||
column: 6
|
||||
end_location:
|
||||
row: 3
|
||||
column: 15
|
||||
fix: ~
|
||||
|
||||
41
src/snapshots/ruff__linter__tests__c410.snap
Normal file
41
src/snapshots/ruff__linter__tests__c410.snap
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnnecessaryLiteralWithinListCall: list
|
||||
location:
|
||||
row: 1
|
||||
column: 6
|
||||
end_location:
|
||||
row: 1
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
UnnecessaryLiteralWithinListCall: tuple
|
||||
location:
|
||||
row: 2
|
||||
column: 6
|
||||
end_location:
|
||||
row: 2
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
UnnecessaryLiteralWithinListCall: list
|
||||
location:
|
||||
row: 3
|
||||
column: 6
|
||||
end_location:
|
||||
row: 3
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
UnnecessaryLiteralWithinListCall: tuple
|
||||
location:
|
||||
row: 4
|
||||
column: 6
|
||||
end_location:
|
||||
row: 4
|
||||
column: 14
|
||||
fix: ~
|
||||
|
||||
131
src/snapshots/ruff__linter__tests__f401_0.snap
Normal file
131
src/snapshots/ruff__linter__tests__f401_0.snap
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- functools
|
||||
location:
|
||||
row: 2
|
||||
column: 1
|
||||
end_location:
|
||||
row: 2
|
||||
column: 21
|
||||
fix:
|
||||
content: import os
|
||||
location:
|
||||
row: 2
|
||||
column: 1
|
||||
end_location:
|
||||
row: 2
|
||||
column: 21
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- collections.OrderedDict
|
||||
location:
|
||||
row: 4
|
||||
column: 1
|
||||
end_location:
|
||||
row: 8
|
||||
column: 2
|
||||
fix:
|
||||
content: "from collections import (\n Counter,\n namedtuple,\n)"
|
||||
location:
|
||||
row: 4
|
||||
column: 1
|
||||
end_location:
|
||||
row: 8
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- logging.handlers
|
||||
location:
|
||||
row: 12
|
||||
column: 1
|
||||
end_location:
|
||||
row: 12
|
||||
column: 24
|
||||
fix:
|
||||
content: import logging.handlers
|
||||
location:
|
||||
row: 12
|
||||
column: 1
|
||||
end_location:
|
||||
row: 12
|
||||
column: 24
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- shelve
|
||||
location:
|
||||
row: 33
|
||||
column: 5
|
||||
end_location:
|
||||
row: 33
|
||||
column: 18
|
||||
fix:
|
||||
content: ""
|
||||
location:
|
||||
row: 33
|
||||
column: 1
|
||||
end_location:
|
||||
row: 34
|
||||
column: 1
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- importlib
|
||||
location:
|
||||
row: 34
|
||||
column: 5
|
||||
end_location:
|
||||
row: 34
|
||||
column: 21
|
||||
fix:
|
||||
content: ""
|
||||
location:
|
||||
row: 34
|
||||
column: 1
|
||||
end_location:
|
||||
row: 35
|
||||
column: 1
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- pathlib
|
||||
location:
|
||||
row: 38
|
||||
column: 5
|
||||
end_location:
|
||||
row: 38
|
||||
column: 19
|
||||
fix:
|
||||
content: ""
|
||||
location:
|
||||
row: 38
|
||||
column: 1
|
||||
end_location:
|
||||
row: 39
|
||||
column: 1
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- pickle
|
||||
location:
|
||||
row: 53
|
||||
column: 9
|
||||
end_location:
|
||||
row: 53
|
||||
column: 22
|
||||
fix:
|
||||
content: pass
|
||||
location:
|
||||
row: 53
|
||||
column: 9
|
||||
end_location:
|
||||
row: 53
|
||||
column: 22
|
||||
applied: false
|
||||
|
||||
6
src/snapshots/ruff__linter__tests__f401_1.snap
Normal file
6
src/snapshots/ruff__linter__tests__f401_1.snap
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
||||
6
src/snapshots/ruff__linter__tests__f401_2.snap
Normal file
6
src/snapshots/ruff__linter__tests__f401_2.snap
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
||||
6
src/snapshots/ruff__linter__tests__f401_3.snap
Normal file
6
src/snapshots/ruff__linter__tests__f401_3.snap
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
||||
6
src/snapshots/ruff__linter__tests__f401_4.snap
Normal file
6
src/snapshots/ruff__linter__tests__f401_4.snap
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
||||
@@ -20,7 +20,8 @@ expression: checks
|
||||
column: 18
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA: E501
|
||||
UnusedNOQA:
|
||||
- E501
|
||||
location:
|
||||
row: 13
|
||||
column: 10
|
||||
@@ -37,7 +38,9 @@ expression: checks
|
||||
column: 24
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA: E501
|
||||
UnusedNOQA:
|
||||
- F841
|
||||
- E501
|
||||
location:
|
||||
row: 16
|
||||
column: 10
|
||||
@@ -45,7 +48,7 @@ expression: checks
|
||||
row: 16
|
||||
column: 30
|
||||
fix:
|
||||
content: " # noqa: F841"
|
||||
content: ""
|
||||
location:
|
||||
row: 16
|
||||
column: 10
|
||||
@@ -54,54 +57,74 @@ expression: checks
|
||||
column: 30
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA: F841
|
||||
UnusedNOQA:
|
||||
- W191
|
||||
location:
|
||||
row: 41
|
||||
row: 19
|
||||
column: 10
|
||||
end_location:
|
||||
row: 19
|
||||
column: 30
|
||||
fix:
|
||||
content: " # noqa: F841"
|
||||
location:
|
||||
row: 19
|
||||
column: 10
|
||||
end_location:
|
||||
row: 19
|
||||
column: 30
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA:
|
||||
- F841
|
||||
location:
|
||||
row: 44
|
||||
column: 4
|
||||
end_location:
|
||||
row: 41
|
||||
row: 44
|
||||
column: 24
|
||||
fix:
|
||||
content: " # noqa: E501"
|
||||
location:
|
||||
row: 41
|
||||
row: 44
|
||||
column: 4
|
||||
end_location:
|
||||
row: 41
|
||||
row: 44
|
||||
column: 24
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA: E501
|
||||
UnusedNOQA:
|
||||
- E501
|
||||
location:
|
||||
row: 49
|
||||
row: 52
|
||||
column: 4
|
||||
end_location:
|
||||
row: 49
|
||||
row: 52
|
||||
column: 18
|
||||
fix:
|
||||
content: ""
|
||||
location:
|
||||
row: 49
|
||||
row: 52
|
||||
column: 4
|
||||
end_location:
|
||||
row: 49
|
||||
row: 52
|
||||
column: 18
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedNOQA: ~
|
||||
location:
|
||||
row: 57
|
||||
row: 60
|
||||
column: 4
|
||||
end_location:
|
||||
row: 57
|
||||
row: 60
|
||||
column: 12
|
||||
fix:
|
||||
content: ""
|
||||
location:
|
||||
row: 57
|
||||
row: 60
|
||||
column: 4
|
||||
end_location:
|
||||
row: 57
|
||||
row: 60
|
||||
column: 12
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -2,36 +2,80 @@
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: NoAssertEquals
|
||||
- kind:
|
||||
DeprecatedUnittestAlias:
|
||||
- assertEquals
|
||||
- assertEqual
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
row: 6
|
||||
column: 9
|
||||
end_location:
|
||||
row: 1
|
||||
column: 18
|
||||
row: 6
|
||||
column: 26
|
||||
fix:
|
||||
content: assertEqual
|
||||
content: self.assertEqual
|
||||
location:
|
||||
row: 1
|
||||
column: 2
|
||||
row: 6
|
||||
column: 9
|
||||
end_location:
|
||||
row: 1
|
||||
column: 14
|
||||
row: 6
|
||||
column: 26
|
||||
applied: false
|
||||
- kind: NoAssertEquals
|
||||
- kind:
|
||||
DeprecatedUnittestAlias:
|
||||
- assertEquals
|
||||
- assertEqual
|
||||
location:
|
||||
row: 2
|
||||
column: 1
|
||||
row: 7
|
||||
column: 9
|
||||
end_location:
|
||||
row: 2
|
||||
column: 18
|
||||
row: 7
|
||||
column: 26
|
||||
fix:
|
||||
content: assertEqual
|
||||
content: self.assertEqual
|
||||
location:
|
||||
row: 2
|
||||
column: 2
|
||||
row: 7
|
||||
column: 9
|
||||
end_location:
|
||||
row: 2
|
||||
column: 14
|
||||
row: 7
|
||||
column: 26
|
||||
applied: false
|
||||
- kind:
|
||||
DeprecatedUnittestAlias:
|
||||
- failUnlessAlmostEqual
|
||||
- assertAlmostEqual
|
||||
location:
|
||||
row: 9
|
||||
column: 9
|
||||
end_location:
|
||||
row: 9
|
||||
column: 35
|
||||
fix:
|
||||
content: self.assertAlmostEqual
|
||||
location:
|
||||
row: 9
|
||||
column: 9
|
||||
end_location:
|
||||
row: 9
|
||||
column: 35
|
||||
applied: false
|
||||
- kind:
|
||||
DeprecatedUnittestAlias:
|
||||
- assertNotRegexpMatches
|
||||
- assertNotRegex
|
||||
location:
|
||||
row: 10
|
||||
column: 9
|
||||
end_location:
|
||||
row: 10
|
||||
column: 36
|
||||
fix:
|
||||
content: self.assertNotRegex
|
||||
location:
|
||||
row: 10
|
||||
column: 9
|
||||
end_location:
|
||||
row: 10
|
||||
column: 36
|
||||
applied: false
|
||||
|
||||
|
||||
85
src/snapshots/ruff__linter__tests__u008.snap
Normal file
85
src/snapshots/ruff__linter__tests__u008.snap
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: SuperCallWithParameters
|
||||
location:
|
||||
row: 17
|
||||
column: 18
|
||||
end_location:
|
||||
row: 17
|
||||
column: 36
|
||||
fix:
|
||||
content: super()
|
||||
location:
|
||||
row: 17
|
||||
column: 18
|
||||
end_location:
|
||||
row: 17
|
||||
column: 36
|
||||
applied: false
|
||||
- kind: SuperCallWithParameters
|
||||
location:
|
||||
row: 18
|
||||
column: 9
|
||||
end_location:
|
||||
row: 18
|
||||
column: 27
|
||||
fix:
|
||||
content: super()
|
||||
location:
|
||||
row: 18
|
||||
column: 9
|
||||
end_location:
|
||||
row: 18
|
||||
column: 27
|
||||
applied: false
|
||||
- kind: SuperCallWithParameters
|
||||
location:
|
||||
row: 19
|
||||
column: 9
|
||||
end_location:
|
||||
row: 22
|
||||
column: 10
|
||||
fix:
|
||||
content: super()
|
||||
location:
|
||||
row: 19
|
||||
column: 9
|
||||
end_location:
|
||||
row: 22
|
||||
column: 10
|
||||
applied: false
|
||||
- kind: SuperCallWithParameters
|
||||
location:
|
||||
row: 36
|
||||
column: 9
|
||||
end_location:
|
||||
row: 36
|
||||
column: 29
|
||||
fix:
|
||||
content: super()
|
||||
location:
|
||||
row: 36
|
||||
column: 9
|
||||
end_location:
|
||||
row: 36
|
||||
column: 29
|
||||
applied: false
|
||||
- kind: SuperCallWithParameters
|
||||
location:
|
||||
row: 50
|
||||
column: 13
|
||||
end_location:
|
||||
row: 50
|
||||
column: 33
|
||||
fix:
|
||||
content: super()
|
||||
location:
|
||||
row: 50
|
||||
column: 13
|
||||
end_location:
|
||||
row: 50
|
||||
column: 33
|
||||
applied: false
|
||||
|
||||
Reference in New Issue
Block a user