Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
651f6b6bce | ||
|
|
d3c3198b24 | ||
|
|
ec6054edce | ||
|
|
a0df78cb7d | ||
|
|
ac41c33d1f | ||
|
|
b4b8782243 | ||
|
|
8e53a4d1d3 | ||
|
|
668860cba3 | ||
|
|
038e8cfba0 | ||
|
|
ebfa55cea3 | ||
|
|
aa0fc0f9c2 | ||
|
|
aa85c81280 | ||
|
|
f5fd6f59ea | ||
|
|
540e31f5f4 | ||
|
|
8136cc9238 | ||
|
|
2c71535016 | ||
|
|
cce8fb9882 | ||
|
|
9e59c99133 | ||
|
|
b032f50775 | ||
|
|
1c2fc38853 | ||
|
|
f16f3a4a03 | ||
|
|
30a09ec211 | ||
|
|
ec7b25290b | ||
|
|
68422d4ff2 | ||
|
|
2abaffd65b | ||
|
|
06cbf5a2ae | ||
|
|
f432ce291a | ||
|
|
1eb331143d | ||
|
|
db1b1672b8 | ||
|
|
6861e59103 | ||
|
|
778c644ee3 | ||
|
|
e66a6b6d05 | ||
|
|
faea478ca5 | ||
|
|
39b5fa0e24 | ||
|
|
df413d1ece | ||
|
|
cfd0693ae5 | ||
|
|
56ad160c05 | ||
|
|
9d8c6ba671 | ||
|
|
1ea88ea56b | ||
|
|
7f44ffb55c | ||
|
|
dbd640d90f | ||
|
|
e5082c7d6c | ||
|
|
841d176289 | ||
|
|
c15595325c | ||
|
|
e97b1a4280 | ||
|
|
82ec884a61 | ||
|
|
7c1a6bce7b | ||
|
|
84a8b628b8 | ||
|
|
142b627bb8 | ||
|
|
fbf231e1b8 | ||
|
|
1dd9ccf7f6 | ||
|
|
d601abe01b | ||
|
|
15d4774b6b | ||
|
|
293c7e00d5 | ||
|
|
c3a3195922 | ||
|
|
39d98d3488 | ||
|
|
cd3d82213a | ||
|
|
a9a0026f2f | ||
|
|
da4618d77b | ||
|
|
1b0748d19d | ||
|
|
0b7fa64481 | ||
|
|
09d593b124 | ||
|
|
adc134ced0 | ||
|
|
6051a0c1c8 | ||
|
|
00495e8620 | ||
|
|
ad8693e3de | ||
|
|
69e20c4554 | ||
|
|
b5816634b3 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
||||
resources/test/cpython
|
||||
docs/
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
|
||||
###
|
||||
# Rust.gitignore
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.238
|
||||
rev: v0.0.240
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
|
||||
@@ -37,7 +37,7 @@ will enable all `F` rules, including `F401`, as the command line's `--select` re
|
||||
The `remove-six-compat` rule has been removed. This rule was only useful for one-time Python 2-to-3
|
||||
upgrades.
|
||||
|
||||
## 0.0.238
|
||||
## 0.0.237
|
||||
|
||||
### `--explain`, `--clean`, and `--generate-shell-completion` are now subcommands ([#2190](https://github.com/charliermarsh/ruff/pull/2190))
|
||||
|
||||
|
||||
@@ -4,10 +4,14 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
|
||||
|
||||
## The basics
|
||||
|
||||
Ruff welcomes contributions in the form of Pull Requests. For small changes (e.g., bug fixes), feel
|
||||
free to submit a PR. For larger changes (e.g., new lint rules, new functionality, new configuration
|
||||
options), consider submitting an [Issue](https://github.com/charliermarsh/ruff/issues) outlining
|
||||
your proposed change.
|
||||
Ruff welcomes contributions in the form of Pull Requests.
|
||||
|
||||
For small changes (e.g., bug fixes), feel free to submit a PR.
|
||||
|
||||
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
|
||||
creating an [**issue**](https://github.com/charliermarsh/ruff/issues) outlining your proposed
|
||||
change. You can also join us on [**Discord**](https://discord.gg/Z8KbeK24) to discuss your idea with
|
||||
the community.
|
||||
|
||||
If you're looking for a place to start, we recommend implementing a new lint rule (see:
|
||||
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
|
||||
@@ -50,7 +54,14 @@ cargo test --all # Testing...
|
||||
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
|
||||
will save you time and expedite the merge process.
|
||||
|
||||
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
|
||||
Note that many code changes also require updating the snapshot tests, which is done interactively
|
||||
after running `cargo test` like so:
|
||||
|
||||
```shell
|
||||
cargo insta review
|
||||
```
|
||||
|
||||
If you have `pre-commit` [installed](https://pre-commit.com/#installation) then you can use it to
|
||||
assist with formatting and linting. The following command will run the `pre-commit` hooks:
|
||||
|
||||
```shell
|
||||
|
||||
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -750,7 +750,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -1922,7 +1922,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
@@ -1977,7 +1977,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2014,7 +2014,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.4",
|
||||
@@ -2035,7 +2035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -46,7 +46,7 @@ num-traits = "0.2.15"
|
||||
once_cell = { version = "1.16.0" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ruff_macros = { version = "0.0.238", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.240", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "4f38cb68e4a97aeea9eb19673803a0bd5f655383" }
|
||||
@@ -77,7 +77,7 @@ wasm-bindgen = { version = "0.2.83" }
|
||||
is_executable = "1.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.19.1", features = ["yaml", "redactions"] }
|
||||
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
|
||||
test-case = { version = "2.2.2" }
|
||||
wasm-bindgen-test = { version = "0.3.33" }
|
||||
|
||||
|
||||
30
LICENSE
30
LICENSE
@@ -1005,3 +1005,33 @@ are:
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
- flake8-raise, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Jon Dufresne
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-self, licensed as follows:
|
||||
"""
|
||||
Freely Distributable
|
||||
"""
|
||||
|
||||
2964
flake8_to_ruff/Cargo.lock
generated
2964
flake8_to_ruff/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -23,6 +23,7 @@ theme:
|
||||
toggle:
|
||||
icon: material/weather-night
|
||||
name: Switch to light mode
|
||||
custom_dir: .overrides
|
||||
repo_url: https://github.com/charliermarsh/ruff
|
||||
repo_name: ruff
|
||||
site_author: charliermarsh
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡</text></svg>"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||
<script src="https://cdn.usefathom.com/script.js" data-site="XWUDIXNB" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
authors = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
|
||||
@@ -4,6 +4,7 @@ d = {}
|
||||
safe = "s3cr3t"
|
||||
password = True
|
||||
password = safe
|
||||
password = ""
|
||||
password is True
|
||||
password == 1
|
||||
d["safe"] = "s3cr3t"
|
||||
|
||||
@@ -7,6 +7,7 @@ string = "Hello World"
|
||||
# OK
|
||||
func("s3cr3t")
|
||||
func(1, password=string)
|
||||
func(1, password="")
|
||||
func(pos="s3cr3t", password=string)
|
||||
|
||||
# Error
|
||||
|
||||
@@ -28,3 +28,7 @@ def ok_all(first, /, pos, default="posonly", *, kwonly="kwonly"):
|
||||
|
||||
def default_all(first, /, pos, secret="posonly", *, password="kwonly"):
|
||||
pass
|
||||
|
||||
|
||||
def ok_empty(first, password=""):
|
||||
pass
|
||||
|
||||
0
resources/test/fixtures/flake8_no_pep420/test_pass_pyi/example.pyi
vendored
Normal file
0
resources/test/fixtures/flake8_no_pep420/test_pass_pyi/example.pyi
vendored
Normal file
@@ -1,2 +1,4 @@
|
||||
this_should_be_linted = "double quote string"
|
||||
this_should_be_linted = u"double quote string"
|
||||
this_should_be_linted = f"double quote string"
|
||||
this_should_be_linted = f"double {'quote'} string"
|
||||
|
||||
@@ -4,3 +4,8 @@ this_is_fine = '"This" is a \'string\''
|
||||
this_is_fine = "This is a 'string'"
|
||||
this_is_fine = "\"This\" is a 'string'"
|
||||
this_is_fine = r'This is a \'string\''
|
||||
this_is_fine = R'This is a \'string\''
|
||||
this_should_raise = (
|
||||
'This is a'
|
||||
'\'string\''
|
||||
)
|
||||
|
||||
27
resources/test/fixtures/flake8_quotes/doubles_implicit.py
vendored
Normal file
27
resources/test/fixtures/flake8_quotes/doubles_implicit.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
x = (
|
||||
"This"
|
||||
"is"
|
||||
"not"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This" \
|
||||
"is" \
|
||||
"not"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This"
|
||||
"is 'actually'"
|
||||
"fine"
|
||||
)
|
||||
|
||||
x = (
|
||||
"This" \
|
||||
"is 'actually'" \
|
||||
"fine"
|
||||
)
|
||||
|
||||
if True:
|
||||
"This can use 'double' quotes"
|
||||
"But this needs to be changed"
|
||||
@@ -1,2 +1,4 @@
|
||||
this_should_be_linted = 'single quote string'
|
||||
this_should_be_linted = u'double quote string'
|
||||
this_should_be_linted = f'double quote string'
|
||||
this_should_be_linted = f'double {"quote"} string'
|
||||
|
||||
@@ -3,3 +3,8 @@ this_is_fine = "'This' is a \"string\""
|
||||
this_is_fine = 'This is a "string"'
|
||||
this_is_fine = '\'This\' is a "string"'
|
||||
this_is_fine = r"This is a \"string\""
|
||||
this_is_fine = R"This is a \"string\""
|
||||
this_should_raise = (
|
||||
"This is a"
|
||||
"\"string\""
|
||||
)
|
||||
|
||||
27
resources/test/fixtures/flake8_quotes/singles_implicit.py
vendored
Normal file
27
resources/test/fixtures/flake8_quotes/singles_implicit.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
x = (
|
||||
'This'
|
||||
'is'
|
||||
'not'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This' \
|
||||
'is' \
|
||||
'not'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This'
|
||||
'is "actually"'
|
||||
'fine'
|
||||
)
|
||||
|
||||
x = (
|
||||
'This' \
|
||||
'is "actually"' \
|
||||
'fine'
|
||||
)
|
||||
|
||||
if True:
|
||||
'This can use "single" quotes'
|
||||
'But this needs to be changed'
|
||||
15
resources/test/fixtures/flake8_raise/RSE102.py
vendored
Normal file
15
resources/test/fixtures/flake8_raise/RSE102.py
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
try:
|
||||
y = 6 + "7"
|
||||
except TypeError:
|
||||
raise ValueError() # RSE102
|
||||
|
||||
try:
|
||||
x = 1 / 0
|
||||
except ZeroDivisionError:
|
||||
raise
|
||||
|
||||
raise TypeError() # RSE102
|
||||
|
||||
raise AssertionError
|
||||
|
||||
raise AttributeError("test message")
|
||||
31
resources/test/fixtures/flake8_self/SLF001.py
vendored
Normal file
31
resources/test/fixtures/flake8_self/SLF001.py
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
class Foo:
|
||||
|
||||
def __init__(self):
|
||||
self.public_thing = "foo"
|
||||
self._private_thing = "bar"
|
||||
self.__really_private_thing = "baz"
|
||||
|
||||
def __str__(self):
|
||||
return "foo"
|
||||
|
||||
def public_func(self):
|
||||
pass
|
||||
|
||||
def _private_func(self):
|
||||
pass
|
||||
|
||||
def __really_private_func(self, arg):
|
||||
pass
|
||||
|
||||
|
||||
foo = Foo()
|
||||
|
||||
print(foo.public_thing)
|
||||
print(foo.public_func())
|
||||
print(foo.__dict__)
|
||||
print(foo.__str__())
|
||||
|
||||
print(foo._private_thing) # SLF001
|
||||
print(foo.__really_private_thing) # SLF001
|
||||
print(foo._private_func()) # SLF001
|
||||
print(foo.__really_private_func(1)) # SLF001
|
||||
@@ -21,3 +21,10 @@ if isinstance(a, int) and isinstance(b, bool) or isinstance(a, float):
|
||||
|
||||
if isinstance(a, bool) or isinstance(b, str):
|
||||
pass
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def isinstance(a, b):
|
||||
return False
|
||||
if isinstance(a, int) or isinstance(a, float):
|
||||
pass
|
||||
|
||||
@@ -6,6 +6,14 @@ def f():
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103
|
||||
if a == b:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103
|
||||
if a:
|
||||
@@ -50,3 +58,29 @@ def f():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
if a:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
if a:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def bool():
|
||||
return False
|
||||
if a:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -54,7 +54,7 @@ else:
|
||||
randbytes = _get_random_bytes
|
||||
|
||||
|
||||
# OK (includes comments)
|
||||
# SIM108 (without fix due to comments)
|
||||
if x > 0:
|
||||
# test test
|
||||
abc = x
|
||||
@@ -93,8 +93,20 @@ if True:
|
||||
b = ddddddddddddddddddddddddddddddddddddd
|
||||
|
||||
|
||||
# OK (trailing comments)
|
||||
# SIM108 (without fix due to trailing comment)
|
||||
if True:
|
||||
exitcode = 0
|
||||
else:
|
||||
exitcode = 1 # Trailing comment
|
||||
|
||||
|
||||
# SIM108
|
||||
if True: x = 3 # Foo
|
||||
else: x = 5
|
||||
|
||||
|
||||
# SIM108
|
||||
if True: # Foo
|
||||
x = 3
|
||||
else:
|
||||
x = 5
|
||||
|
||||
@@ -115,3 +115,43 @@ def f():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def any(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def all(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM110
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -115,3 +115,43 @@ def f():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def any(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
def all(exp):
|
||||
pass
|
||||
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM110
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
# SIM111
|
||||
for x in iterable:
|
||||
if check(x):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -5,3 +5,10 @@ a = True if b != c else False # SIM210
|
||||
a = True if b + c else False # SIM210
|
||||
|
||||
a = False if b else True # OK
|
||||
|
||||
def f():
|
||||
# OK
|
||||
def bool():
|
||||
return False
|
||||
|
||||
a = True if b else False
|
||||
|
||||
@@ -10,6 +10,7 @@ YODA == age # SIM300
|
||||
YODA > age # SIM300
|
||||
YODA >= age # SIM300
|
||||
JediOrder.YODA == age # SIM300
|
||||
0 < (number - 100) # SIM300
|
||||
|
||||
# OK
|
||||
compare == "yoda"
|
||||
@@ -24,3 +25,4 @@ age < YODA
|
||||
age <= YODA
|
||||
YODA == YODA
|
||||
age == JediOrder.YODA
|
||||
(number - 100) > 0
|
||||
|
||||
@@ -4,15 +4,22 @@ if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
|
||||
|
||||
if False:
|
||||
pass # TCH005
|
||||
|
||||
if 0:
|
||||
pass # TCH005
|
||||
|
||||
|
||||
def example():
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
pass # TCH005
|
||||
return
|
||||
|
||||
|
||||
class Test:
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
pass # TCH005
|
||||
x = 2
|
||||
|
||||
|
||||
@@ -23,3 +30,10 @@ if TYPE_CHECKING:
|
||||
|
||||
if TYPE_CHECKING:
|
||||
x: List
|
||||
|
||||
|
||||
if False:
|
||||
x: List
|
||||
|
||||
if 0:
|
||||
x: List
|
||||
|
||||
8
resources/test/fixtures/isort/forced_separate.py
vendored
Normal file
8
resources/test/fixtures/isort/forced_separate.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# office_helper and tests are both first-party,
|
||||
# but we want tests and experiments to be separated, in that order
|
||||
from office_helper.core import CoreState
|
||||
import tests.common.foo as tcf
|
||||
from tests.common import async_mock_service
|
||||
from experiments.starry import *
|
||||
from experiments.weird import varieties
|
||||
from office_helper.assistants import entity_registry as er
|
||||
13
resources/test/fixtures/isort/lines_after_imports_class_after.py
vendored
Normal file
13
resources/test/fixtures/isort/lines_after_imports_class_after.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
class Thing(object):
|
||||
name: str
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
22
resources/test/fixtures/isort/lines_after_imports_func_after.py
vendored
Normal file
22
resources/test/fixtures/isort/lines_after_imports_func_after.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
my_local_folder_object.get()
|
||||
9
resources/test/fixtures/isort/lines_after_imports_nothing_after.py
vendored
Normal file
9
resources/test/fixtures/isort/lines_after_imports_nothing_after.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from requests import Session
|
||||
|
||||
from my_first_party import my_first_party_object
|
||||
|
||||
from . import my_local_folder_object
|
||||
13
resources/test/fixtures/isort/preserve_tabs_2.py
vendored
Normal file
13
resources/test/fixtures/isort/preserve_tabs_2.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from numpy import (
|
||||
cos,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
sin,
|
||||
tan,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
)
|
||||
3
resources/test/fixtures/isort/pyproject.toml
vendored
3
resources/test/fixtures/isort/pyproject.toml
vendored
@@ -1,2 +1,5 @@
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.isort]
|
||||
lines-after-imports = 3
|
||||
|
||||
8
resources/test/fixtures/pyflakes/F841_0.py
vendored
8
resources/test/fixtures/pyflakes/F841_0.py
vendored
@@ -86,3 +86,11 @@ def f():
|
||||
open("") as ((this, that)),
|
||||
):
|
||||
print("hello")
|
||||
|
||||
|
||||
def f():
|
||||
exponential, base_multiplier = 1, 2
|
||||
hash_map = {
|
||||
(exponential := (exponential * base_multiplier) % 3): i + 1 for i in range(2)
|
||||
}
|
||||
return hash_map
|
||||
|
||||
56
resources/test/fixtures/pylint/too_many_statements.py
vendored
Normal file
56
resources/test/fixtures/pylint/too_many_statements.py
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
def f(): # OK
|
||||
return
|
||||
|
||||
|
||||
async def f(): # Too many statements (52/50)
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
3
resources/test/fixtures/pylint/too_many_statements_params.py
vendored
Normal file
3
resources/test/fixtures/pylint/too_many_statements_params.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Too may statements (2/1) for max_statements=1
|
||||
def f(x):
|
||||
pass
|
||||
50
resources/test/fixtures/pyupgrade/UP035.py
vendored
Normal file
50
resources/test/fixtures/pyupgrade/UP035.py
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# UP035
|
||||
from collections import Mapping
|
||||
|
||||
from collections import Mapping as MAP
|
||||
|
||||
from collections import Mapping, Sequence
|
||||
|
||||
from collections import Counter, Mapping
|
||||
|
||||
from collections import (Counter, Mapping)
|
||||
|
||||
from collections import (Counter,
|
||||
Mapping)
|
||||
|
||||
from collections import Counter, \
|
||||
Mapping
|
||||
|
||||
from collections import Counter, Mapping, Sequence
|
||||
|
||||
from collections import Mapping as mapping, Counter
|
||||
|
||||
if True:
|
||||
from collections import Mapping, Counter
|
||||
|
||||
if True:
|
||||
if True:
|
||||
pass
|
||||
from collections import Mapping, Counter
|
||||
|
||||
if True: from collections import Mapping
|
||||
|
||||
import os
|
||||
from collections import Counter, Mapping
|
||||
import sys
|
||||
|
||||
if True:
|
||||
from collections import (
|
||||
Mapping,
|
||||
Callable,
|
||||
Bad,
|
||||
Good,
|
||||
)
|
||||
|
||||
from typing import Callable, Match, Pattern, List
|
||||
|
||||
if True: from collections import (
|
||||
Mapping, Counter)
|
||||
|
||||
# OK
|
||||
from a import b
|
||||
180
resources/test/fixtures/pyupgrade/UP036_0.py
vendored
Normal file
180
resources/test/fixtures/pyupgrade/UP036_0.py
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
if True:
|
||||
print("py2!")
|
||||
else:
|
||||
print("???")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0): print("PY2!")
|
||||
else: print("PY3!")
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
print("PY2")
|
||||
else:
|
||||
print("PY3")
|
||||
|
||||
if sys.version_info < (3,0): print(1 if True else 3)
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f():
|
||||
print("py2")
|
||||
else:
|
||||
def f():
|
||||
print("py3")
|
||||
print("This the next")
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
|
||||
x = 1
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
# ohai
|
||||
|
||||
x = 1
|
||||
|
||||
if sys.version_info > (3,0): print("py3")
|
||||
else: print("py2")
|
||||
|
||||
if sys.version_info > (3,):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print("py3")
|
||||
else:
|
||||
print("py2")
|
||||
|
||||
if sys.version_info < (3,):
|
||||
print("py2")
|
||||
else:
|
||||
print("py3")
|
||||
|
||||
def f():
|
||||
if sys.version_info < (3,0):
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
pass
|
||||
else:
|
||||
yield
|
||||
|
||||
|
||||
class C:
|
||||
def g():
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f(py2):
|
||||
pass
|
||||
else:
|
||||
def f(py3):
|
||||
pass
|
||||
|
||||
def h():
|
||||
pass
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
# comment
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
def f():
|
||||
print("py2")
|
||||
def g():
|
||||
print("py2")
|
||||
else:
|
||||
def f():
|
||||
print("py3")
|
||||
def g():
|
||||
print("py3")
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print(3)
|
||||
# comment
|
||||
print(2+3)
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,): print(3)
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
|
||||
if True:
|
||||
if sys.version_info <= (3, 0):
|
||||
expected_error = []
|
||||
else:
|
||||
expected_error = [
|
||||
"<stdin>:1:5: Generator expression must be parenthesized",
|
||||
"max(1 for i in range(10), key=lambda x: x+1)",
|
||||
" ^",
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info <= (3, 0):
|
||||
expected_error = []
|
||||
else:
|
||||
expected_error = [
|
||||
"<stdin>:1:5: Generator expression must be parenthesized",
|
||||
"max(1 for i in range(10), key=lambda x: x+1)",
|
||||
" ^",
|
||||
]
|
||||
|
||||
|
||||
if sys.version_info > (3,0):
|
||||
"""this
|
||||
is valid"""
|
||||
|
||||
"""the indentation on
|
||||
this line is significant"""
|
||||
|
||||
"this is" \
|
||||
"allowed too"
|
||||
|
||||
("so is"
|
||||
"this for some reason")
|
||||
|
||||
if sys.version_info > (3, 0): expected_error = \
|
||||
[]
|
||||
|
||||
if sys.version_info > (3, 0): expected_error = []
|
||||
|
||||
if sys.version_info > (3, 0): \
|
||||
expected_error = []
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): expected_error = \
|
||||
[]
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): expected_error = []
|
||||
|
||||
if True:
|
||||
if sys.version_info > (3, 0): \
|
||||
expected_error = []
|
||||
76
resources/test/fixtures/pyupgrade/UP036_1.py
vendored
Normal file
76
resources/test/fixtures/pyupgrade/UP036_1.py
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info == 2:
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info < (3,):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
2
|
||||
else:
|
||||
3
|
||||
|
||||
if sys.version_info == 3:
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if sys.version_info > (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
from sys import version_info
|
||||
|
||||
if version_info > (3,):
|
||||
3
|
||||
else:
|
||||
2
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info < (3,0):
|
||||
print(2)
|
||||
else:
|
||||
print(3)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
def f():
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info < (3,0):
|
||||
print(2)
|
||||
else:
|
||||
print(3)
|
||||
|
||||
def f():
|
||||
if True:
|
||||
print(1)
|
||||
elif sys.version_info > (3,):
|
||||
print(3)
|
||||
62
resources/test/fixtures/pyupgrade/UP036_2.py
vendored
Normal file
62
resources/test/fixtures/pyupgrade/UP036_2.py
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
import sys
|
||||
from sys import version_info
|
||||
|
||||
if sys.version_info > (3, 5):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if version_info > (3, 5):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if sys.version_info >= (3,6):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if version_info >= (3,6):
|
||||
3+6
|
||||
else:
|
||||
3-5
|
||||
|
||||
if sys.version_info < (3,6):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info <= (3,5):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info <= (3, 5):
|
||||
3-5
|
||||
else:
|
||||
3+6
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
|
||||
if sys.version_info > (3,):
|
||||
pass
|
||||
elif False:
|
||||
pass
|
||||
|
||||
if sys.version_info[0] > "2":
|
||||
3
|
||||
else:
|
||||
2
|
||||
24
resources/test/fixtures/pyupgrade/UP036_3.py
vendored
Normal file
24
resources/test/fixtures/pyupgrade/UP036_3.py
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
for item in range(10):
|
||||
print(f"PY2-{item}")
|
||||
else :
|
||||
print("py3")
|
||||
for item in range(10):
|
||||
print(f"PY3-{item}")
|
||||
|
||||
if False:
|
||||
if sys.version_info < (3,0):
|
||||
print("py2")
|
||||
for item in range(10):
|
||||
print(f"PY2-{item}")
|
||||
else :
|
||||
print("py3")
|
||||
for item in range(10):
|
||||
print(f"PY3-{item}")
|
||||
|
||||
|
||||
if sys.version_info < (3,0): print("PY2!")
|
||||
else : print("PY3!")
|
||||
45
resources/test/fixtures/pyupgrade/UP036_4.py
vendored
Normal file
45
resources/test/fixtures/pyupgrade/UP036_4.py
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
import sys
|
||||
|
||||
if True:
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
|
||||
if True:
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if True:
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
elif foo:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
|
||||
if foo:
|
||||
pass
|
||||
elif sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
else:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
else:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
cmd = [sys.executable, "-m", "test.regrtest"]
|
||||
elif foo:
|
||||
cmd = [sys.executable, "-m", "test", "-j0"]
|
||||
2
resources/test/fixtures/ruff/RUF100_0.py
vendored
2
resources/test/fixtures/ruff/RUF100_0.py
vendored
@@ -86,3 +86,5 @@ import shelve # noqa: RUF100
|
||||
import sys # noqa: F401, RUF100
|
||||
|
||||
print(sys.path)
|
||||
|
||||
"shape: (6,)\nSeries: '' [duration[μs]]\n[\n\t0µs\n\t1µs\n\t2µs\n\t3µs\n\t4µs\n\t5µs\n]" # noqa: F401
|
||||
|
||||
@@ -768,7 +768,7 @@
|
||||
]
|
||||
},
|
||||
"docstring-quotes": {
|
||||
"description": "Quote style to prefer for docstrings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for docstrings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -779,7 +779,7 @@
|
||||
]
|
||||
},
|
||||
"inline-quotes": {
|
||||
"description": "Quote style to prefer for inline strings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for inline strings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -790,7 +790,7 @@
|
||||
]
|
||||
},
|
||||
"multiline-quotes": {
|
||||
"description": "Quote style to prefer for multiline strings (either \"single\" (`'`) or \"double\" (`\"`)).",
|
||||
"description": "Quote style to prefer for multiline strings (either \"single\" or \"double\").",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Quote"
|
||||
@@ -937,6 +937,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"forced-separate": {
|
||||
"description": "A list of modules to separate into auxiliary block(s) of imports, in the order specified.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"known-first-party": {
|
||||
"description": "A list of modules to consider first-party, regardless of whether they can be identified as such via introspection of the local filesystem.",
|
||||
"type": [
|
||||
@@ -957,6 +967,14 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"lines-after-imports": {
|
||||
"description": "The number of blank lines to place after imports. -1 for automatic determination.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int"
|
||||
},
|
||||
"no-lines-before": {
|
||||
"description": "A list of sections that should _not_ be delineated from the previous section via empty lines.",
|
||||
"type": [
|
||||
@@ -1102,7 +1120,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keep-runtime-typing": {
|
||||
"description": "Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively.",
|
||||
"description": "Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) rewrites even if a file imports `from __future__ import annotations`. Note that this setting is only applicable when the target Python version is below 3.9 and 3.10 respectively, and enabling it is equivalent to disabling `use-pep585-annotation` (`UP006`) and `use-pep604-annotation` (`UP007`) entirely.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -1115,7 +1133,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ignore-overlong-task-comments": {
|
||||
"description": "Whether or not line-length violations (`E501`) should be triggered for comments starting with `task-tags` (by default: [\"TODO\", \"FIXME\", and \"XXX\"]).",
|
||||
"description": "Whether line-length violations (`E501`) should be triggered for comments starting with `task-tags` (by default: [\"TODO\", \"FIXME\", and \"XXX\"]).",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
@@ -1171,6 +1189,15 @@
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"max-statements": {
|
||||
"description": "Maximum number of statements allowed for a method or a statement (see: `PLR0915`).",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -1188,14 +1215,14 @@
|
||||
"Quote": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Use single quotes (`'`).",
|
||||
"description": "Use single quotes.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"single"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Use double quotes (`\"`).",
|
||||
"description": "Use double quotes.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"double"
|
||||
@@ -1643,6 +1670,7 @@
|
||||
"PLR09",
|
||||
"PLR091",
|
||||
"PLR0913",
|
||||
"PLR0915",
|
||||
"PLR1",
|
||||
"PLR17",
|
||||
"PLR170",
|
||||
@@ -1739,6 +1767,10 @@
|
||||
"RET506",
|
||||
"RET507",
|
||||
"RET508",
|
||||
"RSE",
|
||||
"RSE1",
|
||||
"RSE10",
|
||||
"RSE102",
|
||||
"RUF",
|
||||
"RUF0",
|
||||
"RUF00",
|
||||
@@ -1816,6 +1848,10 @@
|
||||
"SIM4",
|
||||
"SIM40",
|
||||
"SIM401",
|
||||
"SLF",
|
||||
"SLF0",
|
||||
"SLF00",
|
||||
"SLF001",
|
||||
"T",
|
||||
"T1",
|
||||
"T10",
|
||||
@@ -1892,6 +1928,8 @@
|
||||
"UP032",
|
||||
"UP033",
|
||||
"UP034",
|
||||
"UP035",
|
||||
"UP036",
|
||||
"W",
|
||||
"W2",
|
||||
"W29",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -58,20 +58,23 @@ fn inner_main() -> Result<ExitCode> {
|
||||
log_level_args,
|
||||
} = Args::parse_from(args);
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
eprintln!(
|
||||
r#"
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
eprintln!(
|
||||
r#"
|
||||
{}: `ruff` crashed. This indicates a bug in `ruff`. If you could open an issue at:
|
||||
|
||||
https://github.com/charliermarsh/ruff/issues/new?title=%5BPanic%5D
|
||||
|
||||
quoting the executed command, along with the relevant file contents and `pyproject.toml` settings, we'd be very appreciative!
|
||||
"#,
|
||||
"error".red().bold(),
|
||||
);
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
"error".red().bold(),
|
||||
);
|
||||
default_panic_hook(info);
|
||||
}));
|
||||
}
|
||||
|
||||
let log_level: LogLevel = (&log_level_args).into();
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
@@ -7,7 +7,7 @@ use annotate_snippets::display_list::{DisplayList, FormatOptions};
|
||||
use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation};
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use itertools::iterate;
|
||||
use itertools::{iterate, Itertools};
|
||||
use ruff::fs::relativize_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
@@ -344,13 +344,16 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
|
||||
pub fn write_statistics(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
let mut violations = diagnostics
|
||||
let violations = diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.map(|message| message.kind.rule())
|
||||
.sorted()
|
||||
.dedup()
|
||||
.collect::<Vec<_>>();
|
||||
violations.sort();
|
||||
violations.dedup();
|
||||
if violations.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let statistics = violations
|
||||
.iter()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -5,6 +5,8 @@ use anyhow::Result;
|
||||
use ruff::settings::options::Options;
|
||||
use schemars::schema_for;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub struct Args {
|
||||
/// Write the generated table to stdout (rather than to `ruff.schema.json`).
|
||||
@@ -19,10 +21,7 @@ pub fn main(args: &Args) -> Result<()> {
|
||||
if args.dry_run {
|
||||
println!("{schema_string}");
|
||||
} else {
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("ruff.schema.json");
|
||||
let file = PathBuf::from(ROOT_DIR).join("ruff.schema.json");
|
||||
fs::write(file, schema_string.as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -30,6 +30,8 @@ mod utils;
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
const ROOT_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../");
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(propagate_version = true)]
|
||||
|
||||
@@ -5,12 +5,11 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
pub fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> {
|
||||
// Read the existing file.
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("README.md");
|
||||
let file = PathBuf::from(ROOT_DIR).join("README.md");
|
||||
let existing = fs::read_to_string(&file)?;
|
||||
|
||||
// Extract the prefix.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.238"
|
||||
version = "0.0.240"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -52,6 +52,7 @@ pub fn expand<'a>(
|
||||
::strum_macros::EnumIter,
|
||||
::strum_macros::EnumString,
|
||||
::strum_macros::AsRefStr,
|
||||
::strum_macros::IntoStaticStr,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
|
||||
@@ -15,16 +15,31 @@ SECTIONS: list[tuple[str, str]] = [
|
||||
("FAQ", "faq.md"),
|
||||
]
|
||||
|
||||
DOCUMENTATION_LINK: str = (
|
||||
"This README is also available as [documentation](https://beta.ruff.rs/docs/)."
|
||||
)
|
||||
|
||||
FATHOM_SCRIPT: str = (
|
||||
'<script src="https://cdn.usefathom.com/script.js" data-site="DUAEBFLB" defer>'
|
||||
"</script>"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Generate an MkDocs-compatible `docs` and `mkdocs.yml`."""
|
||||
with Path("README.md").open(encoding="utf8") as fp:
|
||||
content = fp.read()
|
||||
|
||||
# Remove the documentation link, since we're _in_ the docs.
|
||||
if DOCUMENTATION_LINK not in content:
|
||||
msg = "README.md is not in the expected format."
|
||||
raise ValueError(msg)
|
||||
content = content.replace(DOCUMENTATION_LINK, "")
|
||||
|
||||
Path("docs").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Split the README.md into sections.
|
||||
for (title, filename) in SECTIONS:
|
||||
for title, filename in SECTIONS:
|
||||
with Path(f"docs/{filename}").open("w+") as f:
|
||||
block = content.split(f"<!-- Begin section: {title} -->")
|
||||
if len(block) != 2:
|
||||
@@ -54,6 +69,11 @@ def main() -> None:
|
||||
{"FAQ": "faq.md"},
|
||||
{"Contributing": "contributing.md"},
|
||||
]
|
||||
config["extra"] = {"analytics": {"provider": "fathom"}}
|
||||
|
||||
Path(".overrides/partials/integrations/analytics").mkdir(parents=True, exist_ok=True)
|
||||
with Path(".overrides/partials/integrations/analytics/fathom.html").open("w+") as fp:
|
||||
fp.write(FATHOM_SCRIPT)
|
||||
|
||||
with Path("mkdocs.yml").open("w+") as fp:
|
||||
yaml.safe_dump(config, fp)
|
||||
|
||||
2
setup.py
2
setup.py
@@ -22,3 +22,5 @@ sys.exit(1)
|
||||
# To be removed once GitHub catches up.
|
||||
|
||||
setup(name="ruff", install_requires=[])
|
||||
if True: a = 1; \
|
||||
b = 2
|
||||
|
||||
@@ -266,8 +266,8 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
|
||||
}
|
||||
}
|
||||
ExprKind::NamedExpr { target, value } => {
|
||||
visitor.visit_expr(target);
|
||||
visitor.visit_expr(value);
|
||||
visitor.visit_expr(target);
|
||||
}
|
||||
ExprKind::BinOp { left, op, right } => {
|
||||
visitor.visit_expr(left);
|
||||
|
||||
@@ -36,10 +36,10 @@ use crate::rules::{
|
||||
flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap,
|
||||
flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger,
|
||||
flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions, flake8_logging_format,
|
||||
flake8_pie, flake8_print, flake8_pytest_style, flake8_return, flake8_simplify,
|
||||
flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, flake8_use_pathlib, mccabe,
|
||||
pandas_vet, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade,
|
||||
ruff, tryceratops,
|
||||
flake8_pie, flake8_print, flake8_pytest_style, flake8_raise, flake8_return, flake8_self,
|
||||
flake8_simplify, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments,
|
||||
flake8_use_pathlib, mccabe, pandas_vet, pep8_naming, pycodestyle, pydocstyle, pyflakes,
|
||||
pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
};
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -87,6 +87,9 @@ pub struct Checker<'a> {
|
||||
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>,
|
||||
deferred_assignments: Vec<DeferralContext<'a>>,
|
||||
// Body iteration; used to peek at siblings.
|
||||
body: &'a [Stmt],
|
||||
body_index: usize,
|
||||
// Internal, derivative state.
|
||||
visible_scope: VisibleScope,
|
||||
in_annotation: bool,
|
||||
@@ -145,6 +148,9 @@ impl<'a> Checker<'a> {
|
||||
deferred_functions: vec![],
|
||||
deferred_lambdas: vec![],
|
||||
deferred_assignments: vec![],
|
||||
// Body iteration.
|
||||
body: &[],
|
||||
body_index: 0,
|
||||
// Internal, derivative state.
|
||||
visible_scope: VisibleScope {
|
||||
modifier: Modifier::Module,
|
||||
@@ -580,6 +586,21 @@ where
|
||||
pylint::rules::property_with_parameters(self, stmt, decorator_list, args);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::TooManyArgs) {
|
||||
pylint::rules::too_many_args(self, args, stmt);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::TooManyStatements) {
|
||||
if let Some(diagnostic) = pylint::rules::too_many_statements(
|
||||
stmt,
|
||||
body,
|
||||
self.settings.pylint.max_statements,
|
||||
self.locator,
|
||||
) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
@@ -696,9 +717,6 @@ where
|
||||
context,
|
||||
},
|
||||
);
|
||||
if self.settings.rules.enabled(&Rule::TooManyArgs) {
|
||||
pylint::rules::too_many_args(self, args, stmt);
|
||||
}
|
||||
}
|
||||
StmtKind::Return { .. } => {
|
||||
if self.settings.rules.enabled(&Rule::ReturnOutsideFunction) {
|
||||
@@ -1073,6 +1091,15 @@ where
|
||||
if self.settings.rules.enabled(&Rule::RewriteCElementTree) {
|
||||
pyupgrade::rules::replace_c_element_tree(self, stmt);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ImportReplacements) {
|
||||
pyupgrade::rules::import_replacements(
|
||||
self,
|
||||
stmt,
|
||||
names,
|
||||
module.as_ref().map(String::as_str),
|
||||
level.as_ref(),
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UnnecessaryBuiltinImport) {
|
||||
if let Some(module) = module.as_deref() {
|
||||
pyupgrade::rules::unnecessary_builtin_import(self, stmt, module, names);
|
||||
@@ -1425,6 +1452,15 @@ where
|
||||
tryceratops::rules::raise_vanilla_args(self, expr);
|
||||
}
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UnnecessaryParenOnRaiseException)
|
||||
{
|
||||
if let Some(expr) = exc {
|
||||
flake8_raise::rules::unnecessary_paren_on_raise_exception(self, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
StmtKind::AugAssign { target, .. } => {
|
||||
self.handle_node_load(target);
|
||||
@@ -1476,6 +1512,9 @@ where
|
||||
self.current_stmt_parent().map(Into::into),
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::OutdatedVersionBlock) {
|
||||
pyupgrade::rules::outdated_version_block(self, stmt, test, body, orelse);
|
||||
}
|
||||
}
|
||||
StmtKind::Assert { test, msg } => {
|
||||
if self.settings.rules.enabled(&Rule::AssertTuple) {
|
||||
@@ -1572,7 +1611,11 @@ where
|
||||
if self.settings.rules.enabled(&Rule::ConvertLoopToAny)
|
||||
|| self.settings.rules.enabled(&Rule::ConvertLoopToAll)
|
||||
{
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(self, stmt, None);
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(
|
||||
self,
|
||||
stmt,
|
||||
self.current_sibling_stmt(),
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::KeyInDict) {
|
||||
flake8_simplify::rules::key_in_dict_for(self, target, iter);
|
||||
@@ -1901,7 +1944,7 @@ where
|
||||
|
||||
if flake8_type_checking::helpers::is_type_checking_block(self, test) {
|
||||
if self.settings.rules.enabled(&Rule::EmptyTypeCheckingBlock) {
|
||||
flake8_type_checking::rules::empty_type_checking_block(self, test, body);
|
||||
flake8_type_checking::rules::empty_type_checking_block(self, body);
|
||||
}
|
||||
|
||||
let prev_in_type_checking_block = self.in_type_checking_block;
|
||||
@@ -1990,12 +2033,12 @@ where
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
// Ex) Optional[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
&& self.in_annotation
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.settings.rules.enabled(&Rule::UsePEP604Annotation)
|
||||
&& (self.settings.target_version >= PythonVersion::Py310
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.annotations_future_enabled))
|
||||
&& self.annotations_future_enabled
|
||||
&& self.in_annotation))
|
||||
{
|
||||
pyupgrade::rules::use_pep604_annotation(self, expr, value, slice);
|
||||
}
|
||||
@@ -2045,10 +2088,10 @@ where
|
||||
|
||||
// Ex) List[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.settings.rules.enabled(&Rule::UsePEP585Annotation)
|
||||
&& (self.settings.target_version >= PythonVersion::Py39
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.annotations_future_enabled
|
||||
&& self.in_annotation))
|
||||
&& typing::is_pep585_builtin(self, expr)
|
||||
@@ -2090,6 +2133,7 @@ where
|
||||
ExprKind::Attribute { attr, value, .. } => {
|
||||
// Ex) typing.List[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
&& !self.settings.pyupgrade.keep_runtime_typing
|
||||
&& self.settings.rules.enabled(&Rule::UsePEP585Annotation)
|
||||
&& (self.settings.target_version >= PythonVersion::Py39
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
@@ -2116,6 +2160,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::BannedApi) {
|
||||
flake8_tidy_imports::banned_api::banned_attribute_access(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::PrivateMemberAccess) {
|
||||
flake8_self::rules::private_member_access(self, expr);
|
||||
}
|
||||
pandas_vet::rules::check_attr(self, attr, value, expr);
|
||||
}
|
||||
ExprKind::Call {
|
||||
@@ -3676,19 +3723,18 @@ where
|
||||
flake8_pie::rules::no_unnecessary_pass(self, body);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::ConvertLoopToAny)
|
||||
|| self.settings.rules.enabled(&Rule::ConvertLoopToAll)
|
||||
{
|
||||
for (stmt, sibling) in body.iter().tuple_windows() {
|
||||
if matches!(stmt.node, StmtKind::For { .. })
|
||||
&& matches!(sibling.node, StmtKind::Return { .. })
|
||||
{
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(self, stmt, Some(sibling));
|
||||
}
|
||||
}
|
||||
let prev_body = self.body;
|
||||
let prev_body_index = self.body_index;
|
||||
self.body = body;
|
||||
self.body_index = 0;
|
||||
|
||||
for stmt in body {
|
||||
self.visit_stmt(stmt);
|
||||
self.body_index += 1;
|
||||
}
|
||||
|
||||
visitor::walk_body(self, body);
|
||||
self.body = prev_body;
|
||||
self.body_index = prev_body_index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3777,6 +3823,11 @@ impl<'a> Checker<'a> {
|
||||
self.exprs.iter().rev().nth(2)
|
||||
}
|
||||
|
||||
/// Return the `Stmt` that immediately follows the current `Stmt`, if any.
|
||||
pub fn current_sibling_stmt(&self) -> Option<&'a Stmt> {
|
||||
self.body.get(self.body_index + 1)
|
||||
}
|
||||
|
||||
pub fn current_scope(&self) -> &Scope {
|
||||
&self.scopes[*(self.scope_stack.last().expect("No current scope found"))]
|
||||
}
|
||||
@@ -4398,7 +4449,7 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn check_deferred_assignments(&mut self) {
|
||||
self.deferred_assignments.reverse();
|
||||
while let Some((scopes, _parents)) = self.deferred_assignments.pop() {
|
||||
while let Some((scopes, ..)) = self.deferred_assignments.pop() {
|
||||
let scope_index = scopes[scopes.len() - 1];
|
||||
let parent_scope_index = scopes[scopes.len() - 2];
|
||||
if self.settings.rules.enabled(&Rule::UnusedVariable) {
|
||||
@@ -5201,9 +5252,7 @@ pub fn check_ast(
|
||||
};
|
||||
|
||||
// Iterate over the AST.
|
||||
for stmt in python_ast {
|
||||
checker.visit_stmt(stmt);
|
||||
}
|
||||
checker.visit_body(python_ast);
|
||||
|
||||
// Check any deferred statements.
|
||||
checker.check_deferred_functions();
|
||||
|
||||
@@ -100,8 +100,11 @@ pub fn check_noqa(
|
||||
if enforce_noqa {
|
||||
for (row, (directive, matches)) in noqa_directives {
|
||||
match directive {
|
||||
Directive::All(spaces, start, end) => {
|
||||
Directive::All(spaces, start_byte, end_byte) => {
|
||||
if matches.is_empty() {
|
||||
let start = lines[row][..start_byte].chars().count();
|
||||
let end = start + lines[row][start_byte..end_byte].chars().count();
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedNOQA { codes: None },
|
||||
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
|
||||
@@ -117,7 +120,7 @@ pub fn check_noqa(
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
Directive::Codes(spaces, start, end, codes) => {
|
||||
Directive::Codes(spaces, start_byte, end_byte, codes) => {
|
||||
let mut disabled_codes = vec![];
|
||||
let mut unknown_codes = vec![];
|
||||
let mut unmatched_codes = vec![];
|
||||
@@ -153,6 +156,9 @@ pub fn check_noqa(
|
||||
&& unknown_codes.is_empty()
|
||||
&& unmatched_codes.is_empty())
|
||||
{
|
||||
let start = lines[row][..start_byte].chars().count();
|
||||
let end = start + lines[row][start_byte..end_byte].chars().count();
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::UnusedNOQA {
|
||||
codes: Some(UnusedCodes {
|
||||
|
||||
@@ -48,77 +48,73 @@ pub fn check_tokens(
|
||||
|| settings.rules.enabled(&Rule::TrailingCommaProhibited);
|
||||
let enforce_extraneous_parenthesis = settings.rules.enabled(&Rule::ExtraneousParentheses);
|
||||
|
||||
let mut state_machine = StateMachine::default();
|
||||
for &(start, ref tok, end) in tokens.iter().flatten() {
|
||||
let is_docstring = if enforce_ambiguous_unicode_character || enforce_quotes {
|
||||
state_machine.consume(tok)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if enforce_ambiguous_unicode_character
|
||||
|| enforce_commented_out_code
|
||||
|| enforce_invalid_escape_sequence
|
||||
{
|
||||
let mut state_machine = StateMachine::default();
|
||||
for &(start, ref tok, end) in tokens.iter().flatten() {
|
||||
let is_docstring = if enforce_ambiguous_unicode_character {
|
||||
state_machine.consume(tok)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
if matches!(tok, Tok::String { .. } | Tok::Comment(_)) {
|
||||
diagnostics.extend(ruff::rules::ambiguous_unicode_character(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
if is_docstring {
|
||||
Context::Docstring
|
||||
// RUF001, RUF002, RUF003
|
||||
if enforce_ambiguous_unicode_character {
|
||||
if matches!(tok, Tok::String { .. } | Tok::Comment(_)) {
|
||||
diagnostics.extend(ruff::rules::ambiguous_unicode_character(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
if is_docstring {
|
||||
Context::Docstring
|
||||
} else {
|
||||
Context::String
|
||||
}
|
||||
} else {
|
||||
Context::String
|
||||
}
|
||||
} else {
|
||||
Context::Comment
|
||||
},
|
||||
settings,
|
||||
autofix,
|
||||
));
|
||||
Context::Comment
|
||||
},
|
||||
settings,
|
||||
autofix,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flake8-quotes
|
||||
if enforce_quotes {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
if let Some(diagnostic) = flake8_quotes::rules::quotes(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
is_docstring,
|
||||
settings,
|
||||
autofix,
|
||||
) {
|
||||
if settings.rules.enabled(diagnostic.kind.rule()) {
|
||||
// eradicate
|
||||
if enforce_commented_out_code {
|
||||
if matches!(tok, Tok::Comment(_)) {
|
||||
if let Some(diagnostic) =
|
||||
eradicate::rules::commented_out_code(locator, start, end, settings, autofix)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eradicate
|
||||
if enforce_commented_out_code {
|
||||
if matches!(tok, Tok::Comment(_)) {
|
||||
if let Some(diagnostic) =
|
||||
eradicate::rules::commented_out_code(locator, start, end, settings, autofix)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
diagnostics.extend(pycodestyle::rules::invalid_escape_sequence(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::InvalidEscapeSequence),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
diagnostics.extend(pycodestyle::rules::invalid_escape_sequence(
|
||||
locator,
|
||||
start,
|
||||
end,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::InvalidEscapeSequence),
|
||||
));
|
||||
}
|
||||
}
|
||||
// Q001, Q002, Q003
|
||||
if enforce_quotes {
|
||||
diagnostics.extend(
|
||||
flake8_quotes::rules::from_tokens(tokens, locator, settings, autofix)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// ISC001, ISC002
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::{bail, Result};
|
||||
use libcst_native::{
|
||||
Call, Expr, Expression, Import, ImportFrom, Module, SmallStatement, Statement,
|
||||
Call, Comparison, Expr, Expression, Import, ImportFrom, Module, SmallStatement, Statement,
|
||||
};
|
||||
|
||||
pub fn match_module(module_text: &str) -> Result<Module> {
|
||||
@@ -34,7 +34,7 @@ pub fn match_import<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut Import
|
||||
if let Some(SmallStatement::Import(expr)) = expr.body.first_mut() {
|
||||
Ok(expr)
|
||||
} else {
|
||||
bail!("Expected SmallStatement::Expr")
|
||||
bail!("Expected SmallStatement::Import")
|
||||
}
|
||||
} else {
|
||||
bail!("Expected Statement::Simple")
|
||||
@@ -46,7 +46,7 @@ pub fn match_import_from<'a, 'b>(module: &'a mut Module<'b>) -> Result<&'a mut I
|
||||
if let Some(SmallStatement::ImportFrom(expr)) = expr.body.first_mut() {
|
||||
Ok(expr)
|
||||
} else {
|
||||
bail!("Expected SmallStatement::Expr")
|
||||
bail!("Expected SmallStatement::ImportFrom")
|
||||
}
|
||||
} else {
|
||||
bail!("Expected Statement::Simple")
|
||||
@@ -57,6 +57,16 @@ pub fn match_call<'a, 'b>(expression: &'a mut Expression<'b>) -> Result<&'a mut
|
||||
if let Expression::Call(call) = expression {
|
||||
Ok(call)
|
||||
} else {
|
||||
bail!("Expected SmallStatement::Expr")
|
||||
bail!("Expected Expression::Call")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_comparison<'a, 'b>(
|
||||
expression: &'a mut Expression<'b>,
|
||||
) -> Result<&'a mut Comparison<'b>> {
|
||||
if let Expression::Comparison(comparison) = expression {
|
||||
Ok(comparison)
|
||||
} else {
|
||||
bail!("Expected Expression::Comparison")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::external_config::ExternalConfig;
|
||||
use super::plugin::Plugin;
|
||||
@@ -38,7 +39,7 @@ pub fn convert(
|
||||
.expect("Unable to find flake8 section in INI file");
|
||||
|
||||
// Extract all referenced rule code prefixes, to power plugin inference.
|
||||
let mut referenced_codes: BTreeSet<RuleSelector> = BTreeSet::default();
|
||||
let mut referenced_codes: HashSet<RuleSelector> = HashSet::default();
|
||||
for (key, value) in flake8 {
|
||||
if let Some(value) = value {
|
||||
match key.as_str() {
|
||||
@@ -80,15 +81,15 @@ pub fn convert(
|
||||
.and_then(|value| {
|
||||
value
|
||||
.as_ref()
|
||||
.map(|value| BTreeSet::from_iter(parser::parse_prefix_codes(value)))
|
||||
.map(|value| HashSet::from_iter(parser::parse_prefix_codes(value)))
|
||||
})
|
||||
.unwrap_or_else(|| resolve_select(&plugins));
|
||||
let mut ignore = flake8
|
||||
let mut ignore: HashSet<RuleSelector> = flake8
|
||||
.get("ignore")
|
||||
.and_then(|value| {
|
||||
value
|
||||
.as_ref()
|
||||
.map(|value| BTreeSet::from_iter(parser::parse_prefix_codes(value)))
|
||||
.map(|value| HashSet::from_iter(parser::parse_prefix_codes(value)))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -349,8 +350,18 @@ pub fn convert(
|
||||
}
|
||||
|
||||
// Deduplicate and sort.
|
||||
options.select = Some(Vec::from_iter(select));
|
||||
options.ignore = Some(Vec::from_iter(ignore));
|
||||
options.select = Some(
|
||||
select
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.collect(),
|
||||
);
|
||||
options.ignore = Some(
|
||||
ignore
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.collect(),
|
||||
);
|
||||
if flake8_annotations != flake8_annotations::settings::Options::default() {
|
||||
options.flake8_annotations = Some(flake8_annotations);
|
||||
}
|
||||
@@ -414,8 +425,8 @@ pub fn convert(
|
||||
|
||||
/// Resolve the set of enabled `RuleSelector` values for the given
|
||||
/// plugins.
|
||||
fn resolve_select(plugins: &[Plugin]) -> BTreeSet<RuleSelector> {
|
||||
let mut select: BTreeSet<_> = DEFAULT_SELECTORS.iter().cloned().collect();
|
||||
fn resolve_select(plugins: &[Plugin]) -> HashSet<RuleSelector> {
|
||||
let mut select: HashSet<_> = DEFAULT_SELECTORS.iter().cloned().collect();
|
||||
select.extend(plugins.iter().map(Plugin::selector));
|
||||
select
|
||||
}
|
||||
@@ -446,7 +457,7 @@ mod tests {
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(plugins)
|
||||
.sorted()
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.collect(),
|
||||
),
|
||||
..Options::default()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -293,7 +293,7 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
///
|
||||
/// For example, if the user ignores `ANN101`, we should infer that
|
||||
/// `flake8-annotations` is active.
|
||||
pub fn infer_plugins_from_codes(selectors: &BTreeSet<RuleSelector>) -> Vec<Plugin> {
|
||||
pub fn infer_plugins_from_codes(selectors: &HashSet<RuleSelector>) -> Vec<Plugin> {
|
||||
// Ignore cases in which we've knowingly changed rule prefixes.
|
||||
[
|
||||
Plugin::Flake82020,
|
||||
|
||||
29
src/fs.rs
29
src/fs.rs
@@ -1,8 +1,10 @@
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use log::debug;
|
||||
use path_absolutize::{path_dedot, Absolutize};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
@@ -34,10 +36,28 @@ pub(crate) fn ignores_from_path<'a>(
|
||||
let (file_path, file_basename) = extract_path_names(path)?;
|
||||
Ok(pattern_code_pairs
|
||||
.iter()
|
||||
.filter(|(absolute, basename, _)| {
|
||||
basename.is_match(file_basename) || absolute.is_match(file_path)
|
||||
.filter_map(|(absolute, basename, codes)| {
|
||||
if basename.is_match(file_basename) {
|
||||
debug!(
|
||||
"Adding per-file ignores for {:?} due to basename match on {:?}: {:?}",
|
||||
path,
|
||||
basename.deref().glob().regex(),
|
||||
&**codes
|
||||
);
|
||||
return Some(codes.iter());
|
||||
}
|
||||
if absolute.is_match(file_path) {
|
||||
debug!(
|
||||
"Adding per-file ignores for {:?} due to absolute match on {:?}: {:?}",
|
||||
path,
|
||||
absolute.deref().glob().regex(),
|
||||
&**codes
|
||||
);
|
||||
return Some(codes.iter());
|
||||
}
|
||||
None
|
||||
})
|
||||
.flat_map(|(_, _, codes)| codes.iter())
|
||||
.flatten()
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -61,7 +81,8 @@ pub fn normalize_path_to<P: AsRef<Path>, R: AsRef<Path>>(path: P, project_root:
|
||||
}
|
||||
|
||||
/// Convert an absolute path to be relative to the current working directory.
|
||||
pub fn relativize_path(path: &Path) -> String {
|
||||
pub fn relativize_path(path: impl AsRef<Path>) -> String {
|
||||
let path = path.as_ref();
|
||||
if let Ok(path) = path.strip_prefix(&*path_dedot::CWD) {
|
||||
return format!("{}", path.display());
|
||||
}
|
||||
|
||||
@@ -67,3 +67,6 @@ cfg_if! {
|
||||
pub use lib_wasm::check;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
@@ -16,8 +16,6 @@ use crate::directives::Directives;
|
||||
use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
|
||||
use crate::message::{Message, Source};
|
||||
use crate::noqa::add_noqa;
|
||||
#[cfg(test)]
|
||||
use crate::packaging::detect_package_root;
|
||||
use crate::registry::{Diagnostic, LintSource, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
@@ -222,8 +220,8 @@ pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
|
||||
path,
|
||||
&diagnostics,
|
||||
&contents,
|
||||
indexer.commented_lines(),
|
||||
&directives.noqa_line_for,
|
||||
&settings.external,
|
||||
stylist.line_ending(),
|
||||
)
|
||||
}
|
||||
@@ -382,77 +380,3 @@ quoting the contents of `{}`, along with the `pyproject.toml` settings and execu
|
||||
return Ok((contents, fixed, messages));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_path(path: &Path, settings: &Settings) -> Result<Vec<Diagnostic>> {
|
||||
let contents = fs::read_file(path)?;
|
||||
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
|
||||
let locator = Locator::new(&contents);
|
||||
let stylist = Stylist::from_contents(&contents, &locator);
|
||||
let indexer: Indexer = tokens.as_slice().into();
|
||||
let directives =
|
||||
directives::extract_directives(&tokens, directives::Flags::from_settings(settings));
|
||||
let mut diagnostics = check_path(
|
||||
path,
|
||||
path.parent()
|
||||
.and_then(|parent| detect_package_root(parent, &settings.namespace_packages)),
|
||||
&contents,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
&directives,
|
||||
settings,
|
||||
flags::Autofix::Enabled,
|
||||
flags::Noqa::Enabled,
|
||||
)?;
|
||||
|
||||
// Detect autofixes that don't converge after multiple iterations.
|
||||
if diagnostics
|
||||
.iter()
|
||||
.any(|diagnostic| diagnostic.fix.is_some())
|
||||
{
|
||||
let max_iterations = 10;
|
||||
|
||||
let mut contents = contents.clone();
|
||||
let mut iterations = 0;
|
||||
|
||||
loop {
|
||||
let tokens: Vec<LexResult> = rustpython_helpers::tokenize(&contents);
|
||||
let locator = Locator::new(&contents);
|
||||
let stylist = Stylist::from_contents(&contents, &locator);
|
||||
let indexer: Indexer = tokens.as_slice().into();
|
||||
let directives =
|
||||
directives::extract_directives(&tokens, directives::Flags::from_settings(settings));
|
||||
let diagnostics = check_path(
|
||||
path,
|
||||
None,
|
||||
&contents,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
&directives,
|
||||
settings,
|
||||
flags::Autofix::Enabled,
|
||||
flags::Noqa::Enabled,
|
||||
)?;
|
||||
if let Some((fixed_contents, _)) = fix_file(&diagnostics, &locator) {
|
||||
if iterations < max_iterations {
|
||||
iterations += 1;
|
||||
contents = fixed_contents.to_string();
|
||||
} else {
|
||||
panic!(
|
||||
"Failed to converge after {max_iterations} iterations. This likely \
|
||||
indicates a bug in the implementation of the fix."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics.sort_by_key(|diagnostic| diagnostic.location);
|
||||
Ok(diagnostics)
|
||||
}
|
||||
|
||||
111
src/noqa.rs
111
src/noqa.rs
@@ -10,7 +10,6 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rule_redirects::get_redirect_target;
|
||||
use crate::settings::hashable::HashableHashSet;
|
||||
use crate::source_code::LineEnding;
|
||||
|
||||
static NOQA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
@@ -81,12 +80,17 @@ pub fn add_noqa(
|
||||
path: &Path,
|
||||
diagnostics: &[Diagnostic],
|
||||
contents: &str,
|
||||
commented_lines: &[usize],
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &HashableHashSet<String>,
|
||||
line_ending: &LineEnding,
|
||||
) -> Result<usize> {
|
||||
let (count, output) =
|
||||
add_noqa_inner(diagnostics, contents, noqa_line_for, external, line_ending);
|
||||
let (count, output) = add_noqa_inner(
|
||||
diagnostics,
|
||||
contents,
|
||||
commented_lines,
|
||||
noqa_line_for,
|
||||
line_ending,
|
||||
);
|
||||
fs::write(path, output)?;
|
||||
Ok(count)
|
||||
}
|
||||
@@ -94,31 +98,66 @@ pub fn add_noqa(
|
||||
fn add_noqa_inner(
|
||||
diagnostics: &[Diagnostic],
|
||||
contents: &str,
|
||||
commented_lines: &[usize],
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &HashableHashSet<String>,
|
||||
line_ending: &LineEnding,
|
||||
) -> (usize, String) {
|
||||
let mut matches_by_line: FxHashMap<usize, FxHashSet<&Rule>> = FxHashMap::default();
|
||||
for (lineno, line) in contents.lines().enumerate() {
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for (lineno, line) in lines.iter().enumerate() {
|
||||
// If we hit an exemption for the entire file, bail.
|
||||
if is_file_exempt(line) {
|
||||
return (0, contents.to_string());
|
||||
}
|
||||
|
||||
// Grab the noqa (logical) line number for the current (physical) line.
|
||||
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
|
||||
|
||||
let mut codes: FxHashSet<&Rule> = FxHashSet::default();
|
||||
for diagnostic in diagnostics {
|
||||
// TODO(charlie): Consider respecting parent `noqa` directives. For now, we'll
|
||||
// add a `noqa` for every diagnostic, on its own line. This could lead to
|
||||
// duplication, whereby some parent `noqa` directives become
|
||||
// redundant.
|
||||
if diagnostic.location.row() == lineno + 1 {
|
||||
// Is the violation ignored by a `noqa` directive on the parent line?
|
||||
if let Some(parent_lineno) = diagnostic.parent.map(|location| location.row()) {
|
||||
let noqa_lineno = noqa_line_for.get(&parent_lineno).unwrap_or(&parent_lineno);
|
||||
if commented_lines.contains(noqa_lineno) {
|
||||
match extract_noqa_directive(lines[noqa_lineno - 1]) {
|
||||
Directive::All(..) => {
|
||||
continue;
|
||||
}
|
||||
Directive::Codes(.., codes) => {
|
||||
if includes(diagnostic.kind.rule(), &codes) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Directive::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the diagnostic ignored by a `noqa` directive on the same line?
|
||||
let diagnostic_lineno = diagnostic.location.row();
|
||||
let noqa_lineno = noqa_line_for
|
||||
.get(&diagnostic_lineno)
|
||||
.unwrap_or(&diagnostic_lineno);
|
||||
if commented_lines.contains(noqa_lineno) {
|
||||
match extract_noqa_directive(lines[noqa_lineno - 1]) {
|
||||
Directive::All(..) => {
|
||||
continue;
|
||||
}
|
||||
Directive::Codes(.., codes) => {
|
||||
if includes(diagnostic.kind.rule(), &codes) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Directive::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
// The diagnostic is not ignored by any `noqa` directive; add it to the list.
|
||||
codes.insert(diagnostic.kind.rule());
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the noqa (logical) line number for the current (physical) line.
|
||||
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
|
||||
|
||||
if !codes.is_empty() {
|
||||
matches_by_line
|
||||
.entry(noqa_lineno)
|
||||
@@ -151,28 +190,19 @@ fn add_noqa_inner(
|
||||
output.push_str(line_ending);
|
||||
count += 1;
|
||||
}
|
||||
Directive::All(_, start, _) => {
|
||||
// Add existing content.
|
||||
output.push_str(line[..start].trim_end());
|
||||
|
||||
// Add `noqa` directive.
|
||||
output.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> =
|
||||
rules.iter().map(|r| r.code()).sorted_unstable().collect();
|
||||
let suffix = codes.join(", ");
|
||||
output.push_str(&suffix);
|
||||
Directive::All(..) => {
|
||||
// Leave the line as-is.
|
||||
output.push_str(line);
|
||||
output.push_str(line_ending);
|
||||
count += 1;
|
||||
}
|
||||
Directive::Codes(_, start, _, existing) => {
|
||||
Directive::Codes(_, start_byte, _, existing) => {
|
||||
println!("existing: {:?}", existing);
|
||||
// Reconstruct the line based on the preserved rule codes.
|
||||
// This enables us to tally the number of edits.
|
||||
let mut formatted = String::new();
|
||||
let mut formatted = String::with_capacity(line.len());
|
||||
|
||||
// Add existing content.
|
||||
formatted.push_str(line[..start].trim_end());
|
||||
formatted.push_str(line[..start_byte].trim_end());
|
||||
|
||||
// Add `noqa` directive.
|
||||
formatted.push_str(" # noqa: ");
|
||||
@@ -181,7 +211,7 @@ fn add_noqa_inner(
|
||||
let codes: Vec<&str> = rules
|
||||
.iter()
|
||||
.map(|r| r.code())
|
||||
.chain(existing.into_iter().filter(|code| external.contains(*code)))
|
||||
.chain(existing.into_iter())
|
||||
.sorted_unstable()
|
||||
.collect();
|
||||
let suffix = codes.join(", ");
|
||||
@@ -212,7 +242,6 @@ mod tests {
|
||||
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::rules::pycodestyle::rules::AmbiguousVariableName;
|
||||
use crate::settings::hashable::HashableHashSet;
|
||||
use crate::source_code::LineEnding;
|
||||
use crate::violations;
|
||||
|
||||
@@ -234,13 +263,13 @@ mod tests {
|
||||
fn modification() {
|
||||
let diagnostics = vec![];
|
||||
let contents = "x = 1";
|
||||
let commented_lines = vec![];
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
&commented_lines,
|
||||
&noqa_line_for,
|
||||
&external,
|
||||
&LineEnding::Lf,
|
||||
);
|
||||
assert_eq!(count, 0);
|
||||
@@ -253,13 +282,13 @@ mod tests {
|
||||
Range::new(Location::new(1, 0), Location::new(1, 0)),
|
||||
)];
|
||||
let contents = "x = 1";
|
||||
let commented_lines = vec![1];
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
&commented_lines,
|
||||
&noqa_line_for,
|
||||
&external,
|
||||
&LineEnding::Lf,
|
||||
);
|
||||
assert_eq!(count, 1);
|
||||
@@ -278,13 +307,13 @@ mod tests {
|
||||
),
|
||||
];
|
||||
let contents = "x = 1 # noqa: E741\n";
|
||||
let commented_lines = vec![1];
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
&commented_lines,
|
||||
&noqa_line_for,
|
||||
&external,
|
||||
&LineEnding::Lf,
|
||||
);
|
||||
assert_eq!(count, 1);
|
||||
@@ -303,16 +332,16 @@ mod tests {
|
||||
),
|
||||
];
|
||||
let contents = "x = 1 # noqa";
|
||||
let commented_lines = vec![1];
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = HashableHashSet::default();
|
||||
let (count, output) = add_noqa_inner(
|
||||
&diagnostics,
|
||||
contents,
|
||||
&commented_lines,
|
||||
&noqa_line_for,
|
||||
&external,
|
||||
&LineEnding::Lf,
|
||||
);
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(output, "x = 1 # noqa: E741, F841\n");
|
||||
assert_eq!(count, 0);
|
||||
assert_eq!(output, "x = 1 # noqa\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,50 +119,26 @@ pub fn detect_package_roots<'a>(
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::packaging::detect_package_root;
|
||||
use crate::{packaging::detect_package_root, test::test_resource_path};
|
||||
|
||||
#[test]
|
||||
fn package_detection() {
|
||||
assert_eq!(
|
||||
detect_package_root(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("resources/test/package/src/package")
|
||||
.as_path(),
|
||||
&[],
|
||||
),
|
||||
Some(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("resources/test/package/src/package")
|
||||
.as_path()
|
||||
)
|
||||
detect_package_root(&test_resource_path("package/src/package"), &[],),
|
||||
Some(test_resource_path("package/src/package").as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
detect_package_root(&test_resource_path("project/python_modules/core/core"), &[],),
|
||||
Some(test_resource_path("project/python_modules/core/core").as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
detect_package_root(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("resources/test/project/python_modules/core/core")
|
||||
.as_path(),
|
||||
&test_resource_path("project/examples/docs/docs/concepts"),
|
||||
&[],
|
||||
),
|
||||
Some(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("resources/test/project/python_modules/core/core")
|
||||
.as_path()
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
detect_package_root(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("resources/test/project/examples/docs/docs/concepts")
|
||||
.as_path(),
|
||||
&[],
|
||||
),
|
||||
Some(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("resources/test/project/examples/docs/docs")
|
||||
.as_path()
|
||||
)
|
||||
Some(test_resource_path("project/examples/docs/docs").as_path())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
||||
174
src/registry.rs
174
src/registry.rs
@@ -7,7 +7,6 @@ use strum_macros::{AsRefStr, EnumIter};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::rule_selector::{prefix_to_selector, RuleSelector};
|
||||
use crate::violation::Violation;
|
||||
use crate::{rules, violations};
|
||||
|
||||
@@ -94,58 +93,59 @@ ruff_macros::define_rule_mapping!(
|
||||
PLW0120 => violations::UselessElseOnLoop,
|
||||
PLW0602 => violations::GlobalVariableNotAssigned,
|
||||
PLR0913 => rules::pylint::rules::TooManyArgs,
|
||||
PLR0915 => rules::pylint::rules::TooManyStatements,
|
||||
// flake8-builtins
|
||||
A001 => violations::BuiltinVariableShadowing,
|
||||
A002 => violations::BuiltinArgumentShadowing,
|
||||
A003 => violations::BuiltinAttributeShadowing,
|
||||
A001 => rules::flake8_builtins::rules::BuiltinVariableShadowing,
|
||||
A002 => rules::flake8_builtins::rules::BuiltinArgumentShadowing,
|
||||
A003 => rules::flake8_builtins::rules::BuiltinAttributeShadowing,
|
||||
// flake8-bugbear
|
||||
B002 => violations::UnaryPrefixIncrement,
|
||||
B003 => violations::AssignmentToOsEnviron,
|
||||
B004 => violations::UnreliableCallableCheck,
|
||||
B005 => violations::StripWithMultiCharacters,
|
||||
B006 => violations::MutableArgumentDefault,
|
||||
B007 => violations::UnusedLoopControlVariable,
|
||||
B008 => violations::FunctionCallArgumentDefault,
|
||||
B009 => violations::GetAttrWithConstant,
|
||||
B010 => violations::SetAttrWithConstant,
|
||||
B011 => violations::DoNotAssertFalse,
|
||||
B012 => violations::JumpStatementInFinally,
|
||||
B013 => violations::RedundantTupleInExceptionHandler,
|
||||
B014 => violations::DuplicateHandlerException,
|
||||
B015 => violations::UselessComparison,
|
||||
B016 => violations::CannotRaiseLiteral,
|
||||
B017 => violations::NoAssertRaisesException,
|
||||
B018 => violations::UselessExpression,
|
||||
B019 => violations::CachedInstanceMethod,
|
||||
B020 => violations::LoopVariableOverridesIterator,
|
||||
B021 => violations::FStringDocstring,
|
||||
B022 => violations::UselessContextlibSuppress,
|
||||
B023 => violations::FunctionUsesLoopVariable,
|
||||
B024 => violations::AbstractBaseClassWithoutAbstractMethod,
|
||||
B025 => violations::DuplicateTryBlockException,
|
||||
B026 => violations::StarArgUnpackingAfterKeywordArg,
|
||||
B027 => violations::EmptyMethodWithoutAbstractDecorator,
|
||||
B904 => violations::RaiseWithoutFromInsideExcept,
|
||||
B905 => violations::ZipWithoutExplicitStrict,
|
||||
B002 => rules::flake8_bugbear::rules::UnaryPrefixIncrement,
|
||||
B003 => rules::flake8_bugbear::rules::AssignmentToOsEnviron,
|
||||
B004 => rules::flake8_bugbear::rules::UnreliableCallableCheck,
|
||||
B005 => rules::flake8_bugbear::rules::StripWithMultiCharacters,
|
||||
B006 => rules::flake8_bugbear::rules::MutableArgumentDefault,
|
||||
B007 => rules::flake8_bugbear::rules::UnusedLoopControlVariable,
|
||||
B008 => rules::flake8_bugbear::rules::FunctionCallArgumentDefault,
|
||||
B009 => rules::flake8_bugbear::rules::GetAttrWithConstant,
|
||||
B010 => rules::flake8_bugbear::rules::SetAttrWithConstant,
|
||||
B011 => rules::flake8_bugbear::rules::DoNotAssertFalse,
|
||||
B012 => rules::flake8_bugbear::rules::JumpStatementInFinally,
|
||||
B013 => rules::flake8_bugbear::rules::RedundantTupleInExceptionHandler,
|
||||
B014 => rules::flake8_bugbear::rules::DuplicateHandlerException,
|
||||
B015 => rules::flake8_bugbear::rules::UselessComparison,
|
||||
B016 => rules::flake8_bugbear::rules::CannotRaiseLiteral,
|
||||
B017 => rules::flake8_bugbear::rules::NoAssertRaisesException,
|
||||
B018 => rules::flake8_bugbear::rules::UselessExpression,
|
||||
B019 => rules::flake8_bugbear::rules::CachedInstanceMethod,
|
||||
B020 => rules::flake8_bugbear::rules::LoopVariableOverridesIterator,
|
||||
B021 => rules::flake8_bugbear::rules::FStringDocstring,
|
||||
B022 => rules::flake8_bugbear::rules::UselessContextlibSuppress,
|
||||
B023 => rules::flake8_bugbear::rules::FunctionUsesLoopVariable,
|
||||
B024 => rules::flake8_bugbear::rules::AbstractBaseClassWithoutAbstractMethod,
|
||||
B025 => rules::flake8_bugbear::rules::DuplicateTryBlockException,
|
||||
B026 => rules::flake8_bugbear::rules::StarArgUnpackingAfterKeywordArg,
|
||||
B027 => rules::flake8_bugbear::rules::EmptyMethodWithoutAbstractDecorator,
|
||||
B904 => rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept,
|
||||
B905 => rules::flake8_bugbear::rules::ZipWithoutExplicitStrict,
|
||||
// flake8-blind-except
|
||||
BLE001 => violations::BlindExcept,
|
||||
BLE001 => rules::flake8_blind_except::rules::BlindExcept,
|
||||
// flake8-comprehensions
|
||||
C400 => violations::UnnecessaryGeneratorList,
|
||||
C401 => violations::UnnecessaryGeneratorSet,
|
||||
C402 => violations::UnnecessaryGeneratorDict,
|
||||
C403 => violations::UnnecessaryListComprehensionSet,
|
||||
C404 => violations::UnnecessaryListComprehensionDict,
|
||||
C405 => violations::UnnecessaryLiteralSet,
|
||||
C406 => violations::UnnecessaryLiteralDict,
|
||||
C408 => violations::UnnecessaryCollectionCall,
|
||||
C409 => violations::UnnecessaryLiteralWithinTupleCall,
|
||||
C410 => violations::UnnecessaryLiteralWithinListCall,
|
||||
C411 => violations::UnnecessaryListCall,
|
||||
C413 => violations::UnnecessaryCallAroundSorted,
|
||||
C414 => violations::UnnecessaryDoubleCastOrProcess,
|
||||
C415 => violations::UnnecessarySubscriptReversal,
|
||||
C416 => violations::UnnecessaryComprehension,
|
||||
C417 => violations::UnnecessaryMap,
|
||||
C400 => rules::flake8_comprehensions::rules::UnnecessaryGeneratorList,
|
||||
C401 => rules::flake8_comprehensions::rules::UnnecessaryGeneratorSet,
|
||||
C402 => rules::flake8_comprehensions::rules::UnnecessaryGeneratorDict,
|
||||
C403 => rules::flake8_comprehensions::rules::UnnecessaryListComprehensionSet,
|
||||
C404 => rules::flake8_comprehensions::rules::UnnecessaryListComprehensionDict,
|
||||
C405 => rules::flake8_comprehensions::rules::UnnecessaryLiteralSet,
|
||||
C406 => rules::flake8_comprehensions::rules::UnnecessaryLiteralDict,
|
||||
C408 => rules::flake8_comprehensions::rules::UnnecessaryCollectionCall,
|
||||
C409 => rules::flake8_comprehensions::rules::UnnecessaryLiteralWithinTupleCall,
|
||||
C410 => rules::flake8_comprehensions::rules::UnnecessaryLiteralWithinListCall,
|
||||
C411 => rules::flake8_comprehensions::rules::UnnecessaryListCall,
|
||||
C413 => rules::flake8_comprehensions::rules::UnnecessaryCallAroundSorted,
|
||||
C414 => rules::flake8_comprehensions::rules::UnnecessaryDoubleCastOrProcess,
|
||||
C415 => rules::flake8_comprehensions::rules::UnnecessarySubscriptReversal,
|
||||
C416 => rules::flake8_comprehensions::rules::UnnecessaryComprehension,
|
||||
C417 => rules::flake8_comprehensions::rules::UnnecessaryMap,
|
||||
// flake8-debugger
|
||||
T100 => violations::Debugger,
|
||||
// mccabe
|
||||
@@ -170,10 +170,10 @@ ruff_macros::define_rule_mapping!(
|
||||
T201 => violations::PrintFound,
|
||||
T203 => violations::PPrintFound,
|
||||
// flake8-quotes
|
||||
Q000 => violations::BadQuotesInlineString,
|
||||
Q001 => violations::BadQuotesMultilineString,
|
||||
Q002 => violations::BadQuotesDocstring,
|
||||
Q003 => violations::AvoidQuoteEscape,
|
||||
Q000 => rules::flake8_quotes::rules::BadQuotesInlineString,
|
||||
Q001 => rules::flake8_quotes::rules::BadQuotesMultilineString,
|
||||
Q002 => rules::flake8_quotes::rules::BadQuotesDocstring,
|
||||
Q003 => rules::flake8_quotes::rules::AvoidQuoteEscape,
|
||||
// flake8-annotations
|
||||
ANN001 => violations::MissingTypeFunctionArgument,
|
||||
ANN002 => violations::MissingTypeArgs,
|
||||
@@ -256,6 +256,8 @@ ruff_macros::define_rule_mapping!(
|
||||
UP032 => violations::FString,
|
||||
UP033 => violations::FunctoolsCache,
|
||||
UP034 => violations::ExtraneousParentheses,
|
||||
UP035 => rules::pyupgrade::rules::ImportReplacements,
|
||||
UP036 => rules::pyupgrade::rules::OutdatedVersionBlock,
|
||||
// pydocstyle
|
||||
D100 => violations::PublicModule,
|
||||
D101 => violations::PublicClass,
|
||||
@@ -392,26 +394,26 @@ ruff_macros::define_rule_mapping!(
|
||||
PT003 => rules::flake8_pytest_style::rules::ExtraneousScopeFunction,
|
||||
PT004 => rules::flake8_pytest_style::rules::MissingFixtureNameUnderscore,
|
||||
PT005 => rules::flake8_pytest_style::rules::IncorrectFixtureNameUnderscore,
|
||||
PT006 => violations::ParametrizeNamesWrongType,
|
||||
PT007 => violations::ParametrizeValuesWrongType,
|
||||
PT008 => violations::PatchWithLambda,
|
||||
PT009 => violations::UnittestAssertion,
|
||||
PT010 => violations::RaisesWithoutException,
|
||||
PT011 => violations::RaisesTooBroad,
|
||||
PT012 => violations::RaisesWithMultipleStatements,
|
||||
PT013 => violations::IncorrectPytestImport,
|
||||
PT015 => violations::AssertAlwaysFalse,
|
||||
PT016 => violations::FailWithoutMessage,
|
||||
PT017 => violations::AssertInExcept,
|
||||
PT018 => violations::CompositeAssertion,
|
||||
PT006 => rules::flake8_pytest_style::rules::ParametrizeNamesWrongType,
|
||||
PT007 => rules::flake8_pytest_style::rules::ParametrizeValuesWrongType,
|
||||
PT008 => rules::flake8_pytest_style::rules::PatchWithLambda,
|
||||
PT009 => rules::flake8_pytest_style::rules::UnittestAssertion,
|
||||
PT010 => rules::flake8_pytest_style::rules::RaisesWithoutException,
|
||||
PT011 => rules::flake8_pytest_style::rules::RaisesTooBroad,
|
||||
PT012 => rules::flake8_pytest_style::rules::RaisesWithMultipleStatements,
|
||||
PT013 => rules::flake8_pytest_style::rules::IncorrectPytestImport,
|
||||
PT015 => rules::flake8_pytest_style::rules::AssertAlwaysFalse,
|
||||
PT016 => rules::flake8_pytest_style::rules::FailWithoutMessage,
|
||||
PT017 => rules::flake8_pytest_style::rules::AssertInExcept,
|
||||
PT018 => rules::flake8_pytest_style::rules::CompositeAssertion,
|
||||
PT019 => rules::flake8_pytest_style::rules::FixtureParamWithoutValue,
|
||||
PT020 => rules::flake8_pytest_style::rules::DeprecatedYieldFixture,
|
||||
PT021 => rules::flake8_pytest_style::rules::FixtureFinalizerCallback,
|
||||
PT022 => rules::flake8_pytest_style::rules::UselessYieldFixture,
|
||||
PT023 => violations::IncorrectMarkParenthesesStyle,
|
||||
PT023 => rules::flake8_pytest_style::rules::IncorrectMarkParenthesesStyle,
|
||||
PT024 => rules::flake8_pytest_style::rules::UnnecessaryAsyncioMarkOnFixture,
|
||||
PT025 => rules::flake8_pytest_style::rules::ErroneousUseFixturesOnFixture,
|
||||
PT026 => violations::UseFixturesWithoutParameters,
|
||||
PT026 => rules::flake8_pytest_style::rules::UseFixturesWithoutParameters,
|
||||
// flake8-pie
|
||||
PIE790 => rules::flake8_pie::rules::NoUnnecessaryPass,
|
||||
PIE794 => rules::flake8_pie::rules::DupeClassFieldDefinitions,
|
||||
@@ -481,6 +483,10 @@ ruff_macros::define_rule_mapping!(
|
||||
G101 => rules::flake8_logging_format::violations::LoggingExtraAttrClash,
|
||||
G201 => rules::flake8_logging_format::violations::LoggingExcInfo,
|
||||
G202 => rules::flake8_logging_format::violations::LoggingRedundantExcInfo,
|
||||
// flake8-raise
|
||||
RSE102 => rules::flake8_raise::rules::UnnecessaryParenOnRaiseException,
|
||||
// flake8-self
|
||||
SLF001 => rules::flake8_self::rules::PrivateMemberAccess,
|
||||
// ruff
|
||||
RUF001 => violations::AmbiguousUnicodeCharacterString,
|
||||
RUF002 => violations::AmbiguousUnicodeCharacterDocstring,
|
||||
@@ -610,6 +616,12 @@ pub enum Linter {
|
||||
/// [tryceratops](https://pypi.org/project/tryceratops/1.1.0/)
|
||||
#[prefix = "TRY"]
|
||||
Tryceratops,
|
||||
/// [flake8-raise](https://pypi.org/project/flake8-raise/)
|
||||
#[prefix = "RSE"]
|
||||
Flake8Raise,
|
||||
/// [flake8-self](https://pypi.org/project/flake8-self/)
|
||||
#[prefix = "SLF"]
|
||||
Flake8Self,
|
||||
/// Ruff-specific rules
|
||||
#[prefix = "RUF"]
|
||||
Ruff,
|
||||
@@ -633,27 +645,21 @@ pub trait RuleNamespace: Sized {
|
||||
}
|
||||
|
||||
/// The prefix, name and selector for an upstream linter category.
|
||||
pub struct LinterCategory(pub &'static str, pub &'static str, pub RuleSelector);
|
||||
|
||||
// TODO(martin): Move these constant definitions back to Linter::categories impl
|
||||
// once RuleSelector is an enum with a Linter variant
|
||||
const PYCODESTYLE_CATEGORIES: &[LinterCategory] = &[
|
||||
LinterCategory("E", "Error", prefix_to_selector(RuleCodePrefix::E)),
|
||||
LinterCategory("W", "Warning", prefix_to_selector(RuleCodePrefix::W)),
|
||||
];
|
||||
|
||||
const PYLINT_CATEGORIES: &[LinterCategory] = &[
|
||||
LinterCategory("PLC", "Convention", prefix_to_selector(RuleCodePrefix::PLC)),
|
||||
LinterCategory("PLE", "Error", prefix_to_selector(RuleCodePrefix::PLE)),
|
||||
LinterCategory("PLR", "Refactor", prefix_to_selector(RuleCodePrefix::PLR)),
|
||||
LinterCategory("PLW", "Warning", prefix_to_selector(RuleCodePrefix::PLW)),
|
||||
];
|
||||
pub struct LinterCategory(pub &'static str, pub &'static str, pub RuleCodePrefix);
|
||||
|
||||
impl Linter {
|
||||
pub fn categories(&self) -> Option<&'static [LinterCategory]> {
|
||||
match self {
|
||||
Linter::Pycodestyle => Some(PYCODESTYLE_CATEGORIES),
|
||||
Linter::Pylint => Some(PYLINT_CATEGORIES),
|
||||
Linter::Pycodestyle => Some(&[
|
||||
LinterCategory("E", "Error", RuleCodePrefix::E),
|
||||
LinterCategory("W", "Warning", RuleCodePrefix::W),
|
||||
]),
|
||||
Linter::Pylint => Some(&[
|
||||
LinterCategory("PLC", "Convention", RuleCodePrefix::PLC),
|
||||
LinterCategory("PLE", "Error", RuleCodePrefix::PLE),
|
||||
LinterCategory("PLR", "Refactor", RuleCodePrefix::PLR),
|
||||
LinterCategory("PLW", "Warning", RuleCodePrefix::PLW),
|
||||
]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use strum_macros::EnumIter;
|
||||
use crate::registry::{Rule, RuleCodePrefix, RuleIter};
|
||||
use crate::rule_redirects::get_redirect;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum RuleSelector {
|
||||
/// All rules
|
||||
All,
|
||||
@@ -49,15 +49,21 @@ pub enum ParseError {
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl RuleSelector {
|
||||
pub fn short_code(&self) -> &'static str {
|
||||
match self {
|
||||
RuleSelector::All => "ALL",
|
||||
RuleSelector::Prefix { prefix, .. } => prefix.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for RuleSelector {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
RuleSelector::All => serializer.serialize_str("ALL"),
|
||||
RuleSelector::Prefix { prefix, .. } => prefix.serialize(serializer),
|
||||
}
|
||||
serializer.serialize_str(self.short_code())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,17 +9,15 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_yaml_snapshot, settings};
|
||||
|
||||
#[test_case(Rule::CommentedOutCode, Path::new("ERA001.py"); "ERA001")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/eradicate")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
Path::new("eradicate").join(path).as_path(),
|
||||
&settings::Settings::for_rule(rule_code),
|
||||
)?;
|
||||
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
|
||||
@@ -8,8 +8,8 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_yaml_snapshot, settings};
|
||||
|
||||
#[test_case(Rule::SysVersionSlice3Referenced, Path::new("YTT101.py"); "YTT101")]
|
||||
@@ -25,9 +25,7 @@ mod tests {
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_2020")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
Path::new("flake8_2020").join(path).as_path(),
|
||||
&settings::Settings::for_rule(rule_code),
|
||||
)?;
|
||||
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
|
||||
@@ -11,14 +11,14 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::assert_yaml_snapshot;
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::Settings;
|
||||
use crate::test::test_path;
|
||||
|
||||
#[test]
|
||||
fn defaults() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/annotation_presence.py"),
|
||||
Path::new("flake8_annotations/annotation_presence.py"),
|
||||
&Settings {
|
||||
..Settings::for_rules(vec![
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
@@ -42,7 +42,7 @@ mod tests {
|
||||
#[test]
|
||||
fn suppress_dummy_args() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/suppress_dummy_args.py"),
|
||||
Path::new("flake8_annotations/suppress_dummy_args.py"),
|
||||
&Settings {
|
||||
flake8_annotations: super::settings::Settings {
|
||||
mypy_init_return: false,
|
||||
@@ -66,7 +66,7 @@ mod tests {
|
||||
#[test]
|
||||
fn mypy_init_return() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/mypy_init_return.py"),
|
||||
Path::new("flake8_annotations/mypy_init_return.py"),
|
||||
&Settings {
|
||||
flake8_annotations: super::settings::Settings {
|
||||
mypy_init_return: true,
|
||||
@@ -90,7 +90,7 @@ mod tests {
|
||||
#[test]
|
||||
fn suppress_none_returning() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/suppress_none_returning.py"),
|
||||
Path::new("flake8_annotations/suppress_none_returning.py"),
|
||||
&Settings {
|
||||
flake8_annotations: super::settings::Settings {
|
||||
mypy_init_return: false,
|
||||
@@ -114,7 +114,7 @@ mod tests {
|
||||
#[test]
|
||||
fn allow_star_arg_any() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/allow_star_arg_any.py"),
|
||||
Path::new("flake8_annotations/allow_star_arg_any.py"),
|
||||
&Settings {
|
||||
flake8_annotations: super::settings::Settings {
|
||||
mypy_init_return: false,
|
||||
@@ -132,7 +132,7 @@ mod tests {
|
||||
#[test]
|
||||
fn allow_overload() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/allow_overload.py"),
|
||||
Path::new("flake8_annotations/allow_overload.py"),
|
||||
&Settings {
|
||||
..Settings::for_rules(vec![
|
||||
Rule::MissingReturnTypePublicFunction,
|
||||
@@ -150,7 +150,7 @@ mod tests {
|
||||
#[test]
|
||||
fn allow_nested_overload() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/allow_nested_overload.py"),
|
||||
Path::new("flake8_annotations/allow_nested_overload.py"),
|
||||
&Settings {
|
||||
..Settings::for_rules(vec![
|
||||
Rule::MissingReturnTypePublicFunction,
|
||||
|
||||
@@ -11,9 +11,9 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::assert_yaml_snapshot;
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::Settings;
|
||||
use crate::test::test_path;
|
||||
|
||||
#[test_case(Rule::AssertUsed, Path::new("S101.py"); "S101")]
|
||||
#[test_case(Rule::ExecUsed, Path::new("S102.py"); "S102")]
|
||||
@@ -35,9 +35,7 @@ mod tests {
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bandit")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
Path::new("flake8_bandit").join(path).as_path(),
|
||||
&Settings::for_rule(rule_code),
|
||||
)?;
|
||||
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
@@ -47,7 +45,7 @@ mod tests {
|
||||
#[test]
|
||||
fn check_hardcoded_tmp_additional_dirs() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bandit/S108.py"),
|
||||
Path::new("flake8_bandit/S108.py"),
|
||||
&Settings {
|
||||
flake8_bandit: super::settings::Settings {
|
||||
hardcoded_tmp_directory: vec![
|
||||
@@ -68,7 +66,7 @@ mod tests {
|
||||
#[test]
|
||||
fn check_typed_exception() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bandit/S110.py"),
|
||||
Path::new("flake8_bandit/S110.py"),
|
||||
&Settings {
|
||||
flake8_bandit: super::settings::Settings {
|
||||
check_typed_exception: true,
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
|
||||
fn check_password_kwarg(arg: &Located<ArgData>, default: &Expr) -> Option<Diagnostic> {
|
||||
let string = string_literal(default)?;
|
||||
let string = string_literal(default).filter(|string| !string.is_empty())?;
|
||||
let kwarg_name = &arg.node.arg;
|
||||
if !matches_password_name(kwarg_name) {
|
||||
return None;
|
||||
|
||||
@@ -10,7 +10,7 @@ pub fn hardcoded_password_func_arg(keywords: &[Keyword]) -> Vec<Diagnostic> {
|
||||
keywords
|
||||
.iter()
|
||||
.filter_map(|keyword| {
|
||||
let string = string_literal(&keyword.node.value)?;
|
||||
let string = string_literal(&keyword.node.value).filter(|string| !string.is_empty())?;
|
||||
let arg = keyword.node.arg.as_ref()?;
|
||||
if !matches_password_name(arg) {
|
||||
return None;
|
||||
|
||||
@@ -30,7 +30,7 @@ pub fn compare_to_hardcoded_password_string(left: &Expr, comparators: &[Expr]) -
|
||||
comparators
|
||||
.iter()
|
||||
.filter_map(|comp| {
|
||||
let string = string_literal(comp)?;
|
||||
let string = string_literal(comp).filter(|string| !string.is_empty())?;
|
||||
if !is_password_target(left) {
|
||||
return None;
|
||||
}
|
||||
@@ -46,7 +46,7 @@ pub fn compare_to_hardcoded_password_string(left: &Expr, comparators: &[Expr]) -
|
||||
|
||||
/// S105
|
||||
pub fn assign_hardcoded_password_string(value: &Expr, targets: &[Expr]) -> Option<Diagnostic> {
|
||||
if let Some(string) = string_literal(value) {
|
||||
if let Some(string) = string_literal(value).filter(|string| !string.is_empty()) {
|
||||
for target in targets {
|
||||
if is_password_target(target) {
|
||||
return Some(Diagnostic::new(
|
||||
|
||||
@@ -21,14 +21,14 @@ fn default_tmp_dirs() -> Vec<String> {
|
||||
pub struct Options {
|
||||
#[option(
|
||||
default = "[\"/tmp\", \"/var/tmp\", \"/dev/shm\"]",
|
||||
value_type = "Vec<String>",
|
||||
value_type = "list[str]",
|
||||
example = "hardcoded-tmp-directory = [\"/foo/bar\"]"
|
||||
)]
|
||||
/// A list of directories to consider temporary.
|
||||
pub hardcoded_tmp_directory: Option<Vec<String>>,
|
||||
#[option(
|
||||
default = "[]",
|
||||
value_type = "Vec<String>",
|
||||
value_type = "list[str]",
|
||||
example = "extend-hardcoded-tmp-directory = [\"/foo/bar\"]"
|
||||
)]
|
||||
/// A list of directories to consider temporary, in addition to those
|
||||
|
||||
@@ -6,10 +6,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 12
|
||||
row: 13
|
||||
column: 11
|
||||
end_location:
|
||||
row: 12
|
||||
row: 13
|
||||
column: 19
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -17,10 +17,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 8
|
||||
end_location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 16
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -28,10 +28,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 14
|
||||
row: 15
|
||||
column: 9
|
||||
end_location:
|
||||
row: 14
|
||||
row: 15
|
||||
column: 17
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -39,10 +39,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 15
|
||||
row: 16
|
||||
column: 6
|
||||
end_location:
|
||||
row: 15
|
||||
row: 16
|
||||
column: 14
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -50,10 +50,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 9
|
||||
end_location:
|
||||
row: 16
|
||||
row: 17
|
||||
column: 17
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -61,10 +61,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 17
|
||||
row: 18
|
||||
column: 8
|
||||
end_location:
|
||||
row: 17
|
||||
row: 18
|
||||
column: 16
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -72,22 +72,11 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 18
|
||||
row: 19
|
||||
column: 10
|
||||
end_location:
|
||||
row: 18
|
||||
column: 18
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 19
|
||||
column: 18
|
||||
end_location:
|
||||
row: 19
|
||||
column: 26
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
@@ -105,10 +94,21 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 22
|
||||
row: 21
|
||||
column: 18
|
||||
end_location:
|
||||
row: 21
|
||||
column: 26
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 23
|
||||
column: 16
|
||||
end_location:
|
||||
row: 22
|
||||
row: 23
|
||||
column: 24
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -116,10 +116,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 23
|
||||
row: 24
|
||||
column: 12
|
||||
end_location:
|
||||
row: 23
|
||||
row: 24
|
||||
column: 20
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -127,10 +127,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 24
|
||||
row: 25
|
||||
column: 14
|
||||
end_location:
|
||||
row: 24
|
||||
row: 25
|
||||
column: 22
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -138,10 +138,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 25
|
||||
row: 26
|
||||
column: 11
|
||||
end_location:
|
||||
row: 25
|
||||
row: 26
|
||||
column: 19
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -149,10 +149,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 26
|
||||
row: 27
|
||||
column: 14
|
||||
end_location:
|
||||
row: 26
|
||||
row: 27
|
||||
column: 22
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -160,10 +160,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 27
|
||||
row: 28
|
||||
column: 13
|
||||
end_location:
|
||||
row: 27
|
||||
row: 28
|
||||
column: 21
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -171,22 +171,11 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 28
|
||||
row: 29
|
||||
column: 15
|
||||
end_location:
|
||||
row: 28
|
||||
column: 23
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 29
|
||||
column: 23
|
||||
end_location:
|
||||
row: 29
|
||||
column: 31
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
@@ -204,10 +193,21 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 34
|
||||
row: 31
|
||||
column: 23
|
||||
end_location:
|
||||
row: 31
|
||||
column: 31
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 35
|
||||
column: 15
|
||||
end_location:
|
||||
row: 34
|
||||
row: 35
|
||||
column: 23
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -215,10 +215,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 38
|
||||
row: 39
|
||||
column: 19
|
||||
end_location:
|
||||
row: 38
|
||||
row: 39
|
||||
column: 27
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -226,10 +226,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 39
|
||||
row: 40
|
||||
column: 16
|
||||
end_location:
|
||||
row: 39
|
||||
row: 40
|
||||
column: 24
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -237,10 +237,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 40
|
||||
row: 41
|
||||
column: 17
|
||||
end_location:
|
||||
row: 40
|
||||
row: 41
|
||||
column: 25
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -248,10 +248,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 41
|
||||
row: 42
|
||||
column: 14
|
||||
end_location:
|
||||
row: 41
|
||||
row: 42
|
||||
column: 22
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -259,10 +259,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 42
|
||||
row: 43
|
||||
column: 17
|
||||
end_location:
|
||||
row: 42
|
||||
row: 43
|
||||
column: 25
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -270,10 +270,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 43
|
||||
row: 44
|
||||
column: 16
|
||||
end_location:
|
||||
row: 43
|
||||
row: 44
|
||||
column: 24
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -281,10 +281,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 44
|
||||
row: 45
|
||||
column: 18
|
||||
end_location:
|
||||
row: 44
|
||||
row: 45
|
||||
column: 26
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -292,10 +292,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 46
|
||||
row: 47
|
||||
column: 12
|
||||
end_location:
|
||||
row: 46
|
||||
row: 47
|
||||
column: 20
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -303,10 +303,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 47
|
||||
row: 48
|
||||
column: 9
|
||||
end_location:
|
||||
row: 47
|
||||
row: 48
|
||||
column: 17
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -314,10 +314,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 48
|
||||
row: 49
|
||||
column: 10
|
||||
end_location:
|
||||
row: 48
|
||||
row: 49
|
||||
column: 18
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -325,10 +325,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 49
|
||||
row: 50
|
||||
column: 7
|
||||
end_location:
|
||||
row: 49
|
||||
row: 50
|
||||
column: 15
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -336,10 +336,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 50
|
||||
row: 51
|
||||
column: 10
|
||||
end_location:
|
||||
row: 50
|
||||
row: 51
|
||||
column: 18
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -347,10 +347,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 51
|
||||
row: 52
|
||||
column: 9
|
||||
end_location:
|
||||
row: 51
|
||||
row: 52
|
||||
column: 17
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -358,10 +358,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 52
|
||||
row: 53
|
||||
column: 11
|
||||
end_location:
|
||||
row: 52
|
||||
row: 53
|
||||
column: 19
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -369,10 +369,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 53
|
||||
row: 54
|
||||
column: 20
|
||||
end_location:
|
||||
row: 53
|
||||
row: 54
|
||||
column: 28
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -380,10 +380,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: "1\n2"
|
||||
location:
|
||||
row: 55
|
||||
row: 56
|
||||
column: 12
|
||||
end_location:
|
||||
row: 55
|
||||
row: 56
|
||||
column: 18
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -391,10 +391,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: "3\t4"
|
||||
location:
|
||||
row: 58
|
||||
row: 59
|
||||
column: 12
|
||||
end_location:
|
||||
row: 58
|
||||
row: 59
|
||||
column: 18
|
||||
fix: ~
|
||||
parent: ~
|
||||
@@ -402,10 +402,10 @@ expression: diagnostics
|
||||
HardcodedPasswordString:
|
||||
string: "5\r6"
|
||||
location:
|
||||
row: 61
|
||||
row: 62
|
||||
column: 12
|
||||
end_location:
|
||||
row: 61
|
||||
row: 62
|
||||
column: 18
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -6,10 +6,10 @@ expression: diagnostics
|
||||
HardcodedPasswordFuncArg:
|
||||
string: s3cr3t
|
||||
location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 8
|
||||
end_location:
|
||||
row: 13
|
||||
row: 14
|
||||
column: 25
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
@@ -8,17 +8,15 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_yaml_snapshot, settings};
|
||||
|
||||
#[test_case(Rule::BlindExcept, Path::new("BLE.py"); "BLE001")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_blind_except")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
Path::new("flake8_blind_except").join(path).as_path(),
|
||||
&settings::Settings::for_rule(rule_code),
|
||||
)?;
|
||||
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::helpers::{find_keyword, is_const_true};
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct BlindExcept {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for BlindExcept {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let BlindExcept { name } = self;
|
||||
format!("Do not catch blind exception: `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
/// BLE001
|
||||
pub fn blind_except(
|
||||
@@ -67,7 +81,7 @@ pub fn blind_except(
|
||||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::BlindExcept {
|
||||
BlindExcept {
|
||||
name: id.to_string(),
|
||||
},
|
||||
Range::from_located(type_),
|
||||
|
||||
@@ -8,8 +8,8 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_yaml_snapshot, settings};
|
||||
|
||||
#[test_case(Rule::BooleanPositionalArgInFunctionDefinition, Path::new("FBT.py"); "FBT001")]
|
||||
@@ -18,9 +18,7 @@ mod tests {
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_boolean_trap")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
Path::new("flake8_boolean_trap").join(path).as_path(),
|
||||
&settings::Settings::for_rule(rule_code),
|
||||
)?;
|
||||
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
|
||||
@@ -10,9 +10,9 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::assert_yaml_snapshot;
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::Settings;
|
||||
use crate::test::test_path;
|
||||
|
||||
#[test_case(Rule::UnaryPrefixIncrement, Path::new("B002.py"); "B002")]
|
||||
#[test_case(Rule::AssignmentToOsEnviron, Path::new("B003.py"); "B003")]
|
||||
@@ -45,9 +45,7 @@ mod tests {
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bugbear")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
Path::new("flake8_bugbear").join(path).as_path(),
|
||||
&Settings::for_rule(rule_code),
|
||||
)?;
|
||||
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
@@ -58,7 +56,7 @@ mod tests {
|
||||
fn extend_immutable_calls() -> Result<()> {
|
||||
let snapshot = "extend_immutable_calls".to_string();
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bugbear/B008_extended.py"),
|
||||
Path::new("flake8_bugbear/B008_extended.py"),
|
||||
&Settings {
|
||||
flake8_bugbear: super::settings::Settings {
|
||||
extend_immutable_calls: vec![
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use crate::visibility::{is_abstract, is_overload};
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct AbstractBaseClassWithoutAbstractMethod {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for AbstractBaseClassWithoutAbstractMethod {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let AbstractBaseClassWithoutAbstractMethod { name } = self;
|
||||
format!("`{name}` is an abstract base class, but it has no abstract methods")
|
||||
}
|
||||
}
|
||||
define_violation!(
|
||||
pub struct EmptyMethodWithoutAbstractDecorator {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for EmptyMethodWithoutAbstractDecorator {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let EmptyMethodWithoutAbstractDecorator { name } = self;
|
||||
format!(
|
||||
"`{name}` is an empty method in an abstract base class, but has no abstract decorator"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_abc_class(checker: &Checker, bases: &[Expr], keywords: &[Keyword]) -> bool {
|
||||
keywords.iter().any(|keyword| {
|
||||
@@ -62,16 +90,19 @@ pub fn abstract_base_class(
|
||||
continue;
|
||||
}
|
||||
|
||||
let (StmtKind::FunctionDef {
|
||||
let (
|
||||
StmtKind::FunctionDef {
|
||||
decorator_list,
|
||||
body,
|
||||
name: method_name,
|
||||
..
|
||||
} | StmtKind::AsyncFunctionDef {
|
||||
decorator_list,
|
||||
body,
|
||||
name: method_name,
|
||||
..
|
||||
}
|
||||
| StmtKind::AsyncFunctionDef {
|
||||
decorator_list,
|
||||
body,
|
||||
..
|
||||
}) = &stmt.node else {
|
||||
) = &stmt.node else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -88,8 +119,8 @@ pub fn abstract_base_class(
|
||||
|
||||
if !has_abstract_decorator && is_empty_body(body) && !is_overload(checker, decorator_list) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::EmptyMethodWithoutAbstractDecorator {
|
||||
name: name.to_string(),
|
||||
EmptyMethodWithoutAbstractDecorator {
|
||||
name: format!("{name}.{method_name}"),
|
||||
},
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
@@ -102,7 +133,7 @@ pub fn abstract_base_class(
|
||||
{
|
||||
if !has_abstract_method {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::AbstractBaseClassWithoutAbstractMethod {
|
||||
AbstractBaseClassWithoutAbstractMethod {
|
||||
name: name.to_string(),
|
||||
},
|
||||
Range::from_located(stmt),
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers::unparse_stmt;
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::AlwaysAutofixableViolation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct DoNotAssertFalse;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for DoNotAssertFalse {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Do not `assert False` (`python -O` removes these calls), raise `AssertionError()`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Replace `assert False`".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn assertion_error(msg: Option<&Expr>) -> Stmt {
|
||||
Stmt::new(
|
||||
@@ -46,7 +61,7 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option
|
||||
return;
|
||||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(violations::DoNotAssertFalse, Range::from_located(test));
|
||||
let mut diagnostic = Diagnostic::new(DoNotAssertFalse, Range::from_located(test));
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
unparse_stmt(&assertion_error(msg), checker.stylist),
|
||||
|
||||
@@ -7,13 +7,23 @@
|
||||
//! typo. Either assert for a more specific exception (builtin or
|
||||
//! custom), use `assertRaisesRegex`, or use the context manager form of
|
||||
//! `assertRaises`.
|
||||
|
||||
use rustpython_ast::{ExprKind, Stmt, Withitem};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{ExprKind, Stmt, Withitem};
|
||||
|
||||
define_violation!(
|
||||
pub struct NoAssertRaisesException;
|
||||
);
|
||||
impl Violation for NoAssertRaisesException {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`assertRaises(Exception)` should be considered evil")
|
||||
}
|
||||
}
|
||||
|
||||
/// B017
|
||||
pub fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items: &[Withitem]) {
|
||||
@@ -41,7 +51,7 @@ pub fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items: &[With
|
||||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::NoAssertRaisesException,
|
||||
NoAssertRaisesException,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct AssignmentToOsEnviron;
|
||||
);
|
||||
impl Violation for AssignmentToOsEnviron {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Assigning to `os.environ` doesn't clear the environment")
|
||||
}
|
||||
}
|
||||
/// B003
|
||||
pub fn assignment_to_os_environ(checker: &mut Checker, targets: &[Expr]) {
|
||||
if targets.len() != 1 {
|
||||
@@ -24,7 +34,7 @@ pub fn assignment_to_os_environ(checker: &mut Checker, targets: &[Expr]) {
|
||||
return;
|
||||
}
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::AssignmentToOsEnviron,
|
||||
AssignmentToOsEnviron,
|
||||
Range::from_located(target),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::{Range, ScopeKind};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct CachedInstanceMethod;
|
||||
);
|
||||
impl Violation for CachedInstanceMethod {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
|
||||
checker.resolve_call_path(expr).map_or(false, |call_path| {
|
||||
@@ -35,7 +48,7 @@ pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
|
||||
},
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::CachedInstanceMethod,
|
||||
CachedInstanceMethod,
|
||||
Range::from_located(decorator),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct CannotRaiseLiteral;
|
||||
);
|
||||
impl Violation for CannotRaiseLiteral {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Cannot raise a literal. Did you intend to return it or raise an Exception?")
|
||||
}
|
||||
}
|
||||
|
||||
/// B016
|
||||
pub fn cannot_raise_literal(checker: &mut Checker, expr: &Expr) {
|
||||
@@ -11,7 +22,7 @@ pub fn cannot_raise_literal(checker: &mut Checker, expr: &Expr) {
|
||||
return;
|
||||
};
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::CannotRaiseLiteral,
|
||||
CannotRaiseLiteral,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,14 +1,50 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::helpers::unparse_expr;
|
||||
use crate::ast::types::{CallPath, Range};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::violations;
|
||||
use crate::violation::{AlwaysAutofixableViolation, Violation};
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location};
|
||||
|
||||
define_violation!(
|
||||
pub struct DuplicateTryBlockException {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for DuplicateTryBlockException {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let DuplicateTryBlockException { name } = self;
|
||||
format!("try-except block with duplicate exception `{name}`")
|
||||
}
|
||||
}
|
||||
define_violation!(
|
||||
pub struct DuplicateHandlerException {
|
||||
pub names: Vec<String>,
|
||||
}
|
||||
);
|
||||
impl AlwaysAutofixableViolation for DuplicateHandlerException {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let DuplicateHandlerException { names } = self;
|
||||
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}")
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"De-duplicate exceptions".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn type_pattern(elts: Vec<&Expr>) -> Expr {
|
||||
Expr::new(
|
||||
@@ -49,7 +85,7 @@ fn duplicate_handler_exceptions<'a>(
|
||||
// TODO(charlie): Handle "BaseException" and redundant exception aliases.
|
||||
if !duplicates.is_empty() {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
violations::DuplicateHandlerException {
|
||||
DuplicateHandlerException {
|
||||
names: duplicates
|
||||
.into_iter()
|
||||
.map(|call_path| call_path.join("."))
|
||||
@@ -115,7 +151,7 @@ pub fn duplicate_exceptions(checker: &mut Checker, handlers: &[Excepthandler]) {
|
||||
for (name, exprs) in duplicates {
|
||||
for expr in exprs {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::DuplicateTryBlockException {
|
||||
DuplicateTryBlockException {
|
||||
name: name.join("."),
|
||||
},
|
||||
Range::from_located(expr),
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
use rustpython_ast::{ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{ExprKind, Stmt, StmtKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct FStringDocstring;
|
||||
);
|
||||
impl Violation for FStringDocstring {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"f-string used as docstring. This will be interpreted by python as a joined string \
|
||||
rather than a docstring."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// B021
|
||||
pub fn f_string_docstring(checker: &mut Checker, body: &[Stmt]) {
|
||||
@@ -17,7 +31,7 @@ pub fn f_string_docstring(checker: &mut Checker, body: &[Stmt]) {
|
||||
return;
|
||||
};
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::FStringDocstring,
|
||||
FStringDocstring,
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
use rustpython_ast::{Arguments, Constant, Expr, ExprKind};
|
||||
|
||||
use super::mutable_argument_default::is_mutable_func;
|
||||
use crate::ast::helpers::{compose_call_path, to_call_path};
|
||||
use crate::ast::types::{CallPath, Range};
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::{Diagnostic, DiagnosticKind};
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Arguments, Constant, Expr, ExprKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct FunctionCallArgumentDefault {
|
||||
pub name: Option<String>,
|
||||
}
|
||||
);
|
||||
impl Violation for FunctionCallArgumentDefault {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FunctionCallArgumentDefault { name } = self;
|
||||
if let Some(name) = name {
|
||||
format!("Do not perform function call `{name}` in argument defaults")
|
||||
} else {
|
||||
format!("Do not perform function call in argument defaults")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const IMMUTABLE_FUNCS: &[&[&str]] = &[
|
||||
&["", "tuple"],
|
||||
@@ -48,7 +66,7 @@ where
|
||||
&& !is_nan_or_infinity(func, args)
|
||||
{
|
||||
self.diagnostics.push((
|
||||
violations::FunctionCallArgumentDefault {
|
||||
FunctionCallArgumentDefault {
|
||||
name: compose_call_path(expr),
|
||||
}
|
||||
.into(),
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{Comprehension, Expr, ExprContext, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers::collect_arg_names;
|
||||
use crate::ast::types::{Node, Range};
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::Violation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{Comprehension, Expr, ExprContext, ExprKind, Stmt, StmtKind};
|
||||
|
||||
define_violation!(
|
||||
pub struct FunctionUsesLoopVariable {
|
||||
pub name: String,
|
||||
}
|
||||
);
|
||||
impl Violation for FunctionUsesLoopVariable {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FunctionUsesLoopVariable { name } = self;
|
||||
format!("Function definition does not bind loop variable `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LoadedNamesVisitor<'a> {
|
||||
@@ -260,7 +274,7 @@ where
|
||||
if !checker.flake8_bugbear_seen.contains(&expr) {
|
||||
checker.flake8_bugbear_seen.push(expr);
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::FunctionUsesLoopVariable {
|
||||
FunctionUsesLoopVariable {
|
||||
name: name.to_string(),
|
||||
},
|
||||
range,
|
||||
|
||||
@@ -1,14 +1,31 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location};
|
||||
|
||||
use crate::ast::helpers::unparse_expr;
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::define_violation;
|
||||
use crate::fix::Fix;
|
||||
use crate::python::identifiers::{is_identifier, is_mangled_private};
|
||||
use crate::python::keyword::KWLIST;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::violations;
|
||||
use crate::violation::AlwaysAutofixableViolation;
|
||||
use ruff_macros::derive_message_formats;
|
||||
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location};
|
||||
|
||||
define_violation!(
|
||||
pub struct GetAttrWithConstant;
|
||||
);
|
||||
impl AlwaysAutofixableViolation for GetAttrWithConstant {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!(
|
||||
"Do not call `getattr` with a constant attribute value. It is not any safer than \
|
||||
normal property access."
|
||||
)
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
"Replace `getattr` with attribute access".to_string()
|
||||
}
|
||||
}
|
||||
fn attribute(value: &Expr, attr: &str) -> Expr {
|
||||
Expr::new(
|
||||
Location::default(),
|
||||
@@ -45,8 +62,7 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::GetAttrWithConstant, Range::from_located(expr));
|
||||
let mut diagnostic = Diagnostic::new(GetAttrWithConstant, Range::from_located(expr));
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user