Compare commits

..

20 Commits

Author SHA1 Message Date
Charlie Marsh
4730911b25 Bump version to 0.0.68 2022-10-10 16:50:09 -04:00
Charlie Marsh
4e9fb9907a Implement D205, D209, and D210 (#398) 2022-10-10 16:49:51 -04:00
Charlie Marsh
b8dce8922d Implement D200 (OneLinerDocstring) (#397) 2022-10-10 16:07:51 -04:00
Charlie Marsh
30877127bc Implement D400 (DocstringEndsInNonPeriod) (#396) 2022-10-10 15:35:23 -04:00
Charlie Marsh
8b66bbdc9b Regenerate rules table 2022-10-10 15:18:18 -04:00
Charlie Marsh
71d3a84b14 Implement D410 (EmptyDocstring) (#395) 2022-10-10 15:15:38 -04:00
Charlie Marsh
323a5c857c Implement docstring tracking (#394) 2022-10-10 15:15:09 -04:00
Charlie Marsh
42cec3f5a0 Regenerate rules table 2022-10-10 14:02:31 -04:00
Charlie Marsh
ee42413e10 Enable autofix for B014 2022-10-10 13:18:43 -04:00
Charlie Marsh
765db12b84 Remove check_ prefix from check utilities (#393) 2022-10-10 12:58:40 -04:00
Charlie Marsh
e1b711d9c6 Bump version to 0.0.67 2022-10-10 12:55:04 -04:00
Charlie Marsh
35f593846e Implement B014 from flake8-bugbear (#392) 2022-10-10 12:53:42 -04:00
Charlie Marsh
c384fa513b Implement B025 from flake8-bugbear (#391) 2022-10-10 12:18:31 -04:00
Charlie Marsh
022ff64d29 Implement B011 from flake8-bugbear (#390) 2022-10-10 10:55:55 -04:00
Charlie Marsh
5a06fb28fd Bump version to 0.0.66 2022-10-10 10:03:59 -04:00
Charlie Marsh
46750a3e17 Flag unimplemented error codes in M001 (#388) 2022-10-10 10:03:40 -04:00
Charlie Marsh
9cc902b802 Avoid F821 false-positives with NameError (#386) 2022-10-10 09:39:59 -04:00
Harutaka Kawamura
c2a36ebd1e Implement C410 (#382) 2022-10-09 23:33:55 -04:00
Charlie Marsh
34ca225393 Rename flakes8 to flake8 2022-10-09 23:02:44 -04:00
Harutaka Kawamura
38c30905e6 Implement C409 (#381) 2022-10-09 22:34:52 -04:00
47 changed files with 2226 additions and 256 deletions

2
Cargo.lock generated
View File

@@ -1907,7 +1907,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.65"
version = "0.0.68"
dependencies = [
"anyhow",
"bincode",

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.65"
version = "0.0.68"
edition = "2021"
[lib]

View File

@@ -215,7 +215,9 @@ 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)
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) (6/47)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (partial)
Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis Flake8:
@@ -278,6 +280,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: `ValueError | | 🛠 |
| 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,6 +291,8 @@ 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>() | | |
| T201 | PrintFound | `print` found | | 🛠 |
| T203 | PPrintFound | `pprint` found | | 🛠 |
@@ -293,10 +300,16 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
| 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)` | | 🛠 |
| D200 | OneLinerDocstring | One-line docstring should fit on one line | | |
| D205 | BlankLineAfterSummary | 1 blank line required between summary line and description | | |
| D209 | NewLineAfterLastParagraph | Multi-line docstring closing quotes should be on a separate line | | |
| D210 | NoSurroundingWhitespace | No whitespaces allowed surrounding docstring text | | |
| D400 | DocstringEndsInNonPeriod | First line should end with a period | | |
| D419 | EmptyDocstring | Docstring is empty | | |
| M001 | UnusedNOQA | Unused `noqa` directive | | 🛠 |
## Integrations

10
resources/test/fixtures/B011.py vendored Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
t1 = tuple([1, 2])
t2 = tuple((1, 2))
t3 = tuple([])

4
resources/test/fixtures/C410.py vendored Normal file
View File

@@ -0,0 +1,4 @@
l1 = list([1, 2])
l2 = list((1, 2))
l3 = list([])
l4 = list(())

534
resources/test/fixtures/D.py vendored Normal file
View File

@@ -0,0 +1,534 @@
# No docstring, so we can test D100
from functools import wraps
import os
from .expected import Expectation
from typing import overload
expectation = Expectation()
expect = expectation.expect
expect('class_', 'D101: Missing docstring in public class')
class class_:
expect('meta', 'D419: Docstring is empty')
class meta:
""""""
@expect('D102: Missing docstring in public method')
def method(self=None):
pass
def _ok_since_private(self=None):
pass
@overload
def overloaded_method(self, a: int) -> str:
...
@overload
def overloaded_method(self, a: str) -> str:
"""Foo bar documentation."""
...
def overloaded_method(a):
"""Foo bar documentation."""
return str(a)
expect('overloaded_method',
"D418: Function/ Method decorated with @overload"
" shouldn't contain a docstring")
@property
def foo(self):
"""The foo of the thing, which isn't in imperitive mood."""
return "hello"
@expect('D102: Missing docstring in public method')
def __new__(self=None):
pass
@expect('D107: Missing docstring in __init__')
def __init__(self=None):
pass
@expect('D105: Missing docstring in magic method')
def __str__(self=None):
pass
@expect('D102: Missing docstring in public method')
def __call__(self=None, x=None, y=None, z=None):
pass
@expect('D419: Docstring is empty')
def function():
""" """
def ok_since_nested():
pass
@expect('D419: Docstring is empty')
def nested():
''
def function_with_nesting():
"""Foo bar documentation."""
@overload
def nested_overloaded_func(a: int) -> str:
...
@overload
def nested_overloaded_func(a: str) -> str:
"""Foo bar documentation."""
...
def nested_overloaded_func(a):
"""Foo bar documentation."""
return str(a)
expect('nested_overloaded_func',
"D418: Function/ Method decorated with @overload"
" shouldn't contain a docstring")
@overload
def overloaded_func(a: int) -> str:
...
@overload
def overloaded_func(a: str) -> str:
"""Foo bar documentation."""
...
def overloaded_func(a):
"""Foo bar documentation."""
return str(a)
expect('overloaded_func',
"D418: Function/ Method decorated with @overload"
" shouldn't contain a docstring")
@expect('D200: One-line docstring should fit on one line with quotes '
'(found 3)')
@expect('D212: Multi-line docstring summary should start at the first line')
def asdlkfasd():
"""
Wrong.
"""
@expect('D201: No blank lines allowed before function docstring (found 1)')
def leading_space():
"""Leading space."""
@expect('D202: No blank lines allowed after function docstring (found 1)')
def trailing_space():
"""Leading space."""
pass
@expect('D201: No blank lines allowed before function docstring (found 1)')
@expect('D202: No blank lines allowed after function docstring (found 1)')
def trailing_and_leading_space():
"""Trailing and leading space."""
pass
expect('LeadingSpaceMissing',
'D203: 1 blank line required before class docstring (found 0)')
class LeadingSpaceMissing:
"""Leading space missing."""
expect('WithLeadingSpace',
'D211: No blank lines allowed before class docstring (found 1)')
class WithLeadingSpace:
"""With leading space."""
expect('TrailingSpace',
'D204: 1 blank line required after class docstring (found 0)')
expect('TrailingSpace',
'D211: No blank lines allowed before class docstring (found 1)')
class TrailingSpace:
"""TrailingSpace."""
pass
expect('LeadingAndTrailingSpaceMissing',
'D203: 1 blank line required before class docstring (found 0)')
expect('LeadingAndTrailingSpaceMissing',
'D204: 1 blank line required after class docstring (found 0)')
class LeadingAndTrailingSpaceMissing:
"""Leading and trailing space missing."""
pass
@expect('D205: 1 blank line required between summary line and description '
'(found 0)')
@expect('D213: Multi-line docstring summary should start at the second line')
def multi_line_zero_separating_blanks():
"""Summary.
Description.
"""
@expect('D205: 1 blank line required between summary line and description '
'(found 2)')
@expect('D213: Multi-line docstring summary should start at the second line')
def multi_line_two_separating_blanks():
"""Summary.
Description.
"""
@expect('D213: Multi-line docstring summary should start at the second line')
def multi_line_one_separating_blanks():
"""Summary.
Description.
"""
@expect('D207: Docstring is under-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdf():
"""Summary.
Description.
"""
@expect('D207: Docstring is under-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdsdfsdffsdf():
"""Summary.
Description.
"""
@expect('D208: Docstring is over-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdsdf24():
"""Summary.
Description.
"""
@expect('D208: Docstring is over-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdsdfsdf24():
"""Summary.
Description.
"""
@expect('D208: Docstring is over-indented')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfsdfsdsdsdfsdf24():
"""Summary.
Description.
"""
@expect('D209: Multi-line docstring closing quotes should be on a separate '
'line')
@expect('D213: Multi-line docstring summary should start at the second line')
def asdfljdf24():
"""Summary.
Description."""
@expect('D210: No whitespaces allowed surrounding docstring text')
def endswith():
"""Whitespace at the end. """
@expect('D210: No whitespaces allowed surrounding docstring text')
def around():
""" Whitespace at everywhere. """
@expect('D210: No whitespaces allowed surrounding docstring text')
@expect('D213: Multi-line docstring summary should start at the second line')
def multiline():
""" Whitespace at the beginning.
This is the end.
"""
@expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
def triple_single_quotes_raw():
r'''Summary.'''
@expect('D300: Use """triple double quotes""" (found \'\'\'-quotes)')
def triple_single_quotes_raw_uppercase():
R'''Summary.'''
@expect('D300: Use """triple double quotes""" (found \'-quotes)')
def single_quotes_raw():
r'Summary.'
@expect('D300: Use """triple double quotes""" (found \'-quotes)')
def single_quotes_raw_uppercase():
R'Summary.'
@expect('D300: Use """triple double quotes""" (found \'-quotes)')
@expect('D301: Use r""" if any backslashes in a docstring')
def single_quotes_raw_uppercase_backslash():
R'Sum\mary.'
@expect('D301: Use r""" if any backslashes in a docstring')
def double_quotes_backslash():
"""Sum\\mary."""
@expect('D301: Use r""" if any backslashes in a docstring')
def double_quotes_backslash_uppercase():
R"""Sum\\mary."""
@expect('D213: Multi-line docstring summary should start at the second line')
def exceptions_of_D301():
"""Exclude some backslashes from D301.
In particular, line continuations \
and unicode literals \u0394 and \N{GREEK CAPITAL LETTER DELTA}.
They are considered to be intentionally unescaped.
"""
@expect("D400: First line should end with a period (not 'y')")
@expect("D415: First line should end with a period, question mark, "
"or exclamation point (not 'y')")
def lwnlkjl():
"""Summary"""
@expect("D401: First line should be in imperative mood "
"(perhaps 'Return', not 'Returns')")
def liouiwnlkjl():
"""Returns foo."""
@expect("D401: First line should be in imperative mood; try rephrasing "
"(found 'Constructor')")
def sdgfsdg23245():
"""Constructor for a foo."""
@expect("D401: First line should be in imperative mood; try rephrasing "
"(found 'Constructor')")
def sdgfsdg23245777():
"""Constructor."""
@expect('D402: First line should not be the function\'s "signature"')
def foobar():
"""Signature: foobar()."""
@expect('D213: Multi-line docstring summary should start at the second line')
def new_209():
"""First line.
More lines.
"""
pass
@expect('D213: Multi-line docstring summary should start at the second line')
def old_209():
"""One liner.
Multi-line comments. OK to have extra blank line
"""
@expect("D103: Missing docstring in public function")
def oneliner_d102(): return
@expect("D400: First line should end with a period (not 'r')")
@expect("D415: First line should end with a period, question mark,"
" or exclamation point (not 'r')")
def oneliner_withdoc(): """One liner"""
def ignored_decorator(func): # noqa: D400,D401,D415
"""Runs something"""
func()
pass
def decorator_for_test(func): # noqa: D400,D401,D415
"""Runs something"""
func()
pass
@ignored_decorator
def oneliner_ignored_decorator(): """One liner"""
@decorator_for_test
@expect("D400: First line should end with a period (not 'r')")
@expect("D415: First line should end with a period, question mark,"
" or exclamation point (not 'r')")
def oneliner_with_decorator_expecting_errors(): """One liner"""
@decorator_for_test
def valid_oneliner_with_decorator(): """One liner."""
@expect("D207: Docstring is under-indented")
@expect('D213: Multi-line docstring summary should start at the second line')
def docstring_start_in_same_line(): """First Line.
Second Line
"""
def function_with_lambda_arg(x=lambda y: y):
"""Wrap the given lambda."""
@expect('D213: Multi-line docstring summary should start at the second line')
def a_following_valid_function(x=None):
"""Check for a bug where the previous function caused an assertion.
The assertion was caused in the next function, so this one is necessary.
"""
def outer_function():
"""Do something."""
def inner_function():
"""Do inner something."""
return 0
@expect("D400: First line should end with a period (not 'g')")
@expect("D401: First line should be in imperative mood "
"(perhaps 'Run', not 'Runs')")
@expect("D415: First line should end with a period, question mark, "
"or exclamation point (not 'g')")
def docstring_bad():
"""Runs something"""
pass
def docstring_bad_ignore_all(): # noqa
"""Runs something"""
pass
def docstring_bad_ignore_one(): # noqa: D400,D401,D415
"""Runs something"""
pass
@expect("D401: First line should be in imperative mood "
"(perhaps 'Run', not 'Runs')")
def docstring_ignore_some_violations_but_catch_D401(): # noqa: E501,D400,D415
"""Runs something"""
pass
@expect(
"D401: First line should be in imperative mood "
"(perhaps 'Initiate', not 'Initiates')"
)
def docstring_initiates():
"""Initiates the process."""
@expect(
"D401: First line should be in imperative mood "
"(perhaps 'Initialize', not 'Initializes')"
)
def docstring_initializes():
"""Initializes the process."""
@wraps(docstring_bad_ignore_one)
def bad_decorated_function():
"""Bad (E501) but decorated"""
pass
def valid_google_string(): # noqa: D400
"""Test a valid something!"""
@expect("D415: First line should end with a period, question mark, "
"or exclamation point (not 'g')")
def bad_google_string(): # noqa: D400
"""Test a valid something"""
# This is reproducing a bug where AttributeError is raised when parsing class
# parameters as functions for Google / Numpy conventions.
class Blah: # noqa: D203,D213
"""A Blah.
Parameters
----------
x : int
"""
def __init__(self, x):
pass
expect(os.path.normcase(__file__ if __file__[-1] != 'c' else __file__[:-1]),
'D100: Missing docstring in public module')

View File

@@ -1,4 +1,3 @@
from __future__ import print_function
"""Docstring"""
from __future__ import absolute_import

View File

@@ -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

View File

@@ -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.

View File

@@ -1,4 +1,4 @@
pub mod checks;
pub mod checkers;
pub mod helpers;
pub mod operations;
pub mod relocate;

View File

@@ -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,
};
@@ -16,7 +17,7 @@ use crate::checks::{Check, CheckKind, RejectedCmpop};
use crate::python::builtins::BUILTINS;
/// Check IfTuple compliance.
pub fn check_if_tuple(test: &Expr, location: Range) -> Option<Check> {
pub fn if_tuple(test: &Expr, location: Range) -> Option<Check> {
if let ExprKind::Tuple { elts, .. } = &test.node {
if !elts.is_empty() {
return Some(Check::new(CheckKind::IfTuple, location));
@@ -26,7 +27,7 @@ pub fn check_if_tuple(test: &Expr, location: Range) -> Option<Check> {
}
/// Check AssertTuple compliance.
pub fn check_assert_tuple(test: &Expr, location: Range) -> Option<Check> {
pub fn assert_tuple(test: &Expr, location: Range) -> Option<Check> {
if let ExprKind::Tuple { elts, .. } = &test.node {
if !elts.is_empty() {
return Some(Check::new(CheckKind::AssertTuple, location));
@@ -36,7 +37,7 @@ pub fn check_assert_tuple(test: &Expr, location: Range) -> Option<Check> {
}
/// Check NotInTest and NotIsTest compliance.
pub fn check_not_tests(
pub fn not_tests(
op: &Unaryop,
operand: &Expr,
check_not_in: bool,
@@ -75,7 +76,7 @@ pub fn check_not_tests(
}
/// Check UnusedVariable compliance.
pub fn check_unused_variables(
pub fn unused_variables(
scope: &Scope,
locator: &dyn CheckLocator,
dummy_variable_rgx: &Regex,
@@ -108,7 +109,7 @@ pub fn check_unused_variables(
}
/// Check DoNotAssignLambda compliance.
pub fn check_do_not_assign_lambda(value: &Expr, location: Range) -> Option<Check> {
pub fn do_not_assign_lambda(value: &Expr, location: Range) -> Option<Check> {
if let ExprKind::Lambda { .. } = &value.node {
Some(Check::new(CheckKind::DoNotAssignLambda, location))
} else {
@@ -117,11 +118,7 @@ pub fn check_do_not_assign_lambda(value: &Expr, location: Range) -> Option<Check
}
/// Check UselessMetaclassType compliance.
pub fn check_useless_metaclass_type(
targets: &Vec<Expr>,
value: &Expr,
location: Range,
) -> Option<Check> {
pub fn useless_metaclass_type(targets: &Vec<Expr>, value: &Expr, location: Range) -> Option<Check> {
if targets.len() == 1 {
if let ExprKind::Name { id, .. } = targets.first().map(|expr| &expr.node).unwrap() {
if id == "__metaclass__" {
@@ -137,7 +134,7 @@ pub fn check_useless_metaclass_type(
}
/// Check UnnecessaryAbspath compliance.
pub fn check_unnecessary_abspath(func: &Expr, args: &Vec<Expr>, location: Range) -> Option<Check> {
pub fn unnecessary_abspath(func: &Expr, args: &Vec<Expr>, location: Range) -> Option<Check> {
// Validate the arguments.
if args.len() == 1 {
if let ExprKind::Name { id, .. } = &args[0].node {
@@ -193,7 +190,7 @@ impl Primitive {
}
/// Check TypeOfPrimitive compliance.
pub fn check_type_of_primitive(func: &Expr, args: &Vec<Expr>, location: Range) -> Option<Check> {
pub fn type_of_primitive(func: &Expr, args: &Vec<Expr>, location: Range) -> Option<Check> {
// Validate the arguments.
if args.len() == 1 {
match &func.node {
@@ -221,7 +218,7 @@ fn is_ambiguous_name(name: &str) -> bool {
}
/// Check AmbiguousVariableName compliance.
pub fn check_ambiguous_variable_name(name: &str, location: Range) -> Option<Check> {
pub fn ambiguous_variable_name(name: &str, location: Range) -> Option<Check> {
if is_ambiguous_name(name) {
Some(Check::new(
CheckKind::AmbiguousVariableName(name.to_string()),
@@ -233,7 +230,7 @@ pub fn check_ambiguous_variable_name(name: &str, location: Range) -> Option<Chec
}
/// Check AmbiguousClassName compliance.
pub fn check_ambiguous_class_name(name: &str, location: Range) -> Option<Check> {
pub fn ambiguous_class_name(name: &str, location: Range) -> Option<Check> {
if is_ambiguous_name(name) {
Some(Check::new(
CheckKind::AmbiguousClassName(name.to_string()),
@@ -245,7 +242,7 @@ pub fn check_ambiguous_class_name(name: &str, location: Range) -> Option<Check>
}
/// Check AmbiguousFunctionName compliance.
pub fn check_ambiguous_function_name(name: &str, location: Range) -> Option<Check> {
pub fn ambiguous_function_name(name: &str, location: Range) -> Option<Check> {
if is_ambiguous_name(name) {
Some(Check::new(
CheckKind::AmbiguousFunctionName(name.to_string()),
@@ -257,11 +254,7 @@ pub fn check_ambiguous_function_name(name: &str, location: Range) -> Option<Chec
}
/// Check UselessObjectInheritance compliance.
pub fn check_useless_object_inheritance(
name: &str,
bases: &[Expr],
scope: &Scope,
) -> Option<Check> {
pub fn useless_object_inheritance(name: &str, bases: &[Expr], scope: &Scope) -> Option<Check> {
for expr in bases {
if let ExprKind::Name { id, .. } = &expr.node {
if id == "object" {
@@ -286,7 +279,7 @@ pub fn check_useless_object_inheritance(
}
/// Check DefaultExceptNotLast compliance.
pub fn check_default_except_not_last(handlers: &Vec<Excepthandler>) -> Option<Check> {
pub fn default_except_not_last(handlers: &Vec<Excepthandler>) -> Option<Check> {
for (idx, handler) in handlers.iter().enumerate() {
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
if type_.is_none() && idx < handlers.len() - 1 {
@@ -301,7 +294,7 @@ pub fn check_default_except_not_last(handlers: &Vec<Excepthandler>) -> Option<Ch
}
/// Check RaiseNotImplemented compliance.
pub fn check_raise_not_implemented(expr: &Expr) -> Option<Check> {
pub fn raise_not_implemented(expr: &Expr) -> Option<Check> {
match &expr.node {
ExprKind::Call { func, .. } => {
if let ExprKind::Name { id, .. } = &func.node {
@@ -328,7 +321,7 @@ pub fn check_raise_not_implemented(expr: &Expr) -> Option<Check> {
}
/// Check DuplicateArgumentName compliance.
pub fn check_duplicate_arguments(arguments: &Arguments) -> Vec<Check> {
pub fn duplicate_arguments(arguments: &Arguments) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
// Collect all the arguments into a single vector.
@@ -376,7 +369,7 @@ fn convert_to_value(expr: &Expr) -> Option<DictionaryKey> {
}
/// Check MultiValueRepeatedKeyLiteral and MultiValueRepeatedKeyVariable compliance.
pub fn check_repeated_keys(
pub fn repeated_keys(
keys: &Vec<Expr>,
check_repeated_literals: bool,
check_repeated_variables: bool,
@@ -416,7 +409,7 @@ pub fn check_repeated_keys(
}
/// Check TrueFalseComparison and NoneComparison compliance.
pub fn check_literal_comparisons(
pub fn literal_comparisons(
left: &Expr,
ops: &Vec<Cmpop>,
comparators: &Vec<Expr>,
@@ -547,7 +540,7 @@ fn is_constant_non_singleton(expr: &Expr) -> bool {
}
/// Check IsLiteral compliance.
pub fn check_is_literal(
pub fn is_literal(
left: &Expr,
ops: &Vec<Cmpop>,
comparators: &Vec<Expr>,
@@ -569,11 +562,7 @@ pub fn check_is_literal(
}
/// Check TypeComparison compliance.
pub fn check_type_comparison(
ops: &Vec<Cmpop>,
comparators: &Vec<Expr>,
location: Range,
) -> Vec<Check> {
pub fn type_comparison(ops: &Vec<Cmpop>, comparators: &Vec<Expr>, location: Range) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
for (op, right) in izip!(ops, comparators) {
@@ -609,7 +598,7 @@ pub fn check_type_comparison(
}
/// Check TwoStarredExpressions and TooManyExpressionsInStarredAssignment compliance.
pub fn check_starred_expressions(
pub fn starred_expressions(
elts: &[Expr],
check_too_many_expressions: bool,
check_two_starred_expressions: bool,
@@ -639,7 +628,7 @@ pub fn check_starred_expressions(
}
/// Check BreakOutsideLoop compliance.
pub fn check_break_outside_loop(
pub fn break_outside_loop(
stmt: &Stmt,
parents: &[&Stmt],
parent_stack: &[usize],
@@ -680,7 +669,7 @@ pub fn check_break_outside_loop(
}
/// Check ContinueOutsideLoop compliance.
pub fn check_continue_outside_loop(
pub fn continue_outside_loop(
stmt: &Stmt,
parents: &[&Stmt],
parent_stack: &[usize],
@@ -728,11 +717,7 @@ pub enum ShadowingType {
}
/// Check builtin name shadowing
pub fn check_builtin_shadowing(
name: &str,
location: Range,
node_type: ShadowingType,
) -> Option<Check> {
pub fn builtin_shadowing(name: &str, location: Range, node_type: ShadowingType) -> Option<Check> {
if BUILTINS.contains(&name) {
Some(Check::new(
match node_type {
@@ -747,17 +732,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 {
@@ -969,6 +944,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 {
@@ -1009,14 +1044,14 @@ pub fn unnecessary_subscript_reversal(expr: &Expr, func: &Expr, args: &[Expr]) -
// flake8-super
/// Check that `super()` has no args
pub fn check_super_args(
pub fn super_args(
scope: &Scope,
parents: &[&Stmt],
expr: &Expr,
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;
}
@@ -1075,7 +1110,7 @@ pub fn check_super_args(
// flake8-print
/// Check whether a function call is a `print` or `pprint` invocation
pub fn check_print_call(
pub fn print_call(
expr: &Expr,
func: &Expr,
check_print: bool,

View File

@@ -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
}
}

View File

@@ -1,10 +1,11 @@
use crate::ast::helpers::match_name_or_attr;
use rustpython_parser::ast::{
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, MatchCase, Operator, Pattern,
PatternKind, Stmt, StmtKind, Unaryop, Withitem,
};
use crate::ast::helpers::match_name_or_attr;
pub trait Visitor<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
walk_stmt(self, stmt);

View File

@@ -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::{
@@ -18,13 +18,14 @@ use crate::ast::types::{
ScopeKind,
};
use crate::ast::visitor::{walk_excepthandler, Visitor};
use crate::ast::{checks, helpers, operations, visitor};
use crate::ast::{checkers, helpers, operations, visitor};
use crate::autofix::{fixer, fixes};
use crate::checks::{Check, CheckCode, CheckKind};
use crate::plugins;
use crate::docstrings::{Docstring, DocstringKind};
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::python::future::ALL_FEATURE_NAMES;
use crate::settings::{PythonVersion, Settings};
use crate::{docstrings, plugins};
pub const GLOBAL_SCOPE_INDEX: usize = 0;
@@ -37,6 +38,8 @@ pub struct Checker<'a> {
pub(crate) autofix: &'a fixer::Mode,
// Computed checks.
checks: Vec<Check>,
// Docstring tracking.
docstrings: Vec<Docstring<'a>>,
// Edit tracking.
// TODO(charlie): Instead of exposing deletions, wrap in a public API.
pub(crate) deletions: BTreeSet<usize>,
@@ -53,13 +56,14 @@ pub struct Checker<'a> {
deferred_lambdas: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
deferred_assignments: Vec<usize>,
// Internal, derivative state.
pub(crate) initial: bool,
in_f_string: Option<Range>,
in_annotation: bool,
in_literal: bool,
seen_non_import: bool,
seen_docstring: bool,
seen_import_boundary: bool,
futures_allowed: bool,
annotations_future_enabled: bool,
except_handlers: Vec<Vec<String>>,
}
impl<'a> Checker<'a> {
@@ -74,25 +78,27 @@ 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(),
docstrings: 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(),
initial: true,
in_f_string: None,
in_annotation: Default::default(),
in_literal: Default::default(),
seen_import_boundary: Default::default(),
futures_allowed: true,
annotations_future_enabled: Default::default(),
except_handlers: Default::default(),
}
}
}
@@ -120,47 +126,40 @@ where
self.futures_allowed = false;
}
StmtKind::Expr { value } => {
if self.seen_docstring
&& !self.seen_non_import
&& !operations::in_nested_block(&self.parent_stack, &self.parents)
{
self.seen_non_import = true;
// Track all docstrings: module-, class-, and function-level.
let mut is_module_docstring = false;
if matches!(
&value.node,
ExprKind::Constant {
value: Constant::Str(_),
..
}
) {
if let Some(docstring) = docstrings::extract(self, stmt, value) {
if matches!(&docstring.kind, DocstringKind::Module) {
is_module_docstring = true;
}
self.docstrings.push(docstring);
}
}
if !self.seen_docstring
&& !operations::in_nested_block(&self.parent_stack, &self.parents)
&& matches!(
&value.node,
ExprKind::Constant {
value: Constant::Str(_),
..
},
)
{
self.seen_docstring = true;
}
// Allow docstrings to interrupt __future__ imports.
if self.futures_allowed
&& !matches!(
&value.node,
ExprKind::Constant {
value: Constant::Str(_),
..
},
)
{
if !is_module_docstring {
if !self.seen_import_boundary
&& !operations::in_nested_block(&self.parent_stack, &self.parents)
{
self.seen_import_boundary = true;
}
self.futures_allowed = false;
}
}
node => {
self.futures_allowed = false;
if !self.seen_non_import
if !self.seen_import_boundary
&& !helpers::is_assignment_to_a_dunder(node)
&& !operations::in_nested_block(&self.parent_stack, &self.parents)
{
self.seen_non_import = true;
self.seen_import_boundary = true;
}
}
}
@@ -190,27 +189,24 @@ where
if self.settings.enabled.contains(&CheckCode::E741) {
let location = self.locate_check(Range::from_located(stmt));
self.checks.extend(
names.iter().filter_map(|name| {
checks::check_ambiguous_variable_name(name, location)
}),
names
.iter()
.filter_map(|name| checkers::ambiguous_variable_name(name, location)),
);
}
}
StmtKind::Break => {
if self.settings.enabled.contains(&CheckCode::F701) {
if let Some(check) = checks::check_break_outside_loop(
stmt,
&self.parents,
&self.parent_stack,
self,
) {
if let Some(check) =
checkers::break_outside_loop(stmt, &self.parents, &self.parent_stack, self)
{
self.checks.push(check);
}
}
}
StmtKind::Continue => {
if self.settings.enabled.contains(&CheckCode::F702) {
if let Some(check) = checks::check_continue_outside_loop(
if let Some(check) = checkers::continue_outside_loop(
stmt,
&self.parents,
&self.parent_stack,
@@ -235,7 +231,7 @@ where
..
} => {
if self.settings.enabled.contains(&CheckCode::E743) {
if let Some(check) = checks::check_ambiguous_function_name(
if let Some(check) = checkers::ambiguous_function_name(
name,
self.locate_check(Range::from_located(stmt)),
) {
@@ -323,7 +319,7 @@ where
}
if self.settings.enabled.contains(&CheckCode::E742) {
if let Some(check) = checks::check_ambiguous_class_name(
if let Some(check) = checkers::ambiguous_class_name(
name,
self.locate_check(Range::from_located(stmt)),
) {
@@ -353,7 +349,7 @@ where
.settings
.enabled
.contains(CheckKind::ModuleImportNotAtTopOfFile.code())
&& self.seen_non_import
&& self.seen_import_boundary
&& stmt.location.column() == 1
{
self.checks.push(Check::new(
@@ -414,7 +410,7 @@ where
.settings
.enabled
.contains(CheckKind::ModuleImportNotAtTopOfFile.code())
&& self.seen_non_import
&& self.seen_import_boundary
&& stmt.location.column() == 1
{
self.checks.push(Check::new(
@@ -532,7 +528,7 @@ where
StmtKind::Raise { exc, .. } => {
if self.settings.enabled.contains(&CheckCode::F901) {
if let Some(expr) = exc {
if let Some(check) = checks::check_raise_not_implemented(expr) {
if let Some(check) = checkers::raise_not_implemented(expr) {
self.checks.push(check);
}
}
@@ -546,21 +542,29 @@ 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) {
if let Some(check) = checks::check_default_except_not_last(handlers) {
if let Some(check) = checkers::default_except_not_last(handlers) {
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) {
if let Some(check) = checks::check_do_not_assign_lambda(
if let Some(check) = checkers::do_not_assign_lambda(
value,
self.locate_check(Range::from_located(stmt)),
) {
@@ -574,7 +578,7 @@ where
StmtKind::AnnAssign { value, .. } => {
if self.settings.enabled.contains(&CheckCode::E731) {
if let Some(value) = value {
if let Some(check) = checks::check_do_not_assign_lambda(
if let Some(check) = checkers::do_not_assign_lambda(
value,
self.locate_check(Range::from_located(stmt)),
) {
@@ -586,6 +590,7 @@ where
StmtKind::Delete { .. } => {}
_ => {}
}
self.initial = false;
// Recurse.
match &stmt.node {
@@ -601,6 +606,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),
};
@@ -670,7 +696,7 @@ where
self.settings.enabled.contains(&CheckCode::F621);
let check_two_starred_expressions =
self.settings.enabled.contains(&CheckCode::F622);
if let Some(check) = checks::check_starred_expressions(
if let Some(check) = checkers::starred_expressions(
elts,
check_too_many_expressions,
check_two_starred_expressions,
@@ -693,7 +719,7 @@ where
}
ExprContext::Store => {
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) = checks::check_ambiguous_variable_name(
if let Some(check) = checkers::ambiguous_variable_name(
id,
self.locate_check(Range::from_located(expr)),
) {
@@ -743,26 +769,26 @@ where
// flake8-comprehensions
if self.settings.enabled.contains(&CheckCode::C400) {
if let Some(check) = checks::unnecessary_generator_list(expr, func, args) {
if let Some(check) = checkers::unnecessary_generator_list(expr, func, args) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C401) {
if let Some(check) = checks::unnecessary_generator_set(expr, func, args) {
if let Some(check) = checkers::unnecessary_generator_set(expr, func, args) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C402) {
if let Some(check) = checks::unnecessary_generator_dict(expr, func, args) {
if let Some(check) = checkers::unnecessary_generator_dict(expr, func, args) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C403) {
if let Some(check) =
checks::unnecessary_list_comprehension_set(expr, func, args)
checkers::unnecessary_list_comprehension_set(expr, func, args)
{
self.checks.push(check);
};
@@ -770,34 +796,50 @@ where
if self.settings.enabled.contains(&CheckCode::C404) {
if let Some(check) =
checks::unnecessary_list_comprehension_dict(expr, func, args)
checkers::unnecessary_list_comprehension_dict(expr, func, args)
{
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C405) {
if let Some(check) = checks::unnecessary_literal_set(expr, func, args) {
if let Some(check) = checkers::unnecessary_literal_set(expr, func, args) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C406) {
if let Some(check) = checks::unnecessary_literal_dict(expr, func, args) {
if let Some(check) = checkers::unnecessary_literal_dict(expr, func, args) {
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C408) {
if let Some(check) =
checks::unnecessary_collection_call(expr, func, args, keywords)
checkers::unnecessary_collection_call(expr, func, args, keywords)
{
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C409) {
if let Some(check) =
checkers::unnecessary_literal_within_tuple_call(expr, func, args)
{
self.checks.push(check);
};
}
if self.settings.enabled.contains(&CheckCode::C410) {
if let Some(check) =
checkers::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) {
if let Some(check) = checkers::unnecessary_subscript_reversal(expr, func, args)
{
self.checks.push(check);
};
}
@@ -832,7 +874,7 @@ where
let check_repeated_literals = self.settings.enabled.contains(&CheckCode::F601);
let check_repeated_variables = self.settings.enabled.contains(&CheckCode::F602);
if check_repeated_literals || check_repeated_variables {
self.checks.extend(checks::check_repeated_keys(
self.checks.extend(checkers::repeated_keys(
keys,
check_repeated_literals,
check_repeated_variables,
@@ -884,7 +926,7 @@ where
let check_not_in = self.settings.enabled.contains(&CheckCode::E713);
let check_not_is = self.settings.enabled.contains(&CheckCode::E714);
if check_not_in || check_not_is {
self.checks.extend(checks::check_not_tests(
self.checks.extend(checkers::not_tests(
op,
operand,
check_not_in,
@@ -901,7 +943,7 @@ where
let check_none_comparisons = self.settings.enabled.contains(&CheckCode::E711);
let check_true_false_comparisons = self.settings.enabled.contains(&CheckCode::E712);
if check_none_comparisons || check_true_false_comparisons {
self.checks.extend(checks::check_literal_comparisons(
self.checks.extend(checkers::literal_comparisons(
left,
ops,
comparators,
@@ -912,7 +954,7 @@ where
}
if self.settings.enabled.contains(&CheckCode::F632) {
self.checks.extend(checks::check_is_literal(
self.checks.extend(checkers::is_literal(
left,
ops,
comparators,
@@ -921,7 +963,7 @@ where
}
if self.settings.enabled.contains(&CheckCode::E721) {
self.checks.extend(checks::check_type_comparison(
self.checks.extend(checkers::type_comparison(
ops,
comparators,
self.locate_check(Range::from_located(expr)),
@@ -1148,7 +1190,7 @@ where
match name {
Some(name) => {
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) = checks::check_ambiguous_variable_name(
if let Some(check) = checkers::ambiguous_variable_name(
name,
self.locate_check(Range::from_located(excepthandler)),
) {
@@ -1216,8 +1258,7 @@ where
fn visit_arguments(&mut self, arguments: &'b Arguments) {
if self.settings.enabled.contains(&CheckCode::F831) {
self.checks
.extend(checks::check_duplicate_arguments(arguments));
self.checks.extend(checkers::duplicate_arguments(arguments));
}
// Bind, but intentionally avoid walking default expressions, as we handle them upstream.
@@ -1250,7 +1291,7 @@ where
);
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) = checks::check_ambiguous_variable_name(
if let Some(check) = checkers::ambiguous_variable_name(
&arg.node.arg,
self.locate_check(Range::from_located(arg)),
) {
@@ -1471,6 +1512,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)),
@@ -1690,7 +1739,7 @@ impl<'a> Checker<'a> {
fn check_deferred_assignments(&mut self) {
if self.settings.enabled.contains(&CheckCode::F841) {
while let Some(index) = self.deferred_assignments.pop() {
self.checks.extend(checks::check_unused_variables(
self.checks.extend(checkers::unused_variables(
&self.scopes[index],
self,
&self.settings.dummy_variable_rgx,
@@ -1839,25 +1888,48 @@ impl<'a> Checker<'a> {
}
}
fn check_docstrings(&mut self) {
while let Some(docstring) = self.docstrings.pop() {
if self.settings.enabled.contains(&CheckCode::D200) {
docstrings::one_liner(self, &docstring);
}
if self.settings.enabled.contains(&CheckCode::D205) {
docstrings::blank_after_summary(self, &docstring);
}
if self.settings.enabled.contains(&CheckCode::D209) {
docstrings::newline_after_last_paragraph(self, &docstring);
}
if self.settings.enabled.contains(&CheckCode::D210) {
docstrings::no_surrounding_whitespace(self, &docstring);
}
if self.settings.enabled.contains(&CheckCode::D400) {
docstrings::ends_with_period(self, &docstring);
}
if self.settings.enabled.contains(&CheckCode::D419) {
docstrings::not_empty(self, &docstring);
}
}
}
fn check_builtin_shadowing(&mut self, name: &str, location: Range, is_attribute: bool) {
let scope = self.current_scope();
// flake8-builtins
if is_attribute && matches!(scope.kind, ScopeKind::Class) {
if self.settings.enabled.contains(&CheckCode::A003) {
if let Some(check) = checks::check_builtin_shadowing(
if let Some(check) = checkers::builtin_shadowing(
name,
self.locate_check(location),
checks::ShadowingType::Attribute,
checkers::ShadowingType::Attribute,
) {
self.checks.push(check);
}
}
} else if self.settings.enabled.contains(&CheckCode::A001) {
if let Some(check) = checks::check_builtin_shadowing(
if let Some(check) = checkers::builtin_shadowing(
name,
self.locate_check(location),
checks::ShadowingType::Variable,
checkers::ShadowingType::Variable,
) {
self.checks.push(check);
}
@@ -1866,10 +1938,10 @@ impl<'a> Checker<'a> {
fn check_builtin_arg_shadowing(&mut self, name: &str, location: Range) {
if self.settings.enabled.contains(&CheckCode::A002) {
if let Some(check) = checks::check_builtin_shadowing(
if let Some(check) = checkers::builtin_shadowing(
name,
self.locate_check(location),
checks::ShadowingType::Argument,
checkers::ShadowingType::Argument,
) {
self.checks.push(check);
}
@@ -1906,5 +1978,8 @@ pub fn check_ast(
checker.pop_scope();
checker.check_dead_scopes();
// Check docstrings.
checker.check_docstrings();
checker.checks
}

View File

@@ -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),

View File

@@ -1,9 +1,11 @@
use std::str::FromStr;
use itertools::Itertools;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, EnumIter, EnumString};
use crate::ast::checks::Primitive;
use crate::ast::checkers::Primitive;
use crate::ast::types::Range;
pub const DEFAULT_CHECK_CODES: [CheckCode; 43] = [
@@ -120,6 +122,10 @@ pub enum CheckCode {
A001,
A002,
A003,
// flake8-bugbear
B011,
B014,
B025,
// flake8-comprehensions
C400,
C401,
@@ -129,6 +135,8 @@ pub enum CheckCode {
C405,
C406,
C408,
C409,
C410,
C415,
// flake8-print
T201,
@@ -142,6 +150,13 @@ pub enum CheckCode {
U006,
U007,
U008,
// pydocstyle
D200,
D205,
D209,
D210,
D400,
D419,
// Meta
M001,
}
@@ -210,7 +225,11 @@ pub enum CheckKind {
BuiltinVariableShadowing(String),
BuiltinArgumentShadowing(String),
BuiltinAttributeShadowing(String),
// flakes8-comprehensions
// flake8-bugbear
DoNotAssertFalse,
DuplicateHandlerException(Vec<String>),
DuplicateTryBlockException(String),
// flake8-comprehensions
UnnecessaryGeneratorList,
UnnecessaryGeneratorSet,
UnnecessaryGeneratorDict,
@@ -219,6 +238,8 @@ pub enum CheckKind {
UnnecessaryLiteralSet(String),
UnnecessaryLiteralDict(String),
UnnecessaryCollectionCall(String),
UnnecessaryLiteralWithinTupleCall(String),
UnnecessaryLiteralWithinListCall(String),
UnnecessarySubscriptReversal(String),
// flake8-print
PrintFound,
@@ -232,8 +253,15 @@ pub enum CheckKind {
UsePEP585Annotation(String),
UsePEP604Annotation,
SuperCallWithParameters,
// pydocstyle
OneLinerDocstring,
BlankLineAfterSummary,
NewLineAfterLastParagraph,
NoSurroundingWhitespace,
EmptyDocstring,
DocstringEndsInNonPeriod,
// Meta
UnusedNOQA(Option<String>),
UnusedNOQA(Option<Vec<String>>),
}
impl CheckCode {
@@ -301,6 +329,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(vec!["ValueError".to_string()]),
CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
// flake8-comprehensions
CheckCode::C400 => CheckKind::UnnecessaryGeneratorList,
CheckCode::C401 => CheckKind::UnnecessaryGeneratorSet,
@@ -312,6 +344,12 @@ 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())
}
@@ -330,6 +368,13 @@ impl CheckCode {
CheckCode::U006 => CheckKind::UsePEP585Annotation("List".to_string()),
CheckCode::U007 => CheckKind::UsePEP604Annotation,
CheckCode::U008 => CheckKind::SuperCallWithParameters,
// pydocstyle
CheckCode::D200 => CheckKind::OneLinerDocstring,
CheckCode::D205 => CheckKind::BlankLineAfterSummary,
CheckCode::D209 => CheckKind::NewLineAfterLastParagraph,
CheckCode::D210 => CheckKind::NoSurroundingWhitespace,
CheckCode::D400 => CheckKind::DocstringEndsInNonPeriod,
CheckCode::D419 => CheckKind::EmptyDocstring,
// Meta
CheckCode::M001 => CheckKind::UnusedNOQA(None),
}
@@ -389,6 +434,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,6 +447,8 @@ 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-print
CheckKind::PrintFound => &CheckCode::T201,
@@ -411,6 +462,13 @@ impl CheckKind {
CheckKind::UsePEP604Annotation => &CheckCode::U007,
CheckKind::UselessObjectInheritance(_) => &CheckCode::U004,
CheckKind::SuperCallWithParameters => &CheckCode::U008,
// pydocstyle
CheckKind::OneLinerDocstring => &CheckCode::D200,
CheckKind::BlankLineAfterSummary => &CheckCode::D205,
CheckKind::NewLineAfterLastParagraph => &CheckCode::D209,
CheckKind::NoSurroundingWhitespace => &CheckCode::D210,
CheckKind::DocstringEndsInNonPeriod => &CheckCode::D400,
CheckKind::EmptyDocstring => &CheckCode::D419,
// Meta
CheckKind::UnusedNOQA(_) => &CheckCode::M001,
}
@@ -559,6 +617,23 @@ 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(names) => {
if names.len() == 1 {
let name = &names[0];
format!("Exception handler with duplicate exception: `{name}")
} else {
let names = names.iter().map(|name| format!("`{name}`")).join(", ");
format!("Exception handler with duplicate exceptions: {names}")
}
}
CheckKind::DuplicateTryBlockException(name) => {
format!("try-except block with duplicate exception `{name}`")
}
// flake8-comprehensions
CheckKind::UnnecessaryGeneratorList => {
"Unnecessary generator - rewrite as a list comprehension".to_string()
@@ -584,6 +659,28 @@ 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}()")
}
@@ -615,10 +712,37 @@ impl CheckKind {
CheckKind::SuperCallWithParameters => {
"Use `super()` instead of `super(__class__, self)`".to_string()
}
// pydocstyle
CheckKind::OneLinerDocstring => "One-line docstring should fit on one line".to_string(),
CheckKind::BlankLineAfterSummary => {
"1 blank line required between summary line and description".to_string()
}
CheckKind::NewLineAfterLastParagraph => {
"Multi-line docstring closing quotes should be on a separate line".to_string()
}
CheckKind::NoSurroundingWhitespace => {
"No whitespaces allowed surrounding docstring text".to_string()
}
CheckKind::DocstringEndsInNonPeriod => {
"First line should end with a period".to_string()
}
CheckKind::EmptyDocstring => "Docstring is empty".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}")
}
},
}
}
@@ -628,6 +752,8 @@ impl CheckKind {
matches!(
self,
CheckKind::DeprecatedUnittestAlias(_, _)
| CheckKind::DoNotAssertFalse
| CheckKind::DuplicateHandlerException(_)
| CheckKind::PPrintFound
| CheckKind::PrintFound
| CheckKind::SuperCallWithParameters

View File

@@ -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()?;

198
src/docstrings.rs Normal file
View File

@@ -0,0 +1,198 @@
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
use rustpython_ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
#[derive(Debug)]
pub enum DocstringKind {
Module,
Function,
Class,
}
#[derive(Debug)]
pub struct Docstring<'a> {
pub kind: DocstringKind,
pub parent: Option<&'a Stmt>,
pub expr: &'a Expr,
}
/// Extract a docstring from an expression.
pub fn extract<'a, 'b>(
checker: &'a Checker,
stmt: &'b Stmt,
expr: &'b Expr,
) -> Option<Docstring<'b>> {
let defined_in = checker
.binding_context()
.defined_in
.map(|index| checker.parents[index]);
match defined_in {
None => {
if checker.initial {
return Some(Docstring {
kind: DocstringKind::Module,
parent: None,
expr,
});
}
}
Some(parent) => {
if let StmtKind::FunctionDef { body, .. }
| StmtKind::AsyncFunctionDef { body, .. }
| StmtKind::ClassDef { body, .. } = &parent.node
{
if body.first().map(|node| node == stmt).unwrap_or_default() {
return Some(Docstring {
kind: if matches!(&parent.node, StmtKind::ClassDef { .. }) {
DocstringKind::Class
} else {
DocstringKind::Function
},
parent: None,
expr,
});
}
}
}
}
None
}
pub fn one_liner(checker: &mut Checker, docstring: &Docstring) {
if let ExprKind::Constant {
value: Constant::Str(string),
..
} = &docstring.expr.node
{
let mut line_count = 0;
let mut non_empty_line_count = 0;
for line in string.lines() {
line_count += 1;
if !line.trim().is_empty() {
non_empty_line_count += 1;
}
if non_empty_line_count > 1 {
return;
}
}
if non_empty_line_count == 1 && line_count > 1 {
checker.add_check(Check::new(
CheckKind::OneLinerDocstring,
Range::from_located(docstring.expr),
));
}
}
}
pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) {
if let ExprKind::Constant {
value: Constant::Str(string),
..
} = &docstring.expr.node
{
let mut lines_count = 1;
let mut blanks_count = 0;
for line in string.trim().lines().skip(1) {
lines_count += 1;
if line.trim().is_empty() {
blanks_count += 1;
} else {
break;
}
}
if lines_count > 1 && blanks_count != 1 {
checker.add_check(Check::new(
CheckKind::BlankLineAfterSummary,
Range::from_located(docstring.expr),
));
}
}
}
pub fn newline_after_last_paragraph(checker: &mut Checker, docstring: &Docstring) {
if let ExprKind::Constant {
value: Constant::Str(string),
..
} = &docstring.expr.node
{
let mut line_count = 0;
for line in string.lines() {
if !line.trim().is_empty() {
line_count += 1;
}
if line_count > 1 {
let content = checker
.locator
.slice_source_code_range(&Range::from_located(docstring.expr));
if let Some(line) = content.lines().last() {
let line = line.trim();
if line != "\"\"\"" && line != "'''" {
checker.add_check(Check::new(
CheckKind::NewLineAfterLastParagraph,
Range::from_located(docstring.expr),
));
}
}
return;
}
}
}
}
pub fn no_surrounding_whitespace(checker: &mut Checker, docstring: &Docstring) {
if let ExprKind::Constant {
value: Constant::Str(string),
..
} = &docstring.expr.node
{
let mut lines = string.lines();
if let Some(line) = lines.next() {
if line.trim().is_empty() {
return;
}
if line.starts_with(' ') || (matches!(lines.next(), None) && line.ends_with(' ')) {
checker.add_check(Check::new(
CheckKind::NoSurroundingWhitespace,
Range::from_located(docstring.expr),
));
}
}
}
}
pub fn not_empty(checker: &mut Checker, docstring: &Docstring) {
if let ExprKind::Constant {
value: Constant::Str(string),
..
} = &docstring.expr.node
{
if string.trim().is_empty() {
checker.add_check(Check::new(
CheckKind::EmptyDocstring,
Range::from_located(docstring.expr),
));
}
}
}
pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) {
if let ExprKind::Constant {
value: Constant::Str(string),
..
} = &docstring.expr.node
{
if let Some(string) = string.lines().next() {
if !string.ends_with('.') {
checker.add_check(Check::new(
CheckKind::DocstringEndsInNonPeriod,
Range::from_located(docstring.expr),
));
}
}
}
}

View File

@@ -17,6 +17,7 @@ mod check_lines;
pub mod checks;
pub mod cli;
pub mod code_gen;
pub mod docstrings;
pub mod fs;
pub mod linter;
pub mod logging;

View File

@@ -762,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(
@@ -846,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(
@@ -942,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(
@@ -955,10 +979,70 @@ mod tests {
}
#[test]
fn u008() -> Result<()> {
fn d200() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/U008.py"),
&settings::Settings::for_rule(CheckCode::U008),
Path::new("./resources/test/fixtures/D.py"),
&settings::Settings::for_rule(CheckCode::D200),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn d205() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/D.py"),
&settings::Settings::for_rule(CheckCode::D205),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn d209() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/D.py"),
&settings::Settings::for_rule(CheckCode::D209),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn d210() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/D.py"),
&settings::Settings::for_rule(CheckCode::D210),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn d400() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/D.py"),
&settings::Settings::for_rule(CheckCode::D400),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn d419() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/D.py"),
&settings::Settings::for_rule(CheckCode::D419),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
@@ -1073,4 +1157,52 @@ mod tests {
insta::assert_yaml_snapshot!(checks);
Ok(())
}
#[test]
fn u008() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/U008.py"),
&settings::Settings::for_rule(CheckCode::U008),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
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(())
}
}

View File

@@ -1,5 +1,7 @@
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_false;
mod assert_tuple;
mod deprecated_unittest_alias;
mod duplicate_exceptions;
mod if_tuple;
mod invalid_print_syntax;
mod print_call;

View 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);
}
}

View File

@@ -1,12 +1,12 @@
use rustpython_ast::{Expr, Stmt};
use crate::ast::checks;
use crate::ast::checkers;
use crate::ast::types::{CheckLocator, Range};
use crate::check_ast::Checker;
pub fn assert_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
if let Some(check) =
checks::check_assert_tuple(test, checker.locate_check(Range::from_located(stmt)))
checkers::assert_tuple(test, checker.locate_check(Range::from_located(stmt)))
{
checker.add_check(check);
}

View File

@@ -0,0 +1,114 @@
use std::collections::BTreeSet;
use itertools::Itertools;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Stmt};
use crate::ast::helpers;
use crate::ast::types::{CheckLocator, Range};
use crate::autofix::fixer;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckCode, CheckKind, Fix};
use crate::code_gen::SourceGenerator;
fn type_pattern(elts: Vec<&Expr>) -> Expr {
Expr::new(
Default::default(),
Default::default(),
ExprKind::Tuple {
elts: elts.into_iter().cloned().collect(),
ctx: ExprContext::Load,
},
)
}
pub fn duplicate_handler_exceptions(
checker: &mut Checker,
expr: &Expr,
elts: &Vec<Expr>,
) -> BTreeSet<String> {
let mut seen: BTreeSet<String> = Default::default();
let mut duplicates: BTreeSet<String> = Default::default();
let mut unique_elts: Vec<&Expr> = 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);
unique_elts.push(type_);
}
}
}
if checker.settings.enabled.contains(&CheckCode::B014) {
// TODO(charlie): Handle "BaseException" and redundant exception aliases.
if !duplicates.is_empty() {
let mut check = Check::new(
CheckKind::DuplicateHandlerException(
duplicates.into_iter().sorted().collect::<Vec<String>>(),
),
checker.locate_check(Range::from_located(expr)),
);
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
// TODO(charlie): If we have a single element, remove the tuple.
let mut generator = SourceGenerator::new();
if let Ok(()) = generator.unparse_expr(&type_pattern(unique_elts), 0) {
if let Ok(content) = generator.generate() {
check.amend(Fix {
content,
location: expr.location,
end_location: expr.end_location,
applied: false,
})
}
}
}
checker.add_check(check);
}
}
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, type_, 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)),
));
}
}
}

View File

@@ -1,13 +1,11 @@
use rustpython_ast::{Expr, Stmt};
use crate::ast::checks;
use crate::ast::checkers;
use crate::ast::types::{CheckLocator, Range};
use crate::check_ast::Checker;
pub fn if_tuple(checker: &mut Checker, stmt: &Stmt, test: &Expr) {
if let Some(check) =
checks::check_if_tuple(test, checker.locate_check(Range::from_located(stmt)))
{
if let Some(check) = checkers::if_tuple(test, checker.locate_check(Range::from_located(stmt))) {
checker.add_check(check);
}
}

View File

@@ -1,13 +1,13 @@
use log::error;
use rustpython_ast::{Expr, Stmt, StmtKind};
use crate::ast::checks;
use crate::ast::checkers;
use crate::autofix::{fixer, fixes};
use crate::check_ast::Checker;
use crate::checks::CheckCode;
pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
if let Some(mut check) = checks::check_print_call(
if let Some(mut check) = checkers::print_call(
expr,
func,
checker.settings.enabled.contains(&CheckCode::T201),

View File

@@ -1,6 +1,6 @@
use rustpython_ast::{Expr, Stmt};
use crate::ast::checks;
use crate::ast::{checkers, helpers};
use crate::autofix::{fixer, fixes};
use crate::check_ast::Checker;
@@ -12,14 +12,14 @@ 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
.iter()
.map(|index| checker.parents[*index])
.collect();
if let Some(mut check) = checks::check_super_args(scope, &parents, expr, func, args) {
if let Some(mut check) = checkers::super_args(scope, &parents, expr, func, args) {
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
if let Some(fix) = fixes::remove_super_arguments(&mut checker.locator, expr) {
check.amend(fix);

View File

@@ -1,6 +1,6 @@
use rustpython_ast::Expr;
use crate::ast::checks;
use crate::ast::checkers;
use crate::ast::types::{CheckLocator, Range};
use crate::autofix::fixer;
use crate::check_ast::Checker;
@@ -8,7 +8,7 @@ use crate::checks::{CheckKind, Fix};
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &Vec<Expr>) {
if let Some(mut check) =
checks::check_type_of_primitive(func, args, checker.locate_check(Range::from_located(expr)))
checkers::type_of_primitive(func, args, checker.locate_check(Range::from_located(expr)))
{
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
if let CheckKind::TypeOfPrimitive(primitive) = &check.kind {

View File

@@ -1,17 +1,15 @@
use rustpython_ast::Expr;
use crate::ast::checks;
use crate::ast::checkers;
use crate::ast::types::{CheckLocator, Range};
use crate::autofix::fixer;
use crate::check_ast::Checker;
use crate::checks::Fix;
pub fn unnecessary_abspath(checker: &mut Checker, expr: &Expr, func: &Expr, args: &Vec<Expr>) {
if let Some(mut check) = checks::check_unnecessary_abspath(
func,
args,
checker.locate_check(Range::from_located(expr)),
) {
if let Some(mut check) =
checkers::unnecessary_abspath(func, args, checker.locate_check(Range::from_located(expr)))
{
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
check.amend(Fix {
content: "__file__".to_string(),

View File

@@ -1,7 +1,7 @@
use log::error;
use rustpython_ast::{Expr, Stmt};
use crate::ast::checks;
use crate::ast::checkers;
use crate::ast::types::{CheckLocator, Range};
use crate::autofix::{fixer, fixes};
use crate::check_ast::Checker;
@@ -12,7 +12,7 @@ pub fn useless_metaclass_type(
value: &Expr,
targets: &Vec<Expr>,
) {
if let Some(mut check) = checks::check_useless_metaclass_type(
if let Some(mut check) = checkers::useless_metaclass_type(
targets,
value,
checker.locate_check(Range::from_located(stmt)),

View File

@@ -1,6 +1,6 @@
use rustpython_ast::{Expr, Keyword, Stmt};
use crate::ast::checks;
use crate::ast::checkers;
use crate::autofix::{fixer, fixes};
use crate::check_ast::Checker;
@@ -12,7 +12,7 @@ pub fn useless_object_inheritance(
keywords: &[Keyword],
) {
let scope = checker.current_scope();
if let Some(mut check) = checks::check_useless_object_inheritance(name, bases, scope) {
if let Some(mut check) = checkers::useless_object_inheritance(name, bases, scope) {
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
if let Some(fix) = fixes::remove_class_def_base(
&mut checker.locator,

View 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

View File

@@ -0,0 +1,59 @@
---
source: src/linter.rs
expression: checks
---
- kind:
DuplicateHandlerException:
- OSError
location:
row: 17
column: 9
end_location:
row: 17
column: 25
fix:
content: "OSError,"
location:
row: 17
column: 9
end_location:
row: 17
column: 25
applied: false
- kind:
DuplicateHandlerException:
- MyError
location:
row: 28
column: 9
end_location:
row: 28
column: 25
fix:
content: "MyError,"
location:
row: 28
column: 9
end_location:
row: 28
column: 25
applied: false
- kind:
DuplicateHandlerException:
- re.error
location:
row: 49
column: 9
end_location:
row: 49
column: 27
fix:
content: "re.error,"
location:
row: 49
column: 9
end_location:
row: 49
column: 27
applied: false

View 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: ~

View 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: ~

View 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: ~

View File

@@ -0,0 +1,13 @@
---
source: src/linter.rs
expression: checks
---
- kind: OneLinerDocstring
location:
row: 124
column: 6
end_location:
row: 126
column: 8
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: BlankLineAfterSummary
location:
row: 195
column: 6
end_location:
row: 198
column: 8
fix: ~
- kind: BlankLineAfterSummary
location:
row: 205
column: 6
end_location:
row: 210
column: 8
fix: ~

View File

@@ -0,0 +1,13 @@
---
source: src/linter.rs
expression: checks
---
- kind: NewLineAfterLastParagraph
location:
row: 276
column: 6
end_location:
row: 278
column: 20
fix: ~

View File

@@ -0,0 +1,29 @@
---
source: src/linter.rs
expression: checks
---
- kind: NoSurroundingWhitespace
location:
row: 283
column: 6
end_location:
row: 283
column: 34
fix: ~
- kind: NoSurroundingWhitespace
location:
row: 288
column: 6
end_location:
row: 288
column: 38
fix: ~
- kind: NoSurroundingWhitespace
location:
row: 294
column: 6
end_location:
row: 297
column: 8
fix: ~

View File

@@ -0,0 +1,141 @@
---
source: src/linter.rs
expression: checks
---
- kind: DocstringEndsInNonPeriod
location:
row: 69
column: 6
end_location:
row: 69
column: 12
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 124
column: 6
end_location:
row: 126
column: 8
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 283
column: 6
end_location:
row: 283
column: 34
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 288
column: 6
end_location:
row: 288
column: 38
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 350
column: 6
end_location:
row: 350
column: 18
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 401
column: 26
end_location:
row: 401
column: 40
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 405
column: 6
end_location:
row: 405
column: 25
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 411
column: 6
end_location:
row: 411
column: 25
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 417
column: 36
end_location:
row: 417
column: 50
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 424
column: 50
end_location:
row: 424
column: 64
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 465
column: 6
end_location:
row: 465
column: 25
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 470
column: 6
end_location:
row: 470
column: 25
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 475
column: 6
end_location:
row: 475
column: 25
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 482
column: 6
end_location:
row: 482
column: 25
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 504
column: 6
end_location:
row: 504
column: 35
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 509
column: 6
end_location:
row: 509
column: 34
fix: ~
- kind: DocstringEndsInNonPeriod
location:
row: 515
column: 6
end_location:
row: 515
column: 33
fix: ~

View File

@@ -0,0 +1,29 @@
---
source: src/linter.rs
expression: checks
---
- kind: EmptyDocstring
location:
row: 19
column: 10
end_location:
row: 19
column: 15
fix: ~
- kind: EmptyDocstring
location:
row: 69
column: 6
end_location:
row: 69
column: 12
fix: ~
- kind: EmptyDocstring
location:
row: 75
column: 10
end_location:
row: 75
column: 11
fix: ~

View File

@@ -4,10 +4,10 @@ expression: checks
---
- kind: LateFutureImport
location:
row: 7
row: 6
column: 1
end_location:
row: 7
row: 6
column: 38
fix: ~

View File

@@ -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