Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2df89dedd | ||
|
|
b8f12d2e79 | ||
|
|
67b1d0463a | ||
|
|
d008a181ec | ||
|
|
6d612a428a | ||
|
|
c0cb73ab16 | ||
|
|
2e1eb84cbf | ||
|
|
b03a8728b5 | ||
|
|
1dd3350a30 | ||
|
|
bda34945a5 | ||
|
|
85dcaa8d3c | ||
|
|
4ac74ed0ad | ||
|
|
b7e2a4b9a9 | ||
|
|
53a7758248 | ||
|
|
2ba767957d | ||
|
|
08152787e1 |
@@ -1,5 +1,5 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff
|
||||
rev: v0.0.37
|
||||
rev: v0.0.38
|
||||
hooks:
|
||||
- id: lint
|
||||
|
||||
68
Cargo.lock
generated
68
Cargo.lock
generated
@@ -458,6 +458,19 @@ dependencies = [
|
||||
"cache-padded",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"terminal_size",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
@@ -652,6 +665,12 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
@@ -1011,6 +1030,20 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc61e98be01e89296f3343a878e9f8ca75a494cb5aaf29df65ef55734aeb85f5"
|
||||
dependencies = [
|
||||
"console",
|
||||
"linked-hash-map",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"similar",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -1122,6 +1155,12 @@ version = "0.2.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
@@ -1744,7 +1783,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@@ -1758,8 +1797,8 @@ dependencies = [
|
||||
"fern",
|
||||
"filetime",
|
||||
"glob",
|
||||
"insta",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"notify",
|
||||
"once_cell",
|
||||
@@ -1978,6 +2017,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
@@ -2100,6 +2145,16 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.7.3"
|
||||
@@ -2595,3 +2650,12 @@ dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
14
Cargo.toml
14
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
@@ -18,9 +18,8 @@ common-path = { version = "1.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
fern = { version = "0.6.1" }
|
||||
filetime = { version = "0.2.17" }
|
||||
glob = { version = "0.3.0" }
|
||||
glob = "0.3.0"
|
||||
itertools = "0.10.3"
|
||||
lazy_static = "1.4.0"
|
||||
log = { version = "0.4.17" }
|
||||
notify = { version = "4.0.17" }
|
||||
once_cell = { version = "1.13.1" }
|
||||
@@ -33,6 +32,9 @@ toml = { version = "0.5.9" }
|
||||
update-informer = { version = "0.5.0", default_features = false, features = ["pypi"], optional = true }
|
||||
walkdir = { version = "2.3.2" }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.19.1", features = ["yaml"] }
|
||||
|
||||
[features]
|
||||
default = ["update-informer"]
|
||||
update-informer = ["dep:update-informer"]
|
||||
@@ -42,3 +44,9 @@ panic = "abort"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.similar]
|
||||
opt-level = 3
|
||||
|
||||
14
README.md
14
README.md
@@ -57,7 +57,7 @@ ruff also works with [Pre-Commit](https://pre-commit.com) (requires Cargo on sys
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff
|
||||
rev: v0.0.37
|
||||
rev: v0.0.38
|
||||
hooks:
|
||||
- id: lint
|
||||
```
|
||||
@@ -86,7 +86,7 @@ ruff path/to/code/ --select F401 F403
|
||||
See `ruff --help` for more:
|
||||
|
||||
```shell
|
||||
ruff (v0.0.37)
|
||||
ruff (v0.0.38)
|
||||
An extremely fast Python linter.
|
||||
|
||||
USAGE:
|
||||
@@ -124,14 +124,14 @@ ruff's goal is to achieve feature-parity with Flake8 when used (1) without any p
|
||||
stylistic checks; limiting to Python 3 obviates the need for certain compatibility checks.)
|
||||
|
||||
Under those conditions, Flake8 implements about 60 rules, give or take. At time of writing, ruff
|
||||
implements 38 rules. (Note that these 38 rules likely cover a disproportionate share of errors:
|
||||
implements 42 rules. (Note that these 42 rules likely cover a disproportionate share of errors:
|
||||
unused imports, undefined variables, etc.)
|
||||
|
||||
The unimplemented rules are tracked in #170, and include:
|
||||
|
||||
- 14 rules related to string `.format` calls.
|
||||
- 1 rule related to parsing and syntax.
|
||||
- 6 logical rules.
|
||||
- 4 logical rules.
|
||||
- 1 rule related to parsing.
|
||||
|
||||
Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
|
||||
@@ -149,12 +149,14 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
|
||||
| E712 | TrueFalseComparison | Comparison to `True` should be `cond is True` |
|
||||
| E713 | NotInTest | Test for membership should be `not in` |
|
||||
| E714 | NotIsTest | Test for object identity should be `is not` |
|
||||
| E721 | TypeComparison | do not compare types, use `isinstance()` |
|
||||
| E722 | DoNotUseBareExcept | Do not use bare `except` |
|
||||
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def |
|
||||
| E741 | AmbiguousVariableName | ambiguous variable name '...' |
|
||||
| E742 | AmbiguousClassName | ambiguous class name '...' |
|
||||
| E743 | AmbiguousFunctionName | ambiguous function name '...' |
|
||||
| E902 | IOError | No such file or directory: `...` |
|
||||
| E999 | SyntaxError | SyntaxError: ... |
|
||||
| F401 | UnusedImport | `...` imported but unused |
|
||||
| F403 | ImportStarUsage | `from ... import *` used; unable to detect undefined names |
|
||||
| F404 | LateFutureImport | from __future__ imports must occur at the beginning of the file |
|
||||
@@ -166,6 +168,8 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F
|
||||
| F621 | TooManyExpressionsInStarredAssignment | too many expressions in star-unpacking assignment |
|
||||
| F622 | TwoStarredExpressions | two starred expressions in assignment |
|
||||
| F631 | AssertTuple | Assert test is a non-empty tuple, which is always `True` |
|
||||
| F632 | IsLiteral | use ==/!= to compare constant literals |
|
||||
| F633 | InvalidPrintSyntax | use of >> is invalid with print function |
|
||||
| F634 | IfTuple | If test is a tuple, which is always `True` |
|
||||
| F701 | BreakOutsideLoop | `break` outside loop |
|
||||
| F702 | ContinueOutsideLoop | `continue` not properly in loop |
|
||||
|
||||
@@ -13,13 +13,15 @@ fn main() {
|
||||
CheckKind::DoNotAssignLambda,
|
||||
CheckKind::DoNotUseBareExcept,
|
||||
CheckKind::DuplicateArgumentName,
|
||||
CheckKind::ForwardAnnotationSyntaxError("...".to_string()),
|
||||
CheckKind::FStringMissingPlaceholders,
|
||||
CheckKind::ForwardAnnotationSyntaxError("...".to_string()),
|
||||
CheckKind::FutureFeatureNotDefined("...".to_string()),
|
||||
CheckKind::IOError("...".to_string()),
|
||||
CheckKind::IfTuple,
|
||||
CheckKind::ImportStarNotPermitted("...".to_string()),
|
||||
CheckKind::ImportStarUsage("...".to_string()),
|
||||
CheckKind::InvalidPrintSyntax,
|
||||
CheckKind::IsLiteral,
|
||||
CheckKind::LateFutureImport,
|
||||
CheckKind::LineTooLong(89, 88),
|
||||
CheckKind::ModuleImportNotAtTopOfFile,
|
||||
@@ -31,9 +33,11 @@ fn main() {
|
||||
CheckKind::NotIsTest,
|
||||
CheckKind::RaiseNotImplemented,
|
||||
CheckKind::ReturnOutsideFunction,
|
||||
CheckKind::SyntaxError("...".to_string()),
|
||||
CheckKind::TooManyExpressionsInStarredAssignment,
|
||||
CheckKind::TrueFalseComparison(true, RejectedCmpop::Eq),
|
||||
CheckKind::TwoStarredExpressions,
|
||||
CheckKind::TypeComparison,
|
||||
CheckKind::UndefinedExport("...".to_string()),
|
||||
CheckKind::UndefinedLocal("...".to_string()),
|
||||
CheckKind::UndefinedName("...".to_string()),
|
||||
|
||||
47
resources/test/fixtures/E711.py
vendored
47
resources/test/fixtures/E711.py
vendored
@@ -1,11 +1,50 @@
|
||||
if var == None:
|
||||
#: E711
|
||||
if res == None:
|
||||
pass
|
||||
#: E711
|
||||
if res != None:
|
||||
pass
|
||||
#: E711
|
||||
if None == res:
|
||||
pass
|
||||
#: E711
|
||||
if None != res:
|
||||
pass
|
||||
#: E711
|
||||
if res[1] == None:
|
||||
pass
|
||||
#: E711
|
||||
if res[1] != None:
|
||||
pass
|
||||
#: E711
|
||||
if None != res[1]:
|
||||
pass
|
||||
#: E711
|
||||
if None == res[1]:
|
||||
pass
|
||||
|
||||
if None != var:
|
||||
#: Okay
|
||||
if x not in y:
|
||||
pass
|
||||
|
||||
if var is None:
|
||||
if not (X in Y or X is Z):
|
||||
pass
|
||||
|
||||
if None is not var:
|
||||
if not (X in Y):
|
||||
pass
|
||||
|
||||
if x is not y:
|
||||
pass
|
||||
|
||||
if X is not Y is not Z:
|
||||
pass
|
||||
|
||||
if TrueElement.get_element(True) == TrueElement.get_element(False):
|
||||
pass
|
||||
|
||||
if (True) == TrueElement or x == TrueElement:
|
||||
pass
|
||||
|
||||
assert (not foo) in bar
|
||||
assert {"x": not foo} in bar
|
||||
assert [42, not foo] in bar
|
||||
|
||||
42
resources/test/fixtures/E712.py
vendored
42
resources/test/fixtures/E712.py
vendored
@@ -1,14 +1,46 @@
|
||||
if var == True:
|
||||
#: E712
|
||||
if res == True:
|
||||
pass
|
||||
#: E712
|
||||
if res != False:
|
||||
pass
|
||||
#: E712
|
||||
if True != res:
|
||||
pass
|
||||
#: E712
|
||||
if False == res:
|
||||
pass
|
||||
#: E712
|
||||
if res[1] == True:
|
||||
pass
|
||||
#: E712
|
||||
if res[1] != False:
|
||||
pass
|
||||
#: E712
|
||||
var = 1 if cond == True else -1 if cond == False else cond
|
||||
#: E712
|
||||
if (True) == TrueElement or x == TrueElement:
|
||||
pass
|
||||
|
||||
if False != var:
|
||||
#: Okay
|
||||
if x not in y:
|
||||
pass
|
||||
|
||||
if var != False != True:
|
||||
if not (X in Y or X is Z):
|
||||
pass
|
||||
|
||||
if var is True:
|
||||
if not (X in Y):
|
||||
pass
|
||||
|
||||
if False is not var:
|
||||
if x is not y:
|
||||
pass
|
||||
|
||||
if X is not Y is not Z:
|
||||
pass
|
||||
|
||||
if TrueElement.get_element(True) == TrueElement.get_element(False):
|
||||
pass
|
||||
|
||||
assert (not foo) in bar
|
||||
assert {"x": not foo} in bar
|
||||
assert [42, not foo] in bar
|
||||
|
||||
43
resources/test/fixtures/E713.py
vendored
43
resources/test/fixtures/E713.py
vendored
@@ -1,7 +1,38 @@
|
||||
my_list = [1, 2, 3]
|
||||
if not num in my_list:
|
||||
print(num)
|
||||
#: E713
|
||||
if not X in Y:
|
||||
pass
|
||||
#: E713
|
||||
if not X.B in Y:
|
||||
pass
|
||||
#: E713
|
||||
if not X in Y and Z == "zero":
|
||||
pass
|
||||
#: E713
|
||||
if X == "zero" or not Y in Z:
|
||||
pass
|
||||
#: E713
|
||||
if not (X in Y):
|
||||
pass
|
||||
|
||||
my_list = [1, 2, 3]
|
||||
if num not in my_list:
|
||||
print(num)
|
||||
#: Okay
|
||||
if x not in y:
|
||||
pass
|
||||
|
||||
if not (X in Y or X is Z):
|
||||
pass
|
||||
|
||||
if x is not y:
|
||||
pass
|
||||
|
||||
if X is not Y is not Z:
|
||||
pass
|
||||
|
||||
if TrueElement.get_element(True) == TrueElement.get_element(False):
|
||||
pass
|
||||
|
||||
if (True) == TrueElement or x == TrueElement:
|
||||
pass
|
||||
|
||||
assert (not foo) in bar
|
||||
assert {"x": not foo} in bar
|
||||
assert [42, not foo] in bar
|
||||
|
||||
41
resources/test/fixtures/E714.py
vendored
41
resources/test/fixtures/E714.py
vendored
@@ -1,5 +1,38 @@
|
||||
if not user is None:
|
||||
print(user.name)
|
||||
#: E714
|
||||
if not X is Y:
|
||||
pass
|
||||
#: E714
|
||||
if not X.B is Y:
|
||||
pass
|
||||
#: E714
|
||||
if not X is Y is not Z:
|
||||
pass
|
||||
|
||||
if user is not None:
|
||||
print(user.name)
|
||||
#: Okay
|
||||
if not X is not Y:
|
||||
pass
|
||||
|
||||
if x not in y:
|
||||
pass
|
||||
|
||||
if not (X in Y or X is Z):
|
||||
pass
|
||||
|
||||
if not (X in Y):
|
||||
pass
|
||||
|
||||
if x is not y:
|
||||
pass
|
||||
|
||||
if X is not Y is not Z:
|
||||
pass
|
||||
|
||||
if TrueElement.get_element(True) == TrueElement.get_element(False):
|
||||
pass
|
||||
|
||||
if (True) == TrueElement or x == TrueElement:
|
||||
pass
|
||||
|
||||
assert (not foo) in bar
|
||||
assert {"x": not foo} in bar
|
||||
assert [42, not foo] in bar
|
||||
|
||||
54
resources/test/fixtures/E721.py
vendored
Normal file
54
resources/test/fixtures/E721.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
#: E721
|
||||
if type(res) == type(42):
|
||||
pass
|
||||
#: E721
|
||||
if type(res) != type(""):
|
||||
pass
|
||||
#: E721
|
||||
import types
|
||||
|
||||
if res == types.IntType:
|
||||
pass
|
||||
#: E721
|
||||
import types
|
||||
|
||||
if type(res) is not types.ListType:
|
||||
pass
|
||||
#: E721
|
||||
assert type(res) == type(False) or type(res) == type(None)
|
||||
#: E721
|
||||
assert type(res) == type([])
|
||||
#: E721
|
||||
assert type(res) == type(())
|
||||
#: E721
|
||||
assert type(res) == type((0,))
|
||||
#: E721
|
||||
assert type(res) == type((0))
|
||||
#: E721
|
||||
assert type(res) != type((1,))
|
||||
#: E721
|
||||
assert type(res) is type((1,))
|
||||
#: E721
|
||||
assert type(res) is not type((1,))
|
||||
#: E211 E721
|
||||
assert type(res) == type(
|
||||
[
|
||||
2,
|
||||
]
|
||||
)
|
||||
#: E201 E201 E202 E721
|
||||
assert type(res) == type(())
|
||||
#: E201 E202 E721
|
||||
assert type(res) == type((0,))
|
||||
|
||||
#: Okay
|
||||
import types
|
||||
|
||||
if isinstance(res, int):
|
||||
pass
|
||||
if isinstance(res, str):
|
||||
pass
|
||||
if isinstance(res, types.MethodType):
|
||||
pass
|
||||
if type(a) != type(b) or type(a) == type(ccc):
|
||||
pass
|
||||
26
resources/test/fixtures/E722.py
vendored
26
resources/test/fixtures/E722.py
vendored
@@ -1,9 +1,33 @@
|
||||
#: E722
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
#: E722
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
#: E722
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
#: Okay
|
||||
fake_code = """"
|
||||
try:
|
||||
do_something()
|
||||
except:
|
||||
pass
|
||||
"""
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
#: Okay
|
||||
from . import compute_type
|
||||
|
||||
if compute_type(foo) == 5:
|
||||
pass
|
||||
|
||||
25
resources/test/fixtures/E731.py
vendored
25
resources/test/fixtures/E731.py
vendored
@@ -1,6 +1,21 @@
|
||||
from typing import Callable, Iterable
|
||||
#: E731
|
||||
f = lambda x: 2 * x
|
||||
#: E731
|
||||
f = lambda x: 2 * x
|
||||
#: E731
|
||||
while False:
|
||||
this = lambda y, z: 2 * x
|
||||
|
||||
a = lambda x: x**2
|
||||
b = map(lambda x: 2 * x, range(3))
|
||||
c: Callable = lambda x: x**2
|
||||
d: Iterable = map(lambda x: 2 * x, range(3))
|
||||
|
||||
f = object()
|
||||
#: E731
|
||||
f.method = lambda: "Method"
|
||||
|
||||
f = {}
|
||||
#: E731
|
||||
f["a"] = lambda x: x ** 2
|
||||
|
||||
f = []
|
||||
f.append(lambda x: x ** 2)
|
||||
|
||||
lambda: "no-op"
|
||||
|
||||
2
resources/test/fixtures/E999.py
vendored
Normal file
2
resources/test/fixtures/E999.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
def x():
|
||||
|
||||
5
resources/test/fixtures/F632.py
vendored
Normal file
5
resources/test/fixtures/F632.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
if x is "abc":
|
||||
pass
|
||||
|
||||
if 123 is not y:
|
||||
pass
|
||||
4
resources/test/fixtures/F633.py
vendored
Normal file
4
resources/test/fixtures/F633.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
|
||||
print >> sys.stderr, "Hello"
|
||||
27
resources/test/fixtures/future_annotations.py
vendored
Normal file
27
resources/test/fixtures/future_annotations.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from models import Fruit, Nut
|
||||
|
||||
|
||||
@dataclass
|
||||
class Foo:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
@classmethod
|
||||
def a(cls) -> Foo:
|
||||
return cls(x=0, y=0)
|
||||
|
||||
@classmethod
|
||||
def b(cls) -> "Foo":
|
||||
return cls(x=0, y=0)
|
||||
|
||||
@classmethod
|
||||
def c(cls) -> Bar:
|
||||
return cls(x=0, y=0)
|
||||
|
||||
@classmethod
|
||||
def d(cls) -> Fruit:
|
||||
return cls(x=0, y=0)
|
||||
6
resources/test/fixtures/pyproject.toml
vendored
6
resources/test/fixtures/pyproject.toml
vendored
@@ -1,6 +1,6 @@
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
exclude = ["excluded.py", "**/migrations"]
|
||||
extend-exclude = ["excluded.py", "migrations"]
|
||||
select = [
|
||||
"E402",
|
||||
"E501",
|
||||
@@ -8,12 +8,14 @@ select = [
|
||||
"E712",
|
||||
"E713",
|
||||
"E714",
|
||||
"E721",
|
||||
"E722",
|
||||
"E731",
|
||||
"E741",
|
||||
"E742",
|
||||
"E743",
|
||||
"E902",
|
||||
"E999",
|
||||
"F401",
|
||||
"F403",
|
||||
"F404",
|
||||
@@ -25,6 +27,8 @@ select = [
|
||||
"F621",
|
||||
"F622",
|
||||
"F631",
|
||||
"F632",
|
||||
"F633",
|
||||
"F634",
|
||||
"F701",
|
||||
"F702",
|
||||
|
||||
@@ -42,18 +42,20 @@ pub fn check_not_tests(
|
||||
|
||||
if matches!(op, Unaryop::Not) {
|
||||
if let ExprKind::Compare { ops, .. } = &operand.node {
|
||||
match ops[..] {
|
||||
[Cmpop::In] => {
|
||||
if check_not_in {
|
||||
checks.push(Check::new(CheckKind::NotInTest, operand.location));
|
||||
for op in ops {
|
||||
match op {
|
||||
Cmpop::In => {
|
||||
if check_not_in {
|
||||
checks.push(Check::new(CheckKind::NotInTest, operand.location));
|
||||
}
|
||||
}
|
||||
}
|
||||
[Cmpop::Is] => {
|
||||
if check_not_is {
|
||||
checks.push(Check::new(CheckKind::NotIsTest, operand.location));
|
||||
Cmpop::Is => {
|
||||
if check_not_is {
|
||||
checks.push(Check::new(CheckKind::NotIsTest, operand.location));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,9 +158,7 @@ pub fn check_useless_object_inheritance(
|
||||
CheckKind::UselessObjectInheritance(name.to_string()),
|
||||
expr.location,
|
||||
);
|
||||
if matches!(autofix, fixer::Mode::Generate)
|
||||
|| matches!(autofix, fixer::Mode::Apply)
|
||||
{
|
||||
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
if let Some(fix) = fixes::remove_class_def_base(
|
||||
locator,
|
||||
&stmt.location,
|
||||
@@ -254,9 +254,7 @@ pub fn check_assert_equals(expr: &Expr, autofix: &fixer::Mode) -> Option<Check>
|
||||
if let ExprKind::Name { id, .. } = &value.node {
|
||||
if id == "self" {
|
||||
let mut check = Check::new(CheckKind::NoAssertEquals, expr.location);
|
||||
if matches!(autofix, fixer::Mode::Generate)
|
||||
|| matches!(autofix, fixer::Mode::Apply)
|
||||
{
|
||||
if matches!(autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||
check.amend(Fix {
|
||||
content: "assertEqual".to_string(),
|
||||
start: Location::new(expr.location.row(), expr.location.column() + 1),
|
||||
@@ -315,7 +313,7 @@ pub fn check_repeated_keys(
|
||||
(Some(DictionaryKey::Variable(v1)), Some(DictionaryKey::Variable(v2))) => {
|
||||
if check_repeated_variables && v1 == v2 {
|
||||
checks.push(Check::new(
|
||||
CheckKind::MultiValueRepeatedKeyVariable(v2.to_string()),
|
||||
CheckKind::MultiValueRepeatedKeyVariable((*v2).to_string()),
|
||||
k2.location,
|
||||
))
|
||||
}
|
||||
@@ -436,6 +434,90 @@ pub fn check_literal_comparisons(
|
||||
checks
|
||||
}
|
||||
|
||||
fn is_constant(expr: &Expr) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Constant { .. } => true,
|
||||
ExprKind::Tuple { elts, .. } => elts.iter().all(is_constant),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_singleton(expr: &Expr) -> bool {
|
||||
matches!(
|
||||
expr.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::None | Constant::Bool(_) | Constant::Ellipsis,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn is_constant_non_singleton(expr: &Expr) -> bool {
|
||||
is_constant(expr) && !is_singleton(expr)
|
||||
}
|
||||
|
||||
/// Check IsLiteral compliance.
|
||||
pub fn check_is_literal(
|
||||
left: &Expr,
|
||||
ops: &Vec<Cmpop>,
|
||||
comparators: &Vec<Expr>,
|
||||
location: Location,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
let mut left = left;
|
||||
for (op, right) in izip!(ops, comparators) {
|
||||
if matches!(op, Cmpop::Is | Cmpop::IsNot)
|
||||
&& (is_constant_non_singleton(left) || is_constant_non_singleton(right))
|
||||
{
|
||||
checks.push(Check::new(CheckKind::IsLiteral, location));
|
||||
}
|
||||
left = right;
|
||||
}
|
||||
|
||||
checks
|
||||
}
|
||||
|
||||
/// Check TypeComparison compliance.
|
||||
pub fn check_type_comparison(
|
||||
ops: &Vec<Cmpop>,
|
||||
comparators: &Vec<Expr>,
|
||||
location: Location,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
for (op, right) in izip!(ops, comparators) {
|
||||
if matches!(op, Cmpop::Is | Cmpop::IsNot | Cmpop::Eq | Cmpop::NotEq) {
|
||||
match &right.node {
|
||||
ExprKind::Call { func, args, .. } => {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
// Ex) type(False)
|
||||
if id == "type" {
|
||||
if let Some(arg) = args.first() {
|
||||
// Allow comparison for types which are not obvious.
|
||||
if !matches!(arg.node, ExprKind::Name { .. }) {
|
||||
checks.push(Check::new(CheckKind::TypeComparison, location));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Attribute { value, .. } => {
|
||||
if let ExprKind::Name { id, .. } = &value.node {
|
||||
// Ex) types.IntType
|
||||
if id == "types" {
|
||||
checks.push(Check::new(CheckKind::TypeComparison, location));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checks
|
||||
}
|
||||
|
||||
/// Check TwoStarredExpressions and TooManyExpressionsInStarredAssignment compliance.
|
||||
pub fn check_starred_expressions(
|
||||
elts: &[Expr],
|
||||
|
||||
@@ -22,7 +22,7 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
if let StmtKind::AugAssign { .. } = &stmt.node {
|
||||
if let Some(binding) = scope.values.get("__all__") {
|
||||
if let BindingKind::Export(existing) = &binding.kind {
|
||||
names.extend(existing.clone());
|
||||
names.extend_from_slice(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,9 +70,7 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
pub fn on_conditional_branch(parent_stack: &[usize], parents: &[&Stmt]) -> bool {
|
||||
for index in parent_stack.iter().rev() {
|
||||
let parent = parents[*index];
|
||||
if matches!(parent.node, StmtKind::If { .. })
|
||||
|| matches!(parent.node, StmtKind::While { .. })
|
||||
{
|
||||
if matches!(parent.node, StmtKind::If { .. } | StmtKind::While { .. }) {
|
||||
return true;
|
||||
}
|
||||
if let StmtKind::Expr { value } = &parent.node {
|
||||
@@ -89,10 +87,10 @@ pub fn on_conditional_branch(parent_stack: &[usize], parents: &[&Stmt]) -> bool
|
||||
pub fn in_nested_block(parent_stack: &[usize], parents: &[&Stmt]) -> bool {
|
||||
for index in parent_stack.iter().rev() {
|
||||
let parent = parents[*index];
|
||||
if matches!(parent.node, StmtKind::Try { .. })
|
||||
|| matches!(parent.node, StmtKind::If { .. })
|
||||
|| matches!(parent.node, StmtKind::With { .. })
|
||||
{
|
||||
if matches!(
|
||||
parent.node,
|
||||
StmtKind::Try { .. } | StmtKind::If { .. } | StmtKind::With { .. }
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
131
src/check_ast.rs
131
src/check_ast.rs
@@ -3,7 +3,7 @@ use std::path::Path;
|
||||
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
|
||||
KeywordData, Location, Stmt, StmtKind, Suite,
|
||||
KeywordData, Location, Operator, Stmt, StmtKind, Suite,
|
||||
};
|
||||
use rustpython_parser::parser;
|
||||
|
||||
@@ -36,7 +36,8 @@ struct Checker<'a> {
|
||||
scopes: Vec<Scope>,
|
||||
scope_stack: Vec<usize>,
|
||||
dead_scopes: Vec<usize>,
|
||||
deferred_annotations: Vec<(Location, &'a str)>,
|
||||
deferred_string_annotations: Vec<(Location, &'a str)>,
|
||||
deferred_annotations: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
|
||||
deferred_functions: Vec<(&'a Stmt, Vec<usize>, Vec<usize>)>,
|
||||
deferred_lambdas: Vec<(&'a Expr, Vec<usize>, Vec<usize>)>,
|
||||
deferred_assignments: Vec<usize>,
|
||||
@@ -47,6 +48,7 @@ struct Checker<'a> {
|
||||
seen_non_import: bool,
|
||||
seen_docstring: bool,
|
||||
futures_allowed: bool,
|
||||
annotations_future_enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -67,6 +69,7 @@ impl<'a> Checker<'a> {
|
||||
scopes: vec![],
|
||||
scope_stack: vec![],
|
||||
dead_scopes: vec![],
|
||||
deferred_string_annotations: vec![],
|
||||
deferred_annotations: vec![],
|
||||
deferred_functions: vec![],
|
||||
deferred_lambdas: vec![],
|
||||
@@ -77,6 +80,7 @@ impl<'a> Checker<'a> {
|
||||
seen_non_import: false,
|
||||
seen_docstring: false,
|
||||
futures_allowed: true,
|
||||
annotations_future_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -423,6 +427,10 @@ where
|
||||
},
|
||||
);
|
||||
|
||||
if alias.node.name == "annotations" {
|
||||
self.annotations_future_enabled = true;
|
||||
}
|
||||
|
||||
if self.settings.select.contains(&CheckCode::F407)
|
||||
&& !ALL_FEATURE_NAMES.contains(&alias.node.name.deref())
|
||||
{
|
||||
@@ -472,7 +480,7 @@ where
|
||||
let binding = Binding {
|
||||
kind: BindingKind::Importation(match module {
|
||||
None => name.clone(),
|
||||
Some(parent) => format!("{}.{}", parent, name.clone()),
|
||||
Some(parent) => format!("{}.{}", parent, name),
|
||||
}),
|
||||
used: None,
|
||||
location: stmt.location,
|
||||
@@ -581,6 +589,17 @@ where
|
||||
let prev_in_literal = self.in_literal;
|
||||
let prev_in_annotation = self.in_annotation;
|
||||
|
||||
// Important:
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre-visit.
|
||||
match &expr.node {
|
||||
ExprKind::Subscript { value, .. } => {
|
||||
@@ -645,8 +664,7 @@ where
|
||||
.settings
|
||||
.select
|
||||
.contains(CheckKind::YieldOutsideFunction.code())
|
||||
&& matches!(scope.kind, ScopeKind::Class)
|
||||
|| matches!(scope.kind, ScopeKind::Module)
|
||||
&& matches!(scope.kind, ScopeKind::Class | ScopeKind::Module)
|
||||
{
|
||||
self.checks
|
||||
.push(Check::new(CheckKind::YieldOutsideFunction, expr.location));
|
||||
@@ -669,6 +687,28 @@ where
|
||||
}
|
||||
self.in_f_string = true;
|
||||
}
|
||||
ExprKind::BinOp {
|
||||
left,
|
||||
op: Operator::RShift,
|
||||
..
|
||||
} => {
|
||||
if self.settings.select.contains(&CheckCode::F633) {
|
||||
if let ExprKind::Name { id, .. } = &left.node {
|
||||
if id == "print" {
|
||||
let scope = &self.scopes
|
||||
[*(self.scope_stack.last().expect("No current scope found."))];
|
||||
if let Some(Binding {
|
||||
kind: BindingKind::Builtin,
|
||||
..
|
||||
}) = scope.values.get("print")
|
||||
{
|
||||
self.checks
|
||||
.push(Check::new(CheckKind::InvalidPrintSyntax, left.location));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::UnaryOp { op, operand } => {
|
||||
let check_not_in = self.settings.select.contains(&CheckCode::E713);
|
||||
let check_not_is = self.settings.select.contains(&CheckCode::E714);
|
||||
@@ -697,12 +737,30 @@ where
|
||||
check_true_false_comparisons,
|
||||
));
|
||||
}
|
||||
|
||||
if self.settings.select.contains(&CheckCode::F632) {
|
||||
self.checks.extend(checks::check_is_literal(
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
expr.location,
|
||||
));
|
||||
}
|
||||
|
||||
if self.settings.select.contains(&CheckCode::E721) {
|
||||
self.checks.extend(checks::check_type_comparison(
|
||||
ops,
|
||||
comparators,
|
||||
expr.location,
|
||||
));
|
||||
}
|
||||
}
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} if self.in_annotation && !self.in_literal => {
|
||||
self.deferred_annotations.push((expr.location, value));
|
||||
self.deferred_string_annotations
|
||||
.push((expr.location, value));
|
||||
}
|
||||
ExprKind::GeneratorExp { .. }
|
||||
| ExprKind::ListComp { .. }
|
||||
@@ -763,7 +821,7 @@ where
|
||||
} else if match_name_or_attr(func, "NamedTuple") {
|
||||
self.visit_expr(func);
|
||||
|
||||
// NamedTuple("a", [("a", int)])
|
||||
// Ex) NamedTuple("a", [("a", int)])
|
||||
if args.len() > 1 {
|
||||
match &args[1].node {
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
@@ -787,7 +845,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// NamedTuple("a", a=int)
|
||||
// Ex) NamedTuple("a", a=int)
|
||||
for keyword in keywords {
|
||||
let KeywordData { value, .. } = &keyword.node;
|
||||
self.visit_annotation(value);
|
||||
@@ -795,7 +853,7 @@ where
|
||||
} else if match_name_or_attr(func, "TypedDict") {
|
||||
self.visit_expr(func);
|
||||
|
||||
// TypedDict("a", {"a": int})
|
||||
// Ex) TypedDict("a", {"a": int})
|
||||
if args.len() > 1 {
|
||||
if let ExprKind::Dict { keys, values } = &args[1].node {
|
||||
for key in keys {
|
||||
@@ -809,7 +867,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// TypedDict("a", a=int)
|
||||
// Ex) TypedDict("a", a=int)
|
||||
for keyword in keywords {
|
||||
let KeywordData { value, .. } = &keyword.node;
|
||||
self.visit_annotation(value);
|
||||
@@ -998,7 +1056,7 @@ impl<'a> Checker<'a> {
|
||||
|
||||
for builtin in BUILTINS {
|
||||
scope.values.insert(
|
||||
builtin.to_string(),
|
||||
(*builtin).to_string(),
|
||||
Binding {
|
||||
kind: BindingKind::Builtin,
|
||||
location: Default::default(),
|
||||
@@ -1008,7 +1066,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
for builtin in MAGIC_GLOBALS {
|
||||
scope.values.insert(
|
||||
builtin.to_string(),
|
||||
(*builtin).to_string(),
|
||||
Binding {
|
||||
kind: BindingKind::Builtin,
|
||||
location: Default::default(),
|
||||
@@ -1077,9 +1135,7 @@ impl<'a> Checker<'a> {
|
||||
&& !current.values.contains_key(id)
|
||||
{
|
||||
for scope in self.scopes.iter().rev().skip(1) {
|
||||
if matches!(scope.kind, ScopeKind::Function)
|
||||
|| matches!(scope.kind, ScopeKind::Module)
|
||||
{
|
||||
if matches!(scope.kind, ScopeKind::Function | ScopeKind::Module) {
|
||||
let used = scope
|
||||
.values
|
||||
.get(id)
|
||||
@@ -1110,9 +1166,10 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
// TODO(charlie): Include comprehensions here.
|
||||
if matches!(parent.node, StmtKind::For { .. })
|
||||
|| matches!(parent.node, StmtKind::AsyncFor { .. })
|
||||
|| operations::is_unpacking_assignment(parent)
|
||||
if matches!(
|
||||
parent.node,
|
||||
StmtKind::For { .. } | StmtKind::AsyncFor { .. }
|
||||
) || operations::is_unpacking_assignment(parent)
|
||||
{
|
||||
self.add_binding(
|
||||
id.to_string(),
|
||||
@@ -1127,9 +1184,12 @@ impl<'a> Checker<'a> {
|
||||
|
||||
if id == "__all__"
|
||||
&& matches!(current.kind, ScopeKind::Module)
|
||||
&& (matches!(parent.node, StmtKind::Assign { .. })
|
||||
|| matches!(parent.node, StmtKind::AugAssign { .. })
|
||||
|| matches!(parent.node, StmtKind::AnnAssign { .. }))
|
||||
&& matches!(
|
||||
parent.node,
|
||||
StmtKind::Assign { .. }
|
||||
| StmtKind::AugAssign { .. }
|
||||
| StmtKind::AnnAssign { .. }
|
||||
)
|
||||
{
|
||||
self.add_binding(
|
||||
id.to_string(),
|
||||
@@ -1172,12 +1232,19 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_deferred_annotations<'b>(&mut self, path: &str, allocator: &'b mut Vec<Expr>)
|
||||
fn check_deferred_annotations(&mut self) {
|
||||
while let Some((expr, scopes, parents)) = self.deferred_annotations.pop() {
|
||||
self.parent_stack = parents;
|
||||
self.scope_stack = scopes;
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_deferred_string_annotations<'b>(&mut self, path: &str, allocator: &'b mut Vec<Expr>)
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
while !self.deferred_annotations.is_empty() {
|
||||
let (location, expression) = self.deferred_annotations.pop().unwrap();
|
||||
while let Some((location, expression)) = self.deferred_string_annotations.pop() {
|
||||
if let Ok(mut expr) = parser::parse_expression(expression, path) {
|
||||
relocate_expr(&mut expr, location);
|
||||
allocator.push(expr);
|
||||
@@ -1194,9 +1261,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn check_deferred_functions(&mut self) {
|
||||
while !self.deferred_functions.is_empty() {
|
||||
let (stmt, scopes, parents) = self.deferred_functions.pop().unwrap();
|
||||
|
||||
while let Some((stmt, scopes, parents)) = self.deferred_functions.pop() {
|
||||
self.parent_stack = parents;
|
||||
self.scope_stack = scopes;
|
||||
self.push_scope(Scope::new(ScopeKind::Function));
|
||||
@@ -1220,9 +1285,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn check_deferred_lambdas(&mut self) {
|
||||
while !self.deferred_lambdas.is_empty() {
|
||||
let (expr, scopes, parents) = self.deferred_lambdas.pop().unwrap();
|
||||
|
||||
while let Some((expr, scopes, parents)) = self.deferred_lambdas.pop() {
|
||||
self.parent_stack = parents;
|
||||
self.scope_stack = scopes;
|
||||
self.push_scope(Scope::new(ScopeKind::Function));
|
||||
@@ -1240,8 +1303,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn check_deferred_assignments(&mut self) {
|
||||
while !self.deferred_assignments.is_empty() {
|
||||
let index = self.deferred_assignments.pop().unwrap();
|
||||
while let Some(index) = self.deferred_assignments.pop() {
|
||||
if self.settings.select.contains(&CheckCode::F841) {
|
||||
self.checks
|
||||
.extend(checks::check_unused_variables(&self.scopes[index]));
|
||||
@@ -1256,7 +1318,7 @@ impl<'a> Checker<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
for index in self.dead_scopes.clone() {
|
||||
for index in self.dead_scopes.iter().copied() {
|
||||
let scope = &self.scopes[index];
|
||||
|
||||
let all_binding = scope.values.get("__all__");
|
||||
@@ -1327,8 +1389,9 @@ pub fn check_ast(
|
||||
checker.check_deferred_functions();
|
||||
checker.check_deferred_lambdas();
|
||||
checker.check_deferred_assignments();
|
||||
checker.check_deferred_annotations();
|
||||
let mut allocator = vec![];
|
||||
checker.check_deferred_annotations(path, &mut allocator);
|
||||
checker.check_deferred_string_annotations(path, &mut allocator);
|
||||
|
||||
// Reset the scope to module-level, and check all consumed scopes.
|
||||
checker.scope_stack = vec![GLOBAL_SCOPE_INDEX];
|
||||
|
||||
@@ -14,12 +14,14 @@ pub enum CheckCode {
|
||||
E712,
|
||||
E713,
|
||||
E714,
|
||||
E721,
|
||||
E722,
|
||||
E731,
|
||||
E741,
|
||||
E742,
|
||||
E743,
|
||||
E902,
|
||||
E999,
|
||||
F401,
|
||||
F403,
|
||||
F404,
|
||||
@@ -31,6 +33,8 @@ pub enum CheckCode {
|
||||
F621,
|
||||
F622,
|
||||
F631,
|
||||
F632,
|
||||
F633,
|
||||
F634,
|
||||
F701,
|
||||
F702,
|
||||
@@ -58,13 +62,15 @@ impl FromStr for CheckCode {
|
||||
"E711" => Ok(CheckCode::E711),
|
||||
"E712" => Ok(CheckCode::E712),
|
||||
"E713" => Ok(CheckCode::E713),
|
||||
"E722" => Ok(CheckCode::E722),
|
||||
"E714" => Ok(CheckCode::E714),
|
||||
"E721" => Ok(CheckCode::E721),
|
||||
"E722" => Ok(CheckCode::E722),
|
||||
"E731" => Ok(CheckCode::E731),
|
||||
"E741" => Ok(CheckCode::E741),
|
||||
"E742" => Ok(CheckCode::E742),
|
||||
"E743" => Ok(CheckCode::E743),
|
||||
"E902" => Ok(CheckCode::E902),
|
||||
"E999" => Ok(CheckCode::E999),
|
||||
"F401" => Ok(CheckCode::F401),
|
||||
"F403" => Ok(CheckCode::F403),
|
||||
"F404" => Ok(CheckCode::F404),
|
||||
@@ -76,12 +82,15 @@ impl FromStr for CheckCode {
|
||||
"F621" => Ok(CheckCode::F621),
|
||||
"F622" => Ok(CheckCode::F622),
|
||||
"F631" => Ok(CheckCode::F631),
|
||||
"F632" => Ok(CheckCode::F632),
|
||||
"F633" => Ok(CheckCode::F633),
|
||||
"F634" => Ok(CheckCode::F634),
|
||||
"F701" => Ok(CheckCode::F701),
|
||||
"F702" => Ok(CheckCode::F702),
|
||||
"F704" => Ok(CheckCode::F704),
|
||||
"F706" => Ok(CheckCode::F706),
|
||||
"F707" => Ok(CheckCode::F707),
|
||||
"F722" => Ok(CheckCode::F722),
|
||||
"F821" => Ok(CheckCode::F821),
|
||||
"F822" => Ok(CheckCode::F822),
|
||||
"F823" => Ok(CheckCode::F823),
|
||||
@@ -104,12 +113,14 @@ impl CheckCode {
|
||||
CheckCode::E712 => "E712",
|
||||
CheckCode::E713 => "E713",
|
||||
CheckCode::E714 => "E714",
|
||||
CheckCode::E721 => "E721",
|
||||
CheckCode::E722 => "E722",
|
||||
CheckCode::E731 => "E731",
|
||||
CheckCode::E741 => "E741",
|
||||
CheckCode::E742 => "E742",
|
||||
CheckCode::E743 => "E743",
|
||||
CheckCode::E902 => "E902",
|
||||
CheckCode::E999 => "E999",
|
||||
CheckCode::F401 => "F401",
|
||||
CheckCode::F403 => "F403",
|
||||
CheckCode::F404 => "F404",
|
||||
@@ -121,6 +132,8 @@ impl CheckCode {
|
||||
CheckCode::F621 => "F621",
|
||||
CheckCode::F622 => "F622",
|
||||
CheckCode::F631 => "F631",
|
||||
CheckCode::F632 => "F632",
|
||||
CheckCode::F633 => "F633",
|
||||
CheckCode::F634 => "F634",
|
||||
CheckCode::F701 => "F701",
|
||||
CheckCode::F702 => "F702",
|
||||
@@ -143,7 +156,7 @@ impl CheckCode {
|
||||
pub fn lint_source(&self) -> &'static LintSource {
|
||||
match self {
|
||||
CheckCode::E501 => &LintSource::Lines,
|
||||
CheckCode::E902 => &LintSource::FileSystem,
|
||||
CheckCode::E902 | CheckCode::E999 => &LintSource::FileSystem,
|
||||
_ => &LintSource::AST,
|
||||
}
|
||||
}
|
||||
@@ -181,6 +194,8 @@ pub enum CheckKind {
|
||||
IfTuple,
|
||||
ImportStarNotPermitted(String),
|
||||
ImportStarUsage(String),
|
||||
InvalidPrintSyntax,
|
||||
IsLiteral,
|
||||
LateFutureImport,
|
||||
LineTooLong(usize, usize),
|
||||
ModuleImportNotAtTopOfFile,
|
||||
@@ -192,9 +207,11 @@ pub enum CheckKind {
|
||||
NotIsTest,
|
||||
RaiseNotImplemented,
|
||||
ReturnOutsideFunction,
|
||||
SyntaxError(String),
|
||||
TooManyExpressionsInStarredAssignment,
|
||||
TrueFalseComparison(bool, RejectedCmpop),
|
||||
TwoStarredExpressions,
|
||||
TypeComparison,
|
||||
UndefinedExport(String),
|
||||
UndefinedLocal(String),
|
||||
UndefinedName(String),
|
||||
@@ -218,13 +235,15 @@ impl CheckKind {
|
||||
CheckKind::DoNotAssignLambda => "DoNotAssignLambda",
|
||||
CheckKind::DoNotUseBareExcept => "DoNotUseBareExcept",
|
||||
CheckKind::DuplicateArgumentName => "DuplicateArgumentName",
|
||||
CheckKind::ForwardAnnotationSyntaxError(_) => "ForwardAnnotationSyntaxError",
|
||||
CheckKind::FStringMissingPlaceholders => "FStringMissingPlaceholders",
|
||||
CheckKind::ForwardAnnotationSyntaxError(_) => "ForwardAnnotationSyntaxError",
|
||||
CheckKind::FutureFeatureNotDefined(_) => "FutureFeatureNotDefined",
|
||||
CheckKind::IOError(_) => "IOError",
|
||||
CheckKind::IfTuple => "IfTuple",
|
||||
CheckKind::ImportStarNotPermitted(_) => "ImportStarNotPermitted",
|
||||
CheckKind::ImportStarUsage(_) => "ImportStarUsage",
|
||||
CheckKind::InvalidPrintSyntax => "InvalidPrintSyntax",
|
||||
CheckKind::IsLiteral => "IsLiteral",
|
||||
CheckKind::LateFutureImport => "LateFutureImport",
|
||||
CheckKind::LineTooLong(_, _) => "LineTooLong",
|
||||
CheckKind::ModuleImportNotAtTopOfFile => "ModuleImportNotAtTopOfFile",
|
||||
@@ -236,11 +255,13 @@ impl CheckKind {
|
||||
CheckKind::NotIsTest => "NotIsTest",
|
||||
CheckKind::RaiseNotImplemented => "RaiseNotImplemented",
|
||||
CheckKind::ReturnOutsideFunction => "ReturnOutsideFunction",
|
||||
CheckKind::SyntaxError(_) => "SyntaxError",
|
||||
CheckKind::TooManyExpressionsInStarredAssignment => {
|
||||
"TooManyExpressionsInStarredAssignment"
|
||||
}
|
||||
CheckKind::TrueFalseComparison(_, _) => "TrueFalseComparison",
|
||||
CheckKind::TwoStarredExpressions => "TwoStarredExpressions",
|
||||
CheckKind::TypeComparison => "TypeComparison",
|
||||
CheckKind::UndefinedExport(_) => "UndefinedExport",
|
||||
CheckKind::UndefinedLocal(_) => "UndefinedLocal",
|
||||
CheckKind::UndefinedName(_) => "UndefinedName",
|
||||
@@ -264,13 +285,15 @@ impl CheckKind {
|
||||
CheckKind::DoNotAssignLambda => &CheckCode::E731,
|
||||
CheckKind::DoNotUseBareExcept => &CheckCode::E722,
|
||||
CheckKind::DuplicateArgumentName => &CheckCode::F831,
|
||||
CheckKind::ForwardAnnotationSyntaxError(_) => &CheckCode::F722,
|
||||
CheckKind::FStringMissingPlaceholders => &CheckCode::F541,
|
||||
CheckKind::ForwardAnnotationSyntaxError(_) => &CheckCode::F722,
|
||||
CheckKind::FutureFeatureNotDefined(_) => &CheckCode::F407,
|
||||
CheckKind::IOError(_) => &CheckCode::E902,
|
||||
CheckKind::IfTuple => &CheckCode::F634,
|
||||
CheckKind::ImportStarNotPermitted(_) => &CheckCode::F406,
|
||||
CheckKind::ImportStarUsage(_) => &CheckCode::F403,
|
||||
CheckKind::InvalidPrintSyntax => &CheckCode::F633,
|
||||
CheckKind::IsLiteral => &CheckCode::F632,
|
||||
CheckKind::LateFutureImport => &CheckCode::F404,
|
||||
CheckKind::LineTooLong(_, _) => &CheckCode::E501,
|
||||
CheckKind::ModuleImportNotAtTopOfFile => &CheckCode::E402,
|
||||
@@ -282,9 +305,11 @@ impl CheckKind {
|
||||
CheckKind::NotIsTest => &CheckCode::E714,
|
||||
CheckKind::RaiseNotImplemented => &CheckCode::F901,
|
||||
CheckKind::ReturnOutsideFunction => &CheckCode::F706,
|
||||
CheckKind::SyntaxError(_) => &CheckCode::E999,
|
||||
CheckKind::TooManyExpressionsInStarredAssignment => &CheckCode::F621,
|
||||
CheckKind::TrueFalseComparison(_, _) => &CheckCode::E712,
|
||||
CheckKind::TwoStarredExpressions => &CheckCode::F622,
|
||||
CheckKind::TypeComparison => &CheckCode::E721,
|
||||
CheckKind::UndefinedExport(_) => &CheckCode::F822,
|
||||
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
|
||||
CheckKind::UndefinedName(_) => &CheckCode::F821,
|
||||
@@ -335,12 +360,14 @@ impl CheckKind {
|
||||
format!("No such file or directory: `{name}`")
|
||||
}
|
||||
CheckKind::IfTuple => "If test is a tuple, which is always `True`".to_string(),
|
||||
CheckKind::InvalidPrintSyntax => "use of >> is invalid with print function".to_string(),
|
||||
CheckKind::ImportStarNotPermitted(name) => {
|
||||
format!("`from {name} import *` only allowed at module level")
|
||||
}
|
||||
CheckKind::ImportStarUsage(name) => {
|
||||
format!("`from {name} import *` used; unable to detect undefined names")
|
||||
}
|
||||
CheckKind::IsLiteral => "use ==/!= to compare constant literals".to_string(),
|
||||
CheckKind::LateFutureImport => {
|
||||
"from __future__ imports must occur at the beginning of the file".to_string()
|
||||
}
|
||||
@@ -373,6 +400,7 @@ impl CheckKind {
|
||||
CheckKind::ReturnOutsideFunction => {
|
||||
"a `return` statement outside of a function/method".to_string()
|
||||
}
|
||||
CheckKind::SyntaxError(message) => format!("SyntaxError: {message}"),
|
||||
CheckKind::TooManyExpressionsInStarredAssignment => {
|
||||
"too many expressions in star-unpacking assignment".to_string()
|
||||
}
|
||||
@@ -395,6 +423,7 @@ impl CheckKind {
|
||||
},
|
||||
},
|
||||
CheckKind::TwoStarredExpressions => "two starred expressions in assignment".to_string(),
|
||||
CheckKind::TypeComparison => "do not compare types, use `isinstance()`".to_string(),
|
||||
CheckKind::UndefinedExport(name) => {
|
||||
format!("Undefined name `{name}` in `__all__`")
|
||||
}
|
||||
|
||||
97
src/fs.rs
97
src/fs.rs
@@ -4,34 +4,60 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use glob::Pattern;
|
||||
use log::debug;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
fn is_not_hidden(entry: &DirEntry) -> bool {
|
||||
entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map(|s| (entry.depth() == 0 || !s.starts_with('.')))
|
||||
.unwrap_or(false)
|
||||
fn is_excluded(path: &Path, exclude: &[Pattern]) -> bool {
|
||||
if let Some(file_name) = path.file_name() {
|
||||
if let Some(file_name) = file_name.to_str() {
|
||||
for pattern in exclude {
|
||||
if pattern.matches(file_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_not_excluded(entry: &DirEntry, exclude: &[Pattern]) -> bool {
|
||||
entry
|
||||
.path()
|
||||
.to_str()
|
||||
.map(|s| !exclude.iter().any(|pattern| pattern.matches(s)))
|
||||
.unwrap_or(false)
|
||||
fn is_included(path: &Path) -> bool {
|
||||
let file_name = path.to_string_lossy();
|
||||
file_name.ends_with(".py") || file_name.ends_with(".pyi")
|
||||
}
|
||||
|
||||
pub fn iter_python_files<'a>(
|
||||
path: &'a PathBuf,
|
||||
exclude: &'a [Pattern],
|
||||
extend_exclude: &'a [Pattern],
|
||||
) -> impl Iterator<Item = DirEntry> + 'a {
|
||||
WalkDir::new(path)
|
||||
.follow_links(true)
|
||||
.into_iter()
|
||||
.filter_entry(|entry| is_not_hidden(entry) && is_not_excluded(entry, exclude))
|
||||
.filter_entry(|entry| {
|
||||
if exclude.is_empty() && extend_exclude.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let path = entry.path();
|
||||
if is_excluded(path, exclude) {
|
||||
debug!("Ignored path via `exclude`: {:?}", path);
|
||||
false
|
||||
} else if is_excluded(path, extend_exclude) {
|
||||
debug!("Ignored path via `extend-exclude`: {:?}", path);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.filter_map(|entry| entry.ok())
|
||||
.filter(|entry| entry.path().to_string_lossy().ends_with(".py"))
|
||||
.filter(|entry| {
|
||||
let path = entry.path();
|
||||
is_included(path)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_file(path: &Path) -> Result<String> {
|
||||
@@ -41,3 +67,46 @@ pub fn read_file(path: &Path) -> Result<String> {
|
||||
buf_reader.read_to_string(&mut contents)?;
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use glob::Pattern;
|
||||
|
||||
use crate::fs::{is_excluded, is_included};
|
||||
|
||||
#[test]
|
||||
fn inclusions() {
|
||||
let path = Path::new("foo/bar/baz.py");
|
||||
assert!(is_included(path));
|
||||
|
||||
let path = Path::new("foo/bar/baz.pyi");
|
||||
assert!(is_included(path));
|
||||
|
||||
let path = Path::new("foo/bar/baz.js");
|
||||
assert!(!is_included(path));
|
||||
|
||||
let path = Path::new("foo/bar/baz");
|
||||
assert!(!is_included(path));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclusions() {
|
||||
let path = Path::new("foo");
|
||||
let exclude = vec![Pattern::new("foo").unwrap()];
|
||||
assert!(is_excluded(path, &exclude));
|
||||
|
||||
let path = Path::new("foo/bar");
|
||||
let exclude = vec![Pattern::new("bar").unwrap()];
|
||||
assert!(is_excluded(path, &exclude));
|
||||
|
||||
let path = Path::new("foo/bar/baz.py");
|
||||
let exclude = vec![Pattern::new("baz.py").unwrap()];
|
||||
assert!(is_excluded(path, &exclude));
|
||||
|
||||
let path = Path::new("foo/bar/baz.py");
|
||||
let exclude = vec![Pattern::new("baz").unwrap()];
|
||||
assert!(!is_excluded(path, &exclude));
|
||||
}
|
||||
}
|
||||
|
||||
1226
src/linter.rs
1226
src/linter.rs
File diff suppressed because it is too large
Load Diff
31
src/main.rs
31
src/main.rs
@@ -1,4 +1,4 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::Instant;
|
||||
@@ -54,9 +54,12 @@ struct Cli {
|
||||
/// List of error codes to ignore.
|
||||
#[clap(long, multiple = true)]
|
||||
ignore: Vec<CheckCode>,
|
||||
/// List of file and/or directory patterns to exclude from checks.
|
||||
/// List of paths, used to exclude files and/or directories from checks.
|
||||
#[clap(long, multiple = true)]
|
||||
exclude: Vec<Pattern>,
|
||||
/// Like --exclude, but adds additional files and directories on top of the excluded ones.
|
||||
#[clap(long, multiple = true)]
|
||||
extend_exclude: Vec<Pattern>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "update-informer")]
|
||||
@@ -93,7 +96,7 @@ fn run_once(
|
||||
let start = Instant::now();
|
||||
let paths: Vec<DirEntry> = files
|
||||
.iter()
|
||||
.flat_map(|path| iter_python_files(path, &settings.exclude))
|
||||
.flat_map(|path| iter_python_files(path, &settings.exclude, &settings.extend_exclude))
|
||||
.collect();
|
||||
let duration = start.elapsed();
|
||||
debug!("Identified files to lint in: {:?}", duration);
|
||||
@@ -103,8 +106,17 @@ fn run_once(
|
||||
.par_iter()
|
||||
.map(|entry| {
|
||||
lint_path(entry.path(), settings, &cache.into(), &autofix.into()).unwrap_or_else(|e| {
|
||||
error!("Failed to check {}: {e:?}", entry.path().to_string_lossy());
|
||||
vec![]
|
||||
if settings.select.contains(&CheckCode::E999) {
|
||||
vec![Message {
|
||||
kind: CheckKind::SyntaxError(e.to_string()),
|
||||
fixed: false,
|
||||
location: Default::default(),
|
||||
filename: entry.path().to_string_lossy().to_string(),
|
||||
}]
|
||||
} else {
|
||||
error!("Failed to check {}: {e:?}", entry.path().to_string_lossy());
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
@@ -183,9 +195,7 @@ fn inner_main() -> Result<ExitCode> {
|
||||
|
||||
set_up_logging(cli.verbose)?;
|
||||
|
||||
// TODO(charlie): Can we avoid this cast?
|
||||
let paths: Vec<&Path> = cli.files.iter().map(PathBuf::as_path).collect();
|
||||
let mut settings = Settings::from_paths(paths)?;
|
||||
let mut settings = Settings::from_paths(&cli.files);
|
||||
if !cli.select.is_empty() {
|
||||
settings.select(cli.select);
|
||||
}
|
||||
@@ -193,7 +203,10 @@ fn inner_main() -> Result<ExitCode> {
|
||||
settings.ignore(&cli.ignore);
|
||||
}
|
||||
if !cli.exclude.is_empty() {
|
||||
settings.exclude(cli.exclude);
|
||||
settings.exclude = cli.exclude;
|
||||
}
|
||||
if !cli.extend_exclude.is_empty() {
|
||||
settings.extend_exclude = cli.extend_exclude;
|
||||
}
|
||||
|
||||
if cli.watch {
|
||||
|
||||
@@ -8,29 +8,26 @@ use serde::Deserialize;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::fs;
|
||||
|
||||
pub fn load_config<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<(PathBuf, Config)> {
|
||||
pub fn load_config(paths: &[PathBuf]) -> Config {
|
||||
match find_project_root(paths) {
|
||||
Some(project_root) => match find_pyproject_toml(&project_root) {
|
||||
Some(path) => {
|
||||
debug!("Found pyproject.toml at: {}", path.to_string_lossy());
|
||||
debug!("Found pyproject.toml at: {:?}", path);
|
||||
match parse_pyproject_toml(&path) {
|
||||
Ok(pyproject) => {
|
||||
let config = pyproject
|
||||
.tool
|
||||
.and_then(|tool| tool.ruff)
|
||||
.unwrap_or_default();
|
||||
Ok((project_root, config))
|
||||
}
|
||||
Ok(pyproject) => pyproject
|
||||
.tool
|
||||
.and_then(|tool| tool.ruff)
|
||||
.unwrap_or_default(),
|
||||
Err(e) => {
|
||||
println!("Failed to load pyproject.toml: {:?}", e);
|
||||
println!("Falling back to default configuration...");
|
||||
Ok(Default::default())
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Ok(Default::default()),
|
||||
None => Default::default(),
|
||||
},
|
||||
None => Ok(Default::default()),
|
||||
None => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +36,7 @@ pub fn load_config<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<(Pat
|
||||
pub struct Config {
|
||||
pub line_length: Option<usize>,
|
||||
pub exclude: Option<Vec<PathBuf>>,
|
||||
pub extend_exclude: Option<Vec<PathBuf>>,
|
||||
pub select: Option<Vec<CheckCode>>,
|
||||
pub ignore: Option<Vec<CheckCode>>,
|
||||
}
|
||||
@@ -70,8 +68,8 @@ fn find_user_pyproject_toml() -> Option<PathBuf> {
|
||||
dirs::home_dir().map(|path| path.join(".ruff"))
|
||||
}
|
||||
|
||||
fn find_project_root<'a>(sources: impl IntoIterator<Item = &'a Path>) -> Option<PathBuf> {
|
||||
if let Some(prefix) = common_path_all(sources) {
|
||||
fn find_project_root(sources: &[PathBuf]) -> Option<PathBuf> {
|
||||
if let Some(prefix) = common_path_all(sources.iter().map(PathBuf::as_path)) {
|
||||
for directory in prefix.ancestors() {
|
||||
if directory.join(".git").is_dir() {
|
||||
return Some(directory.to_path_buf());
|
||||
@@ -90,7 +88,7 @@ fn find_project_root<'a>(sources: impl IntoIterator<Item = &'a Path>) -> Option<
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -123,6 +121,7 @@ mod tests {
|
||||
ruff: Some(Config {
|
||||
line_length: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: None,
|
||||
})
|
||||
@@ -142,6 +141,7 @@ line-length = 79
|
||||
ruff: Some(Config {
|
||||
line_length: Some(79),
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: None,
|
||||
})
|
||||
@@ -161,6 +161,7 @@ exclude = ["foo.py"]
|
||||
ruff: Some(Config {
|
||||
line_length: None,
|
||||
exclude: Some(vec![Path::new("foo.py").to_path_buf()]),
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: None,
|
||||
})
|
||||
@@ -180,6 +181,7 @@ select = ["E501"]
|
||||
ruff: Some(Config {
|
||||
line_length: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: Some(vec![CheckCode::E501]),
|
||||
ignore: None,
|
||||
})
|
||||
@@ -199,6 +201,7 @@ ignore = ["E501"]
|
||||
ruff: Some(Config {
|
||||
line_length: None,
|
||||
exclude: None,
|
||||
extend_exclude: None,
|
||||
select: None,
|
||||
ignore: Some(vec![CheckCode::E501]),
|
||||
})
|
||||
@@ -238,8 +241,9 @@ other-attribute = 1
|
||||
|
||||
#[test]
|
||||
fn find_and_parse_pyproject_toml() -> Result<()> {
|
||||
let project_root = find_project_root([Path::new("resources/test/fixtures/__init__.py")])
|
||||
.expect("Unable to find project root.");
|
||||
let project_root =
|
||||
find_project_root(&[PathBuf::from("resources/test/fixtures/__init__.py")])
|
||||
.expect("Unable to find project root.");
|
||||
assert_eq!(project_root, Path::new("resources/test/fixtures"));
|
||||
|
||||
let path = find_pyproject_toml(&project_root).expect("Unable to find pyproject.toml.");
|
||||
@@ -254,9 +258,10 @@ other-attribute = 1
|
||||
config,
|
||||
Config {
|
||||
line_length: Some(88),
|
||||
exclude: Some(vec![
|
||||
exclude: None,
|
||||
extend_exclude: Some(vec![
|
||||
Path::new("excluded.py").to_path_buf(),
|
||||
Path::new("**/migrations").to_path_buf()
|
||||
Path::new("migrations").to_path_buf()
|
||||
]),
|
||||
select: Some(vec![
|
||||
CheckCode::E402,
|
||||
@@ -265,12 +270,14 @@ other-attribute = 1
|
||||
CheckCode::E712,
|
||||
CheckCode::E713,
|
||||
CheckCode::E714,
|
||||
CheckCode::E721,
|
||||
CheckCode::E722,
|
||||
CheckCode::E731,
|
||||
CheckCode::E741,
|
||||
CheckCode::E742,
|
||||
CheckCode::E743,
|
||||
CheckCode::E902,
|
||||
CheckCode::E999,
|
||||
CheckCode::F401,
|
||||
CheckCode::F403,
|
||||
CheckCode::F404,
|
||||
@@ -282,6 +289,8 @@ other-attribute = 1
|
||||
CheckCode::F621,
|
||||
CheckCode::F622,
|
||||
CheckCode::F631,
|
||||
CheckCode::F632,
|
||||
CheckCode::F633,
|
||||
CheckCode::F634,
|
||||
CheckCode::F701,
|
||||
CheckCode::F702,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use lazy_static::lazy_static;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
lazy_static! {
|
||||
static ref ANNOTATED_SUBSCRIPTS: BTreeSet<&'static str> = BTreeSet::from([
|
||||
static ANNOTATED_SUBSCRIPTS: Lazy<BTreeSet<&'static str>> = Lazy::new(|| {
|
||||
BTreeSet::from([
|
||||
"AbstractAsyncContextManager",
|
||||
"AbstractContextManager",
|
||||
"AbstractSet",
|
||||
@@ -80,8 +80,8 @@ lazy_static! {
|
||||
"set",
|
||||
"tuple",
|
||||
"type",
|
||||
]);
|
||||
}
|
||||
])
|
||||
});
|
||||
|
||||
pub fn is_annotated_subscript(name: &str) -> bool {
|
||||
ANNOTATED_SUBSCRIPTS.contains(name)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use glob::Pattern;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::pyproject::load_config;
|
||||
@@ -12,6 +12,7 @@ use crate::pyproject::load_config;
|
||||
pub struct Settings {
|
||||
pub line_length: usize,
|
||||
pub exclude: Vec<Pattern>,
|
||||
pub extend_exclude: Vec<Pattern>,
|
||||
pub select: BTreeSet<CheckCode>,
|
||||
}
|
||||
|
||||
@@ -23,25 +24,56 @@ impl Hash for Settings {
|
||||
}
|
||||
}
|
||||
}
|
||||
static DEFAULT_EXCLUDE: Lazy<Vec<Pattern>> = Lazy::new(|| {
|
||||
vec![
|
||||
Pattern::new(".bzr").unwrap(),
|
||||
Pattern::new(".direnv").unwrap(),
|
||||
Pattern::new(".eggs").unwrap(),
|
||||
Pattern::new(".git").unwrap(),
|
||||
Pattern::new(".hg").unwrap(),
|
||||
Pattern::new(".mypy_cache").unwrap(),
|
||||
Pattern::new(".nox").unwrap(),
|
||||
Pattern::new(".pants.d").unwrap(),
|
||||
Pattern::new(".svn").unwrap(),
|
||||
Pattern::new(".tox").unwrap(),
|
||||
Pattern::new(".venv").unwrap(),
|
||||
Pattern::new("__pypackages__").unwrap(),
|
||||
Pattern::new("_build").unwrap(),
|
||||
Pattern::new("buck-out").unwrap(),
|
||||
Pattern::new("build").unwrap(),
|
||||
Pattern::new("dist").unwrap(),
|
||||
Pattern::new("node_modules").unwrap(),
|
||||
Pattern::new("venv").unwrap(),
|
||||
]
|
||||
});
|
||||
|
||||
impl Settings {
|
||||
pub fn from_paths<'a>(paths: impl IntoIterator<Item = &'a Path>) -> Result<Self> {
|
||||
let (project_root, config) = load_config(paths)?;
|
||||
pub fn from_paths(paths: &[PathBuf]) -> Self {
|
||||
let config = load_config(paths);
|
||||
let mut settings = Settings {
|
||||
line_length: config.line_length.unwrap_or(88),
|
||||
exclude: config
|
||||
.exclude
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
if path.is_relative() {
|
||||
project_root.join(path)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
.map(|paths| {
|
||||
paths
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
Pattern::new(&path.to_string_lossy()).expect("Invalid pattern.")
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.map(|path| Pattern::new(&path.to_string_lossy()).expect("Invalid pattern."))
|
||||
.collect(),
|
||||
.unwrap_or_else(|| DEFAULT_EXCLUDE.clone()),
|
||||
extend_exclude: config
|
||||
.extend_exclude
|
||||
.map(|paths| {
|
||||
paths
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
Pattern::new(&path.to_string_lossy()).expect("Invalid pattern.")
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
select: BTreeSet::from_iter(config.select.unwrap_or_else(|| {
|
||||
vec![
|
||||
CheckCode::E402,
|
||||
@@ -50,12 +82,14 @@ impl Settings {
|
||||
CheckCode::E712,
|
||||
CheckCode::E713,
|
||||
CheckCode::E714,
|
||||
CheckCode::E721,
|
||||
CheckCode::E722,
|
||||
CheckCode::E731,
|
||||
CheckCode::E741,
|
||||
CheckCode::E742,
|
||||
CheckCode::E743,
|
||||
CheckCode::E902,
|
||||
CheckCode::E999,
|
||||
CheckCode::F401,
|
||||
CheckCode::F403,
|
||||
CheckCode::F406,
|
||||
@@ -66,6 +100,8 @@ impl Settings {
|
||||
CheckCode::F621,
|
||||
CheckCode::F622,
|
||||
CheckCode::F631,
|
||||
CheckCode::F632,
|
||||
CheckCode::F633,
|
||||
CheckCode::F634,
|
||||
CheckCode::F701,
|
||||
CheckCode::F702,
|
||||
@@ -88,7 +124,7 @@ impl Settings {
|
||||
if let Some(ignore) = &config.ignore {
|
||||
settings.ignore(ignore);
|
||||
}
|
||||
Ok(settings)
|
||||
settings
|
||||
}
|
||||
|
||||
pub fn select(&mut self, codes: Vec<CheckCode>) {
|
||||
@@ -103,8 +139,4 @@ impl Settings {
|
||||
self.select.remove(code);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exclude(&mut self, exclude: Vec<Pattern>) {
|
||||
self.exclude = exclude;
|
||||
}
|
||||
}
|
||||
|
||||
10
src/snapshots/ruff__linter__tests__e402.snap
Normal file
10
src/snapshots/ruff__linter__tests__e402.snap
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ModuleImportNotAtTopOfFile
|
||||
location:
|
||||
row: 20
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
13
src/snapshots/ruff__linter__tests__e501.snap
Normal file
13
src/snapshots/ruff__linter__tests__e501.snap
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
LineTooLong:
|
||||
- 123
|
||||
- 88
|
||||
location:
|
||||
row: 5
|
||||
column: 89
|
||||
fix: ~
|
||||
|
||||
53
src/snapshots/ruff__linter__tests__e711.snap
Normal file
53
src/snapshots/ruff__linter__tests__e711.snap
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
row: 2
|
||||
column: 11
|
||||
fix: ~
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
row: 5
|
||||
column: 11
|
||||
fix: ~
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
row: 11
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
row: 14
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
row: 17
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
NoneComparison: NotEq
|
||||
location:
|
||||
row: 20
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind:
|
||||
NoneComparison: Eq
|
||||
location:
|
||||
row: 23
|
||||
column: 4
|
||||
fix: ~
|
||||
|
||||
77
src/snapshots/ruff__linter__tests__e712.snap
Normal file
77
src/snapshots/ruff__linter__tests__e712.snap
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
- Eq
|
||||
location:
|
||||
row: 2
|
||||
column: 11
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
- NotEq
|
||||
location:
|
||||
row: 5
|
||||
column: 11
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
- NotEq
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
- Eq
|
||||
location:
|
||||
row: 11
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
- Eq
|
||||
location:
|
||||
row: 14
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
- NotEq
|
||||
location:
|
||||
row: 17
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
- Eq
|
||||
location:
|
||||
row: 20
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- false
|
||||
- Eq
|
||||
location:
|
||||
row: 20
|
||||
column: 44
|
||||
fix: ~
|
||||
- kind:
|
||||
TrueFalseComparison:
|
||||
- true
|
||||
- Eq
|
||||
location:
|
||||
row: 22
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
30
src/snapshots/ruff__linter__tests__e713.snap
Normal file
30
src/snapshots/ruff__linter__tests__e713.snap
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 2
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 5
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 8
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 11
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind: NotInTest
|
||||
location:
|
||||
row: 14
|
||||
column: 11
|
||||
fix: ~
|
||||
|
||||
20
src/snapshots/ruff__linter__tests__e714.snap
Normal file
20
src/snapshots/ruff__linter__tests__e714.snap
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 2
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 5
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind: NotIsTest
|
||||
location:
|
||||
row: 8
|
||||
column: 10
|
||||
fix: ~
|
||||
|
||||
85
src/snapshots/ruff__linter__tests__e721.snap
Normal file
85
src/snapshots/ruff__linter__tests__e721.snap
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 2
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 5
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 10
|
||||
column: 8
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 15
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 18
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 18
|
||||
column: 46
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 20
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 22
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 24
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 26
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 28
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 30
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 32
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 34
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 40
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind: TypeComparison
|
||||
location:
|
||||
row: 42
|
||||
column: 18
|
||||
fix: ~
|
||||
|
||||
20
src/snapshots/ruff__linter__tests__e722.snap
Normal file
20
src/snapshots/ruff__linter__tests__e722.snap
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: DoNotUseBareExcept
|
||||
location:
|
||||
row: 4
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: DoNotUseBareExcept
|
||||
location:
|
||||
row: 11
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: DoNotUseBareExcept
|
||||
location:
|
||||
row: 16
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
30
src/snapshots/ruff__linter__tests__e731.snap
Normal file
30
src/snapshots/ruff__linter__tests__e731.snap
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: DoNotAssignLambda
|
||||
location:
|
||||
row: 2
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: DoNotAssignLambda
|
||||
location:
|
||||
row: 4
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: DoNotAssignLambda
|
||||
location:
|
||||
row: 7
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: DoNotAssignLambda
|
||||
location:
|
||||
row: 12
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: DoNotAssignLambda
|
||||
location:
|
||||
row: 16
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
155
src/snapshots/ruff__linter__tests__e741.snap
Normal file
155
src/snapshots/ruff__linter__tests__e741.snap
Normal file
@@ -0,0 +1,155 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 3
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: I
|
||||
location:
|
||||
row: 4
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: O
|
||||
location:
|
||||
row: 5
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 6
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 9
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 10
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 11
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 16
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 20
|
||||
column: 8
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 25
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 26
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 30
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 33
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 34
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 40
|
||||
column: 8
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: I
|
||||
location:
|
||||
row: 40
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 44
|
||||
column: 8
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: I
|
||||
location:
|
||||
row: 44
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 48
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: I
|
||||
location:
|
||||
row: 48
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 57
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 66
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 71
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousVariableName: l
|
||||
location:
|
||||
row: 74
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
23
src/snapshots/ruff__linter__tests__e742.snap
Normal file
23
src/snapshots/ruff__linter__tests__e742.snap
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
AmbiguousClassName: l
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousClassName: I
|
||||
location:
|
||||
row: 5
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousClassName: O
|
||||
location:
|
||||
row: 9
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
23
src/snapshots/ruff__linter__tests__e743.snap
Normal file
23
src/snapshots/ruff__linter__tests__e743.snap
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
AmbiguousFunctionName: l
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousFunctionName: I
|
||||
location:
|
||||
row: 5
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
AmbiguousFunctionName: O
|
||||
location:
|
||||
row: 10
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
23
src/snapshots/ruff__linter__tests__f401.snap
Normal file
23
src/snapshots/ruff__linter__tests__f401.snap
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedImport: functools
|
||||
location:
|
||||
row: 3
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedImport: collections.OrderedDict
|
||||
location:
|
||||
row: 5
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedImport: logging.handlers
|
||||
location:
|
||||
row: 13
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
17
src/snapshots/ruff__linter__tests__f403.snap
Normal file
17
src/snapshots/ruff__linter__tests__f403.snap
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
ImportStarUsage: F634
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportStarUsage: F634
|
||||
location:
|
||||
row: 2
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
10
src/snapshots/ruff__linter__tests__f404.snap
Normal file
10
src/snapshots/ruff__linter__tests__f404.snap
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: LateFutureImport
|
||||
location:
|
||||
row: 7
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
17
src/snapshots/ruff__linter__tests__f406.snap
Normal file
17
src/snapshots/ruff__linter__tests__f406.snap
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
ImportStarNotPermitted: F634
|
||||
location:
|
||||
row: 5
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportStarNotPermitted: F634
|
||||
location:
|
||||
row: 9
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
11
src/snapshots/ruff__linter__tests__f407.snap
Normal file
11
src/snapshots/ruff__linter__tests__f407.snap
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
FutureFeatureNotDefined: non_existent_feature
|
||||
location:
|
||||
row: 2
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
20
src/snapshots/ruff__linter__tests__f541.snap
Normal file
20
src/snapshots/ruff__linter__tests__f541.snap
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: FStringMissingPlaceholders
|
||||
location:
|
||||
row: 4
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: FStringMissingPlaceholders
|
||||
location:
|
||||
row: 5
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: FStringMissingPlaceholders
|
||||
location:
|
||||
row: 7
|
||||
column: 7
|
||||
fix: ~
|
||||
|
||||
20
src/snapshots/ruff__linter__tests__f601.snap
Normal file
20
src/snapshots/ruff__linter__tests__f601.snap
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: MultiValueRepeatedKeyLiteral
|
||||
location:
|
||||
row: 3
|
||||
column: 6
|
||||
fix: ~
|
||||
- kind: MultiValueRepeatedKeyLiteral
|
||||
location:
|
||||
row: 9
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: MultiValueRepeatedKeyLiteral
|
||||
location:
|
||||
row: 11
|
||||
column: 7
|
||||
fix: ~
|
||||
|
||||
11
src/snapshots/ruff__linter__tests__f602.snap
Normal file
11
src/snapshots/ruff__linter__tests__f602.snap
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
MultiValueRepeatedKeyVariable: a
|
||||
location:
|
||||
row: 5
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
10
src/snapshots/ruff__linter__tests__f622.snap
Normal file
10
src/snapshots/ruff__linter__tests__f622.snap
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: TwoStarredExpressions
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__f631.snap
Normal file
15
src/snapshots/ruff__linter__tests__f631.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: AssertTuple
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: AssertTuple
|
||||
location:
|
||||
row: 2
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__f632.snap
Normal file
15
src/snapshots/ruff__linter__tests__f632.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: IsLiteral
|
||||
location:
|
||||
row: 1
|
||||
column: 6
|
||||
fix: ~
|
||||
- kind: IsLiteral
|
||||
location:
|
||||
row: 4
|
||||
column: 8
|
||||
fix: ~
|
||||
|
||||
10
src/snapshots/ruff__linter__tests__f633.snap
Normal file
10
src/snapshots/ruff__linter__tests__f633.snap
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: InvalidPrintSyntax
|
||||
location:
|
||||
row: 4
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__f634.snap
Normal file
15
src/snapshots/ruff__linter__tests__f634.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: IfTuple
|
||||
location:
|
||||
row: 1
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: IfTuple
|
||||
location:
|
||||
row: 7
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
25
src/snapshots/ruff__linter__tests__f701.snap
Normal file
25
src/snapshots/ruff__linter__tests__f701.snap
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: BreakOutsideLoop
|
||||
location:
|
||||
row: 4
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: BreakOutsideLoop
|
||||
location:
|
||||
row: 16
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: BreakOutsideLoop
|
||||
location:
|
||||
row: 20
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: BreakOutsideLoop
|
||||
location:
|
||||
row: 23
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
25
src/snapshots/ruff__linter__tests__f702.snap
Normal file
25
src/snapshots/ruff__linter__tests__f702.snap
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ContinueOutsideLoop
|
||||
location:
|
||||
row: 4
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: ContinueOutsideLoop
|
||||
location:
|
||||
row: 16
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: ContinueOutsideLoop
|
||||
location:
|
||||
row: 20
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: ContinueOutsideLoop
|
||||
location:
|
||||
row: 23
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
20
src/snapshots/ruff__linter__tests__f704.snap
Normal file
20
src/snapshots/ruff__linter__tests__f704.snap
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: YieldOutsideFunction
|
||||
location:
|
||||
row: 6
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: YieldOutsideFunction
|
||||
location:
|
||||
row: 9
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: YieldOutsideFunction
|
||||
location:
|
||||
row: 10
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__f706.snap
Normal file
15
src/snapshots/ruff__linter__tests__f706.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ReturnOutsideFunction
|
||||
location:
|
||||
row: 6
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: ReturnOutsideFunction
|
||||
location:
|
||||
row: 9
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
20
src/snapshots/ruff__linter__tests__f707.snap
Normal file
20
src/snapshots/ruff__linter__tests__f707.snap
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: DefaultExceptNotLast
|
||||
location:
|
||||
row: 3
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: DefaultExceptNotLast
|
||||
location:
|
||||
row: 10
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind: DefaultExceptNotLast
|
||||
location:
|
||||
row: 19
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
11
src/snapshots/ruff__linter__tests__f722.snap
Normal file
11
src/snapshots/ruff__linter__tests__f722.snap
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
ForwardAnnotationSyntaxError: ///
|
||||
location:
|
||||
row: 9
|
||||
column: 13
|
||||
fix: ~
|
||||
|
||||
35
src/snapshots/ruff__linter__tests__f821.snap
Normal file
35
src/snapshots/ruff__linter__tests__f821.snap
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UndefinedName: self
|
||||
location:
|
||||
row: 2
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: self
|
||||
location:
|
||||
row: 6
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: self
|
||||
location:
|
||||
row: 10
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: numeric_string
|
||||
location:
|
||||
row: 21
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: Bar
|
||||
location:
|
||||
row: 58
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
11
src/snapshots/ruff__linter__tests__f822.snap
Normal file
11
src/snapshots/ruff__linter__tests__f822.snap
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UndefinedExport: b
|
||||
location:
|
||||
row: 3
|
||||
column: 1
|
||||
fix: ~
|
||||
|
||||
11
src/snapshots/ruff__linter__tests__f823.snap
Normal file
11
src/snapshots/ruff__linter__tests__f823.snap
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UndefinedLocal: my_var
|
||||
location:
|
||||
row: 6
|
||||
column: 5
|
||||
fix: ~
|
||||
|
||||
20
src/snapshots/ruff__linter__tests__f831.snap
Normal file
20
src/snapshots/ruff__linter__tests__f831.snap
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: DuplicateArgumentName
|
||||
location:
|
||||
row: 1
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind: DuplicateArgumentName
|
||||
location:
|
||||
row: 5
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: DuplicateArgumentName
|
||||
location:
|
||||
row: 9
|
||||
column: 27
|
||||
fix: ~
|
||||
|
||||
35
src/snapshots/ruff__linter__tests__f841.snap
Normal file
35
src/snapshots/ruff__linter__tests__f841.snap
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedVariable: e
|
||||
location:
|
||||
row: 3
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: z
|
||||
location:
|
||||
row: 16
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: foo
|
||||
location:
|
||||
row: 20
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: a
|
||||
location:
|
||||
row: 21
|
||||
column: 6
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedVariable: b
|
||||
location:
|
||||
row: 21
|
||||
column: 9
|
||||
fix: ~
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__f901.snap
Normal file
15
src/snapshots/ruff__linter__tests__f901.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: RaiseNotImplemented
|
||||
location:
|
||||
row: 2
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind: RaiseNotImplemented
|
||||
location:
|
||||
row: 6
|
||||
column: 11
|
||||
fix: ~
|
||||
|
||||
17
src/snapshots/ruff__linter__tests__future_annotations.snap
Normal file
17
src/snapshots/ruff__linter__tests__future_annotations.snap
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedImport: models.Nut
|
||||
location:
|
||||
row: 5
|
||||
column: 1
|
||||
fix: ~
|
||||
- kind:
|
||||
UndefinedName: Bar
|
||||
location:
|
||||
row: 22
|
||||
column: 19
|
||||
fix: ~
|
||||
|
||||
285
src/snapshots/ruff__linter__tests__r001.snap
Normal file
285
src/snapshots/ruff__linter__tests__r001.snap
Normal file
@@ -0,0 +1,285 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 5
|
||||
column: 9
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 5
|
||||
column: 8
|
||||
end:
|
||||
row: 5
|
||||
column: 16
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 10
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 9
|
||||
column: 8
|
||||
end:
|
||||
row: 11
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 16
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 15
|
||||
column: 8
|
||||
end:
|
||||
row: 18
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 24
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 22
|
||||
column: 8
|
||||
end:
|
||||
row: 25
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 31
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 29
|
||||
column: 8
|
||||
end:
|
||||
row: 32
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 37
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 36
|
||||
column: 8
|
||||
end:
|
||||
row: 39
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 45
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 43
|
||||
column: 8
|
||||
end:
|
||||
row: 47
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 53
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 51
|
||||
column: 8
|
||||
end:
|
||||
row: 55
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 61
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 59
|
||||
column: 8
|
||||
end:
|
||||
row: 63
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 69
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 67
|
||||
column: 8
|
||||
end:
|
||||
row: 71
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: B
|
||||
location:
|
||||
row: 75
|
||||
column: 12
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 75
|
||||
column: 10
|
||||
end:
|
||||
row: 75
|
||||
column: 18
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: B
|
||||
location:
|
||||
row: 79
|
||||
column: 9
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 79
|
||||
column: 9
|
||||
end:
|
||||
row: 79
|
||||
column: 17
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: B
|
||||
location:
|
||||
row: 84
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 84
|
||||
column: 5
|
||||
end:
|
||||
row: 85
|
||||
column: 5
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: B
|
||||
location:
|
||||
row: 92
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 91
|
||||
column: 6
|
||||
end:
|
||||
row: 92
|
||||
column: 11
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: B
|
||||
location:
|
||||
row: 98
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 98
|
||||
column: 5
|
||||
end:
|
||||
row: 100
|
||||
column: 5
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: B
|
||||
location:
|
||||
row: 108
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 107
|
||||
column: 6
|
||||
end:
|
||||
row: 108
|
||||
column: 11
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 114
|
||||
column: 13
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 114
|
||||
column: 12
|
||||
end:
|
||||
row: 114
|
||||
column: 20
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 119
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 118
|
||||
column: 8
|
||||
end:
|
||||
row: 120
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 125
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 124
|
||||
column: 8
|
||||
end:
|
||||
row: 126
|
||||
column: 2
|
||||
applied: false
|
||||
- kind:
|
||||
UselessObjectInheritance: A
|
||||
location:
|
||||
row: 131
|
||||
column: 5
|
||||
fix:
|
||||
content: ""
|
||||
start:
|
||||
row: 130
|
||||
column: 8
|
||||
end:
|
||||
row: 133
|
||||
column: 2
|
||||
applied: false
|
||||
|
||||
31
src/snapshots/ruff__linter__tests__r002.snap
Normal file
31
src/snapshots/ruff__linter__tests__r002.snap
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: NoAssertEquals
|
||||
location:
|
||||
row: 1
|
||||
column: 5
|
||||
fix:
|
||||
content: assertEqual
|
||||
start:
|
||||
row: 1
|
||||
column: 6
|
||||
end:
|
||||
row: 1
|
||||
column: 18
|
||||
applied: false
|
||||
- kind: NoAssertEquals
|
||||
location:
|
||||
row: 2
|
||||
column: 5
|
||||
fix:
|
||||
content: assertEqual
|
||||
start:
|
||||
row: 2
|
||||
column: 6
|
||||
end:
|
||||
row: 2
|
||||
column: 18
|
||||
applied: false
|
||||
|
||||
Reference in New Issue
Block a user