Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dca994d05f | ||
|
|
9944246f98 | ||
|
|
82b0b7941a | ||
|
|
72453695d6 | ||
|
|
1617d715f2 | ||
|
|
c4a7344791 | ||
|
|
ea9acda732 | ||
|
|
6c8021e970 | ||
|
|
61b6ad46ea | ||
|
|
041d8108e6 | ||
|
|
e2c4a098de | ||
|
|
e865f58426 | ||
|
|
23b4e16b1d | ||
|
|
ae2ac905dc | ||
|
|
55619b321a | ||
|
|
6f31b002f8 | ||
|
|
1a79965aa0 | ||
|
|
16da183f8e | ||
|
|
3f689917cb | ||
|
|
a4a215e8a3 | ||
|
|
aa1c884910 | ||
|
|
7fb55c6d99 | ||
|
|
04ea523ad8 | ||
|
|
9897f81cf3 | ||
|
|
1a2559b001 | ||
|
|
721a1e9443 | ||
|
|
f38bba18ee | ||
|
|
14cf36f922 | ||
|
|
d28e026525 | ||
|
|
d19a8aa54d | ||
|
|
f299940452 | ||
|
|
e1ab7163ac | ||
|
|
9edc479c6c | ||
|
|
560558b814 | ||
|
|
bef601b994 | ||
|
|
7445d00b88 | ||
|
|
7c78d4e103 | ||
|
|
8b14f1b8cc | ||
|
|
5a6b51e623 |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.138
|
||||
rev: v0.0.145
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
37
Cargo.lock
generated
37
Cargo.lock
generated
@@ -290,6 +290,36 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
|
||||
dependencies = [
|
||||
"clap 4.0.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_command"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
|
||||
dependencies = [
|
||||
"clap 4.0.22",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_fig"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
|
||||
dependencies = [
|
||||
"clap 4.0.22",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.0.21"
|
||||
@@ -670,7 +700,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.138-dev.0"
|
||||
version = "0.0.145-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -1775,7 +1805,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.138"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1786,6 +1816,7 @@ dependencies = [
|
||||
"cachedir",
|
||||
"chrono",
|
||||
"clap 4.0.22",
|
||||
"clap_complete_command",
|
||||
"clearscreen",
|
||||
"colored",
|
||||
"common-path",
|
||||
@@ -1825,7 +1856,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.138"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.138"
|
||||
version = "0.0.145"
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -22,6 +22,7 @@ bitflags = { version = "1.3.2" }
|
||||
cachedir = { version = "0.3.0" }
|
||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
clap_complete_command = "0.4.0"
|
||||
colored = { version = "2.0.0" }
|
||||
common-path = { version = "1.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
|
||||
50
LICENSE
50
LICENSE
@@ -242,6 +242,56 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-debugger, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Joseph Kahn
|
||||
|
||||
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-eradicate, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Nikita Sobolev
|
||||
|
||||
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-tidy-imports, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
779
README.md
779
README.md
@@ -20,19 +20,18 @@ An extremely fast Python linter, written in Rust.
|
||||
- 🤝 Python 3.10 compatibility
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 `--fix` support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 👀 `--watch` support, for continuous file monitoring
|
||||
- 🔧 Autofix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- ⚖️ [Near-parity](#how-does-ruff-compare-to-flake8) with the built-in Flake8 rule set
|
||||
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) ([`pydocstyle`](https://pypi.org/project/pydocstyle/))
|
||||
- 🔌 Native re-implementations of popular Flake8 plugins, like [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
|
||||
|
||||
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
|
||||
functionality behind a single, common interface. Ruff can be used to replace Flake8 (plus a variety
|
||||
of plugins), [`isort`](https://pypi.org/project/isort/), [`pydocstyle`](https://pypi.org/project/pydocstyle/),
|
||||
[`yesqa`](https://github.com/asottile/yesqa), and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/)
|
||||
and [`autoflake`](https://pypi.org/project/autoflake/) all while executing tens or hundreds of times
|
||||
faster than any individual tool.
|
||||
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
|
||||
and even a subset of [`pyupgrade`](https://pypi.org/project/pyupgrade/) and [`autoflake`](https://pypi.org/project/autoflake/)
|
||||
all while executing tens or hundreds of times faster than any individual tool.
|
||||
|
||||
Ruff is actively developed and used in major open-source projects like:
|
||||
Ruff is extremely actively developed and used in major open-source projects like:
|
||||
|
||||
- [FastAPI](https://github.com/tiangolo/fastapi)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
@@ -43,6 +42,26 @@ Ruff is actively developed and used in major open-source projects like:
|
||||
|
||||
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
|
||||
## Testimonials
|
||||
|
||||
[**Sebastián Ramírez**](https://twitter.com/tiangolo/status/1591912354882764802), creator
|
||||
of [FastAPI](https://github.com/tiangolo/fastapi):
|
||||
|
||||
> Ruff is so fast that sometimes I add an intentional bug in the code just to confirm it's actually
|
||||
> running and checking the code.
|
||||
|
||||
[**Bryan Van de Ven**](https://github.com/bokeh/bokeh/pull/12605), co-creator
|
||||
of [Bokeh](https://github.com/bokeh/bokeh/), original author
|
||||
of [Conda](https://docs.conda.io/en/latest/):
|
||||
|
||||
> Ruff is ~150-200x faster than flake8 on my machine, scanning the whole repo takes ~0.2s instead of
|
||||
> ~20s. This is an enormous quality of life improvement for local dev. It's fast enough that I added
|
||||
> it as an actual commit hook, which is terrific.
|
||||
|
||||
[**Tim Abbott**](https://github.com/charliermarsh/ruff/issues/465#issuecomment-1317400028), lead developer of [Zulip](https://github.com/zulip/zulip):
|
||||
|
||||
> This is just ridiculously fast... `ruff` is amazing.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Installation and Usage](#installation-and-usage)
|
||||
@@ -54,17 +73,19 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
1. [pydocstyle (D)](#pydocstyle)
|
||||
1. [pyupgrade (U)](#pyupgrade)
|
||||
1. [pep8-naming (N)](#pep8-naming)
|
||||
1. [eradicate (ERA)](#eradicate)
|
||||
1. [flake8-bandit (S)](#flake8-bandit)
|
||||
1. [flake8-comprehensions (C)](#flake8-comprehensions)
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
|
||||
1. [flake8-bugbear (B)](#flake8-bugbear)
|
||||
1. [flake8-builtins (A)](#flake8-builtins)
|
||||
1. [flake8-debugger (T)](#flake8-debugger)
|
||||
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports)
|
||||
1. [flake8-print (T)](#flake8-print)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes)
|
||||
1. [flake8-annotations (ANN)](#flake8-annotations)
|
||||
1. [flake8-2020 (YTT)](#flake8-2020)
|
||||
1. [flake8-blind-except (BLE)](#flake8-blind-except)
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
|
||||
1. [mccabe (C90)](#mccabe)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules)
|
||||
1. [Meta rules (M)](#meta-rules)
|
||||
@@ -73,6 +94,7 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
1. [Development](#development)
|
||||
1. [Releases](#releases)
|
||||
1. [Benchmarks](#benchmarks)
|
||||
1. [Reference](#reference)
|
||||
1. [License](#license)
|
||||
1. [Contributing](#contributing)
|
||||
|
||||
@@ -80,12 +102,18 @@ Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-mu
|
||||
|
||||
### Installation
|
||||
|
||||
Available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
Ruff is available as [`ruff`](https://pypi.org/project/ruff/) on PyPI:
|
||||
|
||||
```shell
|
||||
pip install ruff
|
||||
```
|
||||
|
||||
If you're a **macOS Homebrew** or a **Linuxbrew** user, you can also install `ruff` via Homebrew:
|
||||
|
||||
```shell
|
||||
brew install ruff
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
To run Ruff, try any of the following:
|
||||
@@ -107,7 +135,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.138
|
||||
rev: v0.0.145
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -117,8 +145,10 @@ _Note: prior to `v0.0.86`, `ruff-pre-commit` used `lint` (rather than `ruff`) as
|
||||
|
||||
## Configuration
|
||||
|
||||
Ruff is configurable both via `pyproject.toml` and the command line. If left unspecified, the
|
||||
default configuration is equivalent to:
|
||||
Ruff is configurable both via `pyproject.toml` and the command line. For a full list of configurable
|
||||
options, see the [API reference](#reference).
|
||||
|
||||
If left unspecified, the default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
@@ -179,8 +209,10 @@ ignore = ["E501"]
|
||||
fix = true
|
||||
unfixable = ["F401"]
|
||||
|
||||
# Ignore `E402` (import violations in any `__init__.py` file, and in `path/to/file.py`.
|
||||
per-file-ignores = {"__init__.py" = ["E402"], "path/to/file.py" = ["E402"]}
|
||||
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
|
||||
[tool.ruff.per-file-ignores]
|
||||
"__init__.py" = ["E402"]
|
||||
"path/to/file.py" = ["E402"]
|
||||
```
|
||||
|
||||
Plugin configurations should be expressed as subsections, e.g.:
|
||||
@@ -194,7 +226,9 @@ select = ["E", "F", "Q"]
|
||||
docstring-quotes = "double"
|
||||
```
|
||||
|
||||
Alternatively, common configuration settings can be provided via the command-line:
|
||||
For a full list of configurable options, see the [API reference](#reference).
|
||||
|
||||
Some common configuration settings can be provided via the command-line:
|
||||
|
||||
```shell
|
||||
ruff path/to/code/ --select F401 --select F403
|
||||
@@ -271,21 +305,10 @@ Options:
|
||||
Print version information
|
||||
```
|
||||
|
||||
### Excluding files
|
||||
|
||||
Exclusions are based on globs, and can be either:
|
||||
|
||||
- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the
|
||||
tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching
|
||||
`foo_*.py` ).
|
||||
- Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py`
|
||||
(to exclude any Python files in `directory`). Note that these paths are relative to the
|
||||
project root (e.g., the directory containing your `pyproject.toml`).
|
||||
|
||||
### Ignoring errors
|
||||
|
||||
To omit a lint check entirely, add it to the "ignore" list via `--ignore` or `--extend-ignore`,
|
||||
either on the command-line or in your `project.toml` file.
|
||||
either on the command-line or in your `project.toml` file.
|
||||
|
||||
To ignore an error in-line, Ruff uses a `noqa` system similar to [Flake8](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html).
|
||||
To ignore an individual error, add `# noqa: {code}` to the end of the line, like so:
|
||||
@@ -350,7 +373,20 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
|
||||
| F405 | ImportStarUsage | `...` may be undefined, or defined from star imports: `...` | |
|
||||
| F406 | ImportStarNotPermitted | `from ... import *` only allowed at module level | |
|
||||
| F407 | FutureFeatureNotDefined | Future feature `...` is not defined | |
|
||||
| F501 | PercentFormatInvalidFormat | '...' % ... has invalid format string: ... | |
|
||||
| F502 | PercentFormatExpectedMapping | '...' % ... expected mapping but got sequence | |
|
||||
| F503 | PercentFormatExpectedSequence | '...' % ... expected sequence but got mapping | |
|
||||
| F504 | PercentFormatExtraNamedArguments | '...' % ... has unused named argument(s): ... | |
|
||||
| F505 | PercentFormatMissingArgument | '...' % ... is missing argument(s) for placeholder(s): ... | |
|
||||
| F506 | PercentFormatMixedPositionalAndNamed | '...' % ... has mixed positional and named placeholders | |
|
||||
| F507 | PercentFormatPositionalCountMismatch | '...' % ... has 4 placeholder(s) but 2 substitution(s) | |
|
||||
| F508 | PercentFormatStarRequiresSequence | '...' % ... `*` specifier requires sequence | |
|
||||
| F509 | PercentFormatUnsupportedFormatCharacter | '...' % ... has unsupported format character 'c' | |
|
||||
| F521 | StringDotFormatInvalidFormat | '...'.format(...) has invalid format string: ... | |
|
||||
| F522 | StringDotFormatExtraNamedArguments | '...'.format(...) has unused named argument(s): ... | |
|
||||
| F523 | StringDotFormatExtraPositionalArguments | '...'.format(...) has unused arguments at position(s): ... | |
|
||||
| F524 | StringDotFormatMissingArguments | '...'.format(...) is missing argument(s) for placeholder(s): ... | |
|
||||
| F525 | StringDotFormatMixingAutomatic | '...'.format(...) mixes automatic and manual numbering | |
|
||||
| F541 | FStringMissingPlaceholders | f-string without any placeholders | |
|
||||
| F601 | MultiValueRepeatedKeyLiteral | Dictionary key literal repeated | |
|
||||
| F602 | MultiValueRepeatedKeyVariable | Dictionary key `...` repeated | |
|
||||
@@ -498,6 +534,14 @@ For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyP
|
||||
| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` | |
|
||||
| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix | |
|
||||
|
||||
### eradicate
|
||||
|
||||
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ERA001 | CommentedCode | Found commented-out code | 🛠 |
|
||||
|
||||
### flake8-bandit
|
||||
|
||||
For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on PyPI.
|
||||
@@ -534,6 +578,14 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 |
|
||||
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
|
||||
|
||||
### flake8-debugger
|
||||
|
||||
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| T100 | Debugger | Import for `...` found | |
|
||||
|
||||
### flake8-boolean-trap
|
||||
|
||||
For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/0.1.0/) on PyPI.
|
||||
@@ -571,6 +623,7 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
|
||||
| B020 | LoopVariableOverridesIterator | Loop control variable `...` overrides iterable it iterates | |
|
||||
| B021 | FStringDocstring | f-string used as docstring. This will be interpreted by python as a joined string rather than a docstring. | |
|
||||
| B022 | UselessContextlibSuppress | No arguments passed to `contextlib.suppress`. No exceptions will be suppressed and therefore this context manager is redundant | |
|
||||
| B023 | FunctionUsesLoopVariable | Function definition does not bind loop variable `...` | |
|
||||
| B024 | AbstractBaseClassWithoutAbstractMethod | `...` is an abstract base class, but it has no abstract methods | |
|
||||
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | |
|
||||
| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged | |
|
||||
@@ -804,42 +857,44 @@ stylistic lint rules that are obviated by autoformatting.
|
||||
(Coming from Flake8? Try [`flake8-to-ruff`](https://pypi.org/project/flake8-to-ruff/) to
|
||||
automatically convert your existing configuration.)
|
||||
|
||||
Ruff can be used as a (near) drop-in replacement for Flake8 when used (1) without or with a small
|
||||
number of plugins, (2) alongside Black, and (3) on Python 3 code.
|
||||
Ruff can be used as a drop-in replacement for Flake8 when used (1) without or with a small number of
|
||||
plugins, (2) alongside Black, and (3) on Python 3 code.
|
||||
|
||||
Under those conditions Ruff is missing 14 rules related to string `.format` calls, 1 rule related
|
||||
to docstring parsing, and 1 rule related to redefined variables.
|
||||
Under those conditions, Ruff implements every rule in Flake8, with the exception of `F811`.
|
||||
|
||||
Ruff re-implements some of the most popular Flake8 plugins and related code quality tools natively,
|
||||
including:
|
||||
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
|
||||
natively, including:
|
||||
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (27/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
|
||||
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
- [`eradicate`](https://pypi.org/project/eradicate/)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
Beyond the rule set, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
|
||||
1. Ruff does not yet support a few Python 3.9 and 3.10 language features, including structural
|
||||
pattern matching and parenthesized context managers.
|
||||
2. Flake8 has a plugin architecture and supports writing custom lint rules. (To date, popular Flake8
|
||||
plugins have been re-implemented within Ruff directly.)
|
||||
2. Flake8 has a plugin architecture and supports writing custom lint rules. (Instead, popular Flake8
|
||||
plugins are re-implemented in Rust as part of Ruff itself.)
|
||||
|
||||
### Which tools does Ruff replace?
|
||||
|
||||
@@ -847,19 +902,21 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (27/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-blind-except`](https://pypi.org/project/flake8-blind-except/)
|
||||
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/)
|
||||
- [`flake8-builtins`](https://pypi.org/project/flake8-builtins/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
|
||||
Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),
|
||||
@@ -1141,6 +1198,614 @@ Summary
|
||||
389.73 ± 9.92 times faster than 'flake8 resources/test/cpython'
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
### Options
|
||||
|
||||
#### [`dummy_variable_rgx`](#dummy_variable_rgx)
|
||||
|
||||
A regular expression used to identify "dummy" variables, or those which should be ignored when evaluating
|
||||
(e.g.) unused-variable checks.
|
||||
|
||||
**Default value**: `"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"` (matches `_`, `__`, and `_var`, but not `_var_`)
|
||||
|
||||
**Type**: `Regex`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Only ignore variables named "_".
|
||||
dummy_variable_rgx = "^_$"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`exclude`](#exclude)
|
||||
|
||||
A list of file patterns to exclude from linting.
|
||||
|
||||
Exclusions are based on globs, and can be either:
|
||||
|
||||
- Single-path patterns, like `.mypy_cache` (to exclude any directory named `.mypy_cache` in the
|
||||
tree), `foo.py` (to exclude any file named `foo.py`), or `foo_*.py` (to exclude any file matching
|
||||
`foo_*.py` ).
|
||||
- Relative patterns, like `directory/foo.py` (to exclude that specific file) or `directory/*.py`
|
||||
(to exclude any Python files in `directory`). Note that these paths are relative to the
|
||||
project root (e.g., the directory containing your `pyproject.toml`).
|
||||
|
||||
Note that you'll typically want to use [`extend_exclude`](#extend_exclude) to modify the excluded
|
||||
paths.
|
||||
|
||||
**Default value**: `[".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv"]`
|
||||
|
||||
**Type**: `Vec<FilePattern>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
exclude = [".venv"]
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
#### [`extend_exclude`](#extend_exclude)
|
||||
|
||||
A list of file patterns to omit from linting, in addition to those specified by `exclude`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<FilePattern>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# In addition to the standard set of exclusions, omit all tests, plus a specific file.
|
||||
extend-exclude = ["tests", "src/bad.py"]
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
#### [`ignore`](#ignore)
|
||||
|
||||
A list of check code prefixes to ignore. Prefixes can specify exact checks (like `F841`), entire
|
||||
categories (like `F`), or anything in between.
|
||||
|
||||
When breaking ties between enabled and disabled checks (via `select` and `ignore`, respectively),
|
||||
more specific prefixes override less specific prefixes.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Skip unused variable checks (`F841`).
|
||||
ignore = ["F841"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`extend_ignore`](#extend_ignore)
|
||||
|
||||
A list of check code prefixes to ignore, in addition to those specified by `ignore`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Skip unused variable checks (`F841`).
|
||||
extend-ignore = ["F841"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`select`](#select)
|
||||
|
||||
A list of check code prefixes to enable. Prefixes can specify exact checks (like `F841`), entire
|
||||
categories (like `F`), or anything in between.
|
||||
|
||||
When breaking ties between enabled and disabled checks (via `select` and `ignore`, respectively),
|
||||
more specific prefixes override less specific prefixes.
|
||||
|
||||
**Default value**: `["E", "F"]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# On top of the defaults (`E`, `F`), enable flake8-bugbear (`B`) and flake8-quotes (`Q`).
|
||||
select = ["E", "F", "B", "Q"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`extend_select`](#extend_select)
|
||||
|
||||
A list of check code prefixes to enable, in addition to those specified by `select`.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# On top of the default `select` (`E`, `F`), enable flake8-bugbear (`B`) and flake8-quotes (`Q`).
|
||||
extend-select = ["B", "Q"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`fix`](#fix)
|
||||
|
||||
Enable autofix behavior by-default when running `ruff` (overridden by the `--fix` and `--no-fix`
|
||||
command-line flags).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
fix = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`fixable`](#fixable)
|
||||
|
||||
A list of check code prefixes to consider autofix-able.
|
||||
|
||||
**Default value**: `["A", "ANN", "B", "BLE", "C", "D", "E", "F", "FBT", "I", "M", "N", "Q", "RUF", "S", "T", "U", "W", "YTT"]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Only allow autofix behavior for `E` and `F` checks.
|
||||
fixable = ["E", "F"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`unfixable`](#unfixable)
|
||||
|
||||
A list of check code prefixes to consider un-autofix-able.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Disable autofix for unused imports (`F401`).
|
||||
unfixable = ["F401"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`line_length`](#line_length)
|
||||
|
||||
The line length to use when enforcing long-lines violations (like E501).
|
||||
|
||||
**Default value**: `88`
|
||||
|
||||
**Type**: `usize`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Allow lines to be as long as 120 characters.
|
||||
line-length = 120
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`per_file_ignores`](#per_file_ignores)
|
||||
|
||||
A list of mappings from file pattern to check code prefixes to exclude, when considering any
|
||||
matching files.
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
**Type**: `HashMap<String, Vec<CheckCodePrefix>>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
|
||||
[tool.ruff.per-file-ignores]
|
||||
"__init__.py" = ["E402"]
|
||||
"path/to/file.py" = ["E402"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`show_source`](#show_source)
|
||||
|
||||
Whether to show source code snippets when reporting lint error violations (overridden by the
|
||||
`--show-source` command-line flag).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# By default, always show source code snippets.
|
||||
show_source = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`src`](#src)
|
||||
|
||||
The source code paths to consider, e.g., when resolving first- vs. third-party imports.
|
||||
|
||||
**Default value**: `["."]`
|
||||
|
||||
**Type**: `Vec<PathBuf>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Allow imports relative to the "src" and "test" directories.
|
||||
src = ["src", "test"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`target_version`](#target_version)
|
||||
|
||||
The Python version to target, e.g., when considering automatic code upgrades, like rewriting type
|
||||
annotations. Note that the target version will _not_ be inferred from the _current_ Python version,
|
||||
and instead must be specified explicitly (as seen below).
|
||||
|
||||
**Default value**: `"py310"`
|
||||
|
||||
**Type**: `PythonVersion`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Always generate Python 3.7-compatible code.
|
||||
target-version = "py37"
|
||||
```
|
||||
|
||||
### `flake8-annotations`
|
||||
|
||||
#### [`mypy_init_return`](#mypy_init_return)
|
||||
|
||||
Whether to allow the omission of a return type hint for `__init__` if at least one argument is
|
||||
annotated.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
mypy_init_return = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`suppress_dummy_args`](#suppress_dummy_args)
|
||||
|
||||
Whether to suppress `ANN000`-level errors for arguments matching the "dummy" variable regex (like
|
||||
`_`).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
suppress_dummy_args = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`suppress_none_returning`](#suppress_none_returning)
|
||||
|
||||
Whether to suppress `ANN200`-level errors for functions that meet either of the following criteria:
|
||||
|
||||
- Contain no `return` statement.
|
||||
- Explicit `return` statement(s) all return `None` (explicitly or implicitly).
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
suppress_none_returning = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`allow_star_arg_any`](#allow_star_arg_any)
|
||||
|
||||
Whether to suppress `ANN401` for dynamically typed `*args` and `**kwargs` arguments.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-annotations]
|
||||
allow_star_arg_any = true
|
||||
```
|
||||
|
||||
### `flake8-bugbear`
|
||||
|
||||
#### [`extend_immutable_calls`](#extend_immutable_calls)
|
||||
|
||||
Additional callable functions to consider "immutable" when evaluating, e.g., no-mutable-default-argument
|
||||
checks (`B006`).
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-bugbear]
|
||||
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
|
||||
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
|
||||
```
|
||||
|
||||
### `flake8-quotes`
|
||||
|
||||
#### [`inline_quotes`](#inline_quotes)
|
||||
|
||||
Quote style to prefer for inline strings (either "single" (`'`) or "double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
**Type**: `Quote`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`multiline_quotes`](#multiline_quotes)
|
||||
|
||||
Quote style to prefer for multiline strings (either "single" (`'`) or "double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
**Type**: `Quote`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
multiline-quotes = "single"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`docstring_quotes`](#docstring_quotes)
|
||||
|
||||
Quote style to prefer for docstrings (either "single" (`'`) or "double" (`"`)).
|
||||
|
||||
**Default value**: `"double"`
|
||||
|
||||
**Type**: `Quote`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
docstring-quotes = "single"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`avoid_escape`](#avoid_escape)
|
||||
|
||||
Whether to avoid using single quotes if a string contains single quotes, or vice-versa with
|
||||
double quotes, as per [PEP8](https://peps.python.org/pep-0008/#string-quotes). This minimizes the
|
||||
need to escape quotation marks within strings.
|
||||
|
||||
**Default value**: `true`
|
||||
|
||||
**Type**: `bool`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-quotes]
|
||||
# Don't bother trying to avoid escapes.
|
||||
avoid-escape = false
|
||||
```
|
||||
|
||||
### `flake8-tidy-imports`
|
||||
|
||||
#### [`ban_relative_imports`](#ban_relative_imports)
|
||||
|
||||
Whether to ban all relative imports (`"all"`), or only those imports that extend into the parent
|
||||
module and beyond (`"parents"`).
|
||||
|
||||
**Default value**: `"parents"`
|
||||
|
||||
**Type**: `Strictness`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
# Disallow all relative imports.
|
||||
ban-relative-imports = "all"
|
||||
```
|
||||
|
||||
### `isort`
|
||||
|
||||
#### [`known_first_party`](known_first_party)
|
||||
|
||||
A list of modules to consider first-party, regardless of whether they can be identified as such
|
||||
via introspection of the local filesystem.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["src"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`known_third_party`](known_third_party)
|
||||
|
||||
A list of modules to consider third-party, regardless of whether they can be identified as such
|
||||
via introspection of the local filesystem.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
known-third-party = ["fastapi"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`extra_standard_library`](extra_standard_library)
|
||||
|
||||
A list of modules to consider standard-library, in addition to those known to Ruff in advance.
|
||||
|
||||
**Default value**: `[]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
extra-standard-library = ["path"]
|
||||
```
|
||||
|
||||
### `mccabe`
|
||||
|
||||
#### [`max_complexity`](#max_complexity)
|
||||
|
||||
The maximum McCabe complexity to allow before triggering `C901` errors.
|
||||
|
||||
**Default value**: `10`
|
||||
|
||||
**Type**: `usize`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
# Flag errors (`C901`) whenever the complexity level exceeds 5.
|
||||
max-complexity = 5
|
||||
```
|
||||
|
||||
### `pep8-naming`
|
||||
|
||||
#### [`ignore_names`](#ignore_names)
|
||||
|
||||
A list of names to ignore when considering `pep8-naming` violations.
|
||||
|
||||
**Default value**: `["setUp", "tearDown", "setUpClass", "tearDownClass", "setUpModule", "tearDownModule", "asyncSetUp", "asyncTearDown", "setUpTestData", "failureException", "longMessage", "maxDiff"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.pep8-naming]
|
||||
ignore-names = ["callMethod"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`classmethod_decorators`](#classmethod_decorators)
|
||||
|
||||
A list of decorators that, when applied to a method, indicate that the method should be treated as
|
||||
a class method. For example, Ruff will expect that any method decorated by a decorator in this list
|
||||
takes a `cls` argument as its first argument.
|
||||
|
||||
**Default value**: `["classmethod"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.pep8-naming]
|
||||
# Allow Pydantic's `@validator` decorator to trigger class method treatment.
|
||||
classmethod-decorators = ["classmethod", "pydantic.validator"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`staticmethod_decorators`](#staticmethod_decorators)
|
||||
|
||||
A list of decorators that, when applied to a method, indicate that the method should be treated as
|
||||
a static method. For example, Ruff will expect that any method decorated by a decorator in this list
|
||||
has no `self` or `cls` argument.
|
||||
|
||||
**Default value**: `["staticmethod"]`
|
||||
|
||||
**Type**: `Vec<String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.pep8-naming]
|
||||
# Allow a shorthand alias, `@stcmthd`, to trigger static method treatment.
|
||||
staticmethod-decorators = ["staticmethod", "stcmthd"]
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.138"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.138"
|
||||
version = "0.0.145"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.138-dev.0"
|
||||
version = "0.0.145-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -6,17 +6,19 @@ use ruff::checks_gen::CheckCodePrefix;
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Plugin {
|
||||
Flake8Annotations,
|
||||
Flake8Bandit,
|
||||
Flake8BlindExcept,
|
||||
Flake8Bugbear,
|
||||
Flake8Builtins,
|
||||
Flake8Comprehensions,
|
||||
Flake8Debugger,
|
||||
Flake8Docstrings,
|
||||
Flake8TidyImports,
|
||||
Flake8Eradicate,
|
||||
Flake8Print,
|
||||
Flake8Quotes,
|
||||
Flake8Annotations,
|
||||
Flake8TidyImports,
|
||||
McCabe,
|
||||
Flake8BlindExcept,
|
||||
PEP8Naming,
|
||||
Pyupgrade,
|
||||
}
|
||||
@@ -26,16 +28,18 @@ impl FromStr for Plugin {
|
||||
|
||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||
match string {
|
||||
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
||||
"flake8-bandit" => Ok(Plugin::Flake8Bandit),
|
||||
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
|
||||
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
|
||||
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
|
||||
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
|
||||
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
|
||||
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
|
||||
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
|
||||
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
|
||||
"flake8-print" => Ok(Plugin::Flake8Print),
|
||||
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
|
||||
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
|
||||
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
|
||||
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
|
||||
"mccabe" => Ok(Plugin::McCabe),
|
||||
"pep8-naming" => Ok(Plugin::PEP8Naming),
|
||||
"pyupgrade" => Ok(Plugin::Pyupgrade),
|
||||
@@ -47,16 +51,18 @@ impl FromStr for Plugin {
|
||||
impl Plugin {
|
||||
pub fn default(&self) -> CheckCodePrefix {
|
||||
match self {
|
||||
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
||||
Plugin::Flake8Bandit => CheckCodePrefix::S,
|
||||
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
|
||||
Plugin::Flake8Bugbear => CheckCodePrefix::B,
|
||||
Plugin::Flake8Builtins => CheckCodePrefix::A,
|
||||
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
|
||||
Plugin::Flake8Debugger => CheckCodePrefix::T1,
|
||||
Plugin::Flake8Docstrings => CheckCodePrefix::D,
|
||||
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
|
||||
Plugin::Flake8Print => CheckCodePrefix::T,
|
||||
Plugin::Flake8Eradicate => CheckCodePrefix::ERA,
|
||||
Plugin::Flake8Print => CheckCodePrefix::T2,
|
||||
Plugin::Flake8Quotes => CheckCodePrefix::Q,
|
||||
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
|
||||
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
|
||||
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
|
||||
Plugin::McCabe => CheckCodePrefix::C9,
|
||||
Plugin::PEP8Naming => CheckCodePrefix::N,
|
||||
Plugin::Pyupgrade => CheckCodePrefix::U,
|
||||
@@ -65,10 +71,13 @@ impl Plugin {
|
||||
|
||||
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
|
||||
match self {
|
||||
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
|
||||
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
|
||||
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
|
||||
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
|
||||
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
|
||||
Plugin::Flake8Debugger => vec![CheckCodePrefix::T1],
|
||||
Plugin::Flake8Docstrings => {
|
||||
// Use the user-provided docstring.
|
||||
for key in ["docstring-convention", "docstring_convention"] {
|
||||
@@ -85,11 +94,10 @@ impl Plugin {
|
||||
// Default to PEP8.
|
||||
DocstringConvention::PEP8.select()
|
||||
}
|
||||
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||
Plugin::Flake8Print => vec![CheckCodePrefix::T],
|
||||
Plugin::Flake8Eradicate => vec![CheckCodePrefix::ERA],
|
||||
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
|
||||
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
|
||||
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||
Plugin::McCabe => vec![CheckCodePrefix::C9],
|
||||
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
|
||||
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
|
||||
@@ -277,31 +285,6 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
let mut plugins = BTreeSet::new();
|
||||
for key in flake8.keys() {
|
||||
match key.as_str() {
|
||||
// flake8-docstrings
|
||||
"docstring-convention" | "docstring_convention" => {
|
||||
plugins.insert(Plugin::Flake8Docstrings);
|
||||
}
|
||||
// flake8-bugbear
|
||||
"extend-immutable-calls" | "extend_immutable_calls" => {
|
||||
plugins.insert(Plugin::Flake8Bugbear);
|
||||
}
|
||||
// flake8-builtins
|
||||
"builtins-ignorelist" | "builtins_ignorelist" => {
|
||||
plugins.insert(Plugin::Flake8Builtins);
|
||||
}
|
||||
// flake8-quotes
|
||||
"quotes" | "inline-quotes" | "inline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"multiline-quotes" | "multiline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"docstring-quotes" | "docstring_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"avoid-escape" | "avoid_escape" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
// flake8-annotations
|
||||
"suppress-none-returning" | "suppress_none_returning" => {
|
||||
plugins.insert(Plugin::Flake8Annotations);
|
||||
@@ -327,6 +310,41 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
"allow-star-arg-any" | "allow_star_arg_any" => {
|
||||
plugins.insert(Plugin::Flake8Annotations);
|
||||
}
|
||||
// flake8-bugbear
|
||||
"extend-immutable-calls" | "extend_immutable_calls" => {
|
||||
plugins.insert(Plugin::Flake8Bugbear);
|
||||
}
|
||||
// flake8-builtins
|
||||
"builtins-ignorelist" | "builtins_ignorelist" => {
|
||||
plugins.insert(Plugin::Flake8Builtins);
|
||||
}
|
||||
// flake8-docstrings
|
||||
"docstring-convention" | "docstring_convention" => {
|
||||
plugins.insert(Plugin::Flake8Docstrings);
|
||||
}
|
||||
// flake8-eradicate
|
||||
"eradicate-aggressive" | "eradicate_aggressive" => {
|
||||
plugins.insert(Plugin::Flake8Eradicate);
|
||||
}
|
||||
"eradicate-whitelist" | "eradicate_whitelist" => {
|
||||
plugins.insert(Plugin::Flake8Eradicate);
|
||||
}
|
||||
"eradicate-whitelist-extend" | "eradicate_whitelist_extend" => {
|
||||
plugins.insert(Plugin::Flake8Eradicate);
|
||||
}
|
||||
// flake8-quotes
|
||||
"quotes" | "inline-quotes" | "inline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"multiline-quotes" | "multiline_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"docstring-quotes" | "docstring_quotes" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
"avoid-escape" | "avoid_escape" => {
|
||||
plugins.insert(Plugin::Flake8Quotes);
|
||||
}
|
||||
// flake8-tidy-imports
|
||||
"ban-relative-imports" | "ban_relative_imports" => {
|
||||
plugins.insert(Plugin::Flake8TidyImports);
|
||||
@@ -360,16 +378,18 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
/// `flake8-annotations` is active.
|
||||
pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin> {
|
||||
[
|
||||
Plugin::Flake8Annotations,
|
||||
Plugin::Flake8Bandit,
|
||||
Plugin::Flake8BlindExcept,
|
||||
Plugin::Flake8Bugbear,
|
||||
Plugin::Flake8Builtins,
|
||||
Plugin::Flake8Comprehensions,
|
||||
Plugin::Flake8Debugger,
|
||||
Plugin::Flake8Docstrings,
|
||||
Plugin::Flake8TidyImports,
|
||||
Plugin::Flake8Eradicate,
|
||||
Plugin::Flake8Print,
|
||||
Plugin::Flake8Quotes,
|
||||
Plugin::Flake8Annotations,
|
||||
Plugin::Flake8BlindExcept,
|
||||
Plugin::Flake8TidyImports,
|
||||
Plugin::PEP8Naming,
|
||||
Plugin::Pyupgrade,
|
||||
]
|
||||
|
||||
82
resources/test/fixtures/B023.py
vendored
Normal file
82
resources/test/fixtures/B023.py
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
"""
|
||||
Should emit:
|
||||
B023 - on lines 12, 13, 16, 28, 29, 30, 31, 40, 42, 50, 51, 52, 53, 61, 68.
|
||||
"""
|
||||
|
||||
functions = []
|
||||
z = 0
|
||||
|
||||
for x in range(3):
|
||||
y = x + 1
|
||||
# Subject to late-binding problems
|
||||
functions.append(lambda: x)
|
||||
functions.append(lambda: y) # not just the loop var
|
||||
|
||||
def f_bad_1():
|
||||
return x
|
||||
|
||||
# Actually OK
|
||||
functions.append(lambda x: x * 2)
|
||||
functions.append(lambda x=x: x)
|
||||
functions.append(lambda: z) # OK because not assigned in the loop
|
||||
|
||||
def f_ok_1(x):
|
||||
return x * 2
|
||||
|
||||
|
||||
def check_inside_functions_too():
|
||||
ls = [lambda: x for x in range(2)]
|
||||
st = {lambda: x for x in range(2)}
|
||||
gn = (lambda: x for x in range(2))
|
||||
dt = {x: lambda: x for x in range(2)}
|
||||
|
||||
|
||||
async def pointless_async_iterable():
|
||||
yield 1
|
||||
|
||||
|
||||
async def container_for_problems():
|
||||
async for x in pointless_async_iterable():
|
||||
functions.append(lambda: x)
|
||||
|
||||
[lambda: x async for x in pointless_async_iterable()]
|
||||
|
||||
|
||||
a = 10
|
||||
b = 0
|
||||
while True:
|
||||
a = a_ = a - 1
|
||||
b += 1
|
||||
functions.append(lambda: a)
|
||||
functions.append(lambda: a_)
|
||||
functions.append(lambda: b)
|
||||
functions.append(lambda: c) # not a name error because of late binding!
|
||||
c: bool = a > 3
|
||||
if not c:
|
||||
break
|
||||
|
||||
# Nested loops should not duplicate reports
|
||||
for j in range(2):
|
||||
for k in range(3):
|
||||
lambda: j * k
|
||||
|
||||
|
||||
for j, k, l in [(1, 2, 3)]:
|
||||
|
||||
def f():
|
||||
j = None # OK because it's an assignment
|
||||
[l for k in range(2)] # error for l, not for k
|
||||
|
||||
assert a and functions
|
||||
|
||||
a.attribute = 1 # modifying an attribute doesn't make it a loop variable
|
||||
functions[0] = lambda: None # same for an element
|
||||
|
||||
for var in range(2):
|
||||
|
||||
def explicit_capture(captured=var):
|
||||
return captured
|
||||
|
||||
|
||||
for i in range(3):
|
||||
lambda: f"{i}"
|
||||
7
resources/test/fixtures/D.py
vendored
7
resources/test/fixtures/D.py
vendored
@@ -3,6 +3,7 @@ from functools import wraps
|
||||
import os
|
||||
from .expected import Expectation
|
||||
from typing import overload
|
||||
from typing_extensions import override
|
||||
|
||||
|
||||
expectation = Expectation()
|
||||
@@ -42,9 +43,13 @@ class class_:
|
||||
"D418: Function/ Method decorated with @overload"
|
||||
" shouldn't contain a docstring")
|
||||
|
||||
@override
|
||||
def overridden_method(a):
|
||||
return str(a)
|
||||
|
||||
@property
|
||||
def foo(self):
|
||||
"""The foo of the thing, which isn't in imperitive mood."""
|
||||
"""The foo of the thing, which isn't in imperative mood."""
|
||||
return "hello"
|
||||
|
||||
@expect('D102: Missing docstring in public method')
|
||||
|
||||
6
resources/test/fixtures/E501.py
vendored
6
resources/test/fixtures/E501.py
vendored
@@ -49,3 +49,9 @@ sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor
|
||||
sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
""", # noqa: E501
|
||||
}
|
||||
|
||||
# OK
|
||||
# A very long URL: https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
|
||||
|
||||
# OK
|
||||
# https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
|
||||
|
||||
13
resources/test/fixtures/ERA001.py
vendored
Normal file
13
resources/test/fixtures/ERA001.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#import os
|
||||
# from foo import junk
|
||||
#a = 3
|
||||
a = 4
|
||||
#foo(1, 2, 3)
|
||||
|
||||
def foo(x, y, z):
|
||||
contentet = 1 # print('hello')
|
||||
print(x, y, z)
|
||||
|
||||
# This is a real comment.
|
||||
#return True
|
||||
return False
|
||||
13
resources/test/fixtures/F502.py
vendored
Normal file
13
resources/test/fixtures/F502.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
dog = {"bob": "bob"}
|
||||
|
||||
"%(bob)s" % dog
|
||||
"%(bob)s" % {"bob": "bob"}
|
||||
"%(bob)s" % {**{"bob": "bob"}}
|
||||
"%(bob)s" % ["bob"] # F202
|
||||
"%(bob)s" % ("bob",) # F202
|
||||
"%(bob)s" % {"bob"} # F202
|
||||
"%(bob)s" % [*["bob"]] # F202
|
||||
"%(bob)s" % {"bob": "bob" for _ in range(1)}
|
||||
"%(bob)s" % ["bob" for _ in range(1)] # F202
|
||||
"%(bob)s" % ("bob" for _ in range(1)) # F202
|
||||
"%(bob)s" % {"bob" for _ in range(1)} # F202
|
||||
26
resources/test/fixtures/F503.py
vendored
Normal file
26
resources/test/fixtures/F503.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
dog = {"bob": "bob"}
|
||||
|
||||
# Single placeholder always fine
|
||||
"%s" % dog
|
||||
"%s" % {"bob": "bob"}
|
||||
"%s" % {**{"bob": "bob"}}
|
||||
"%s" % ["bob"]
|
||||
"%s" % ("bob",)
|
||||
"%s" % {"bob"}
|
||||
"%s" % [*["bob"]]
|
||||
"%s" % {"bob": "bob" for _ in range(1)}
|
||||
"%s" % ["bob" for _ in range(1)]
|
||||
"%s" % ("bob" for _ in range(1))
|
||||
"%s" % {"bob" for _ in range(1)}
|
||||
# Multiple placeholders
|
||||
"%s %s" % dog
|
||||
"%s %s" % {"bob": "bob"} # F503
|
||||
"%s %s" % {**{"bob": "bob"}} # F503
|
||||
"%s %s" % ["bob"]
|
||||
"%s %s" % ("bob",)
|
||||
"%s %s" % {"bob"}
|
||||
"%s %s" % [*["bob"]]
|
||||
"%s %s" % {"bob": "bob" for _ in range(1)} # F503
|
||||
"%s %s" % ["bob" for _ in range(1)]
|
||||
"%s %s" % ("bob" for _ in range(1))
|
||||
"%s %s" % {"bob" for _ in range(1)}
|
||||
6
resources/test/fixtures/F504.py
vendored
Normal file
6
resources/test/fixtures/F504.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Ruff has no way of knowing if the following are F505s
|
||||
a = "wrong"
|
||||
"%(a)s %(c)s" % {a: "?", "b": "!"} # F504 ("b" not used)
|
||||
|
||||
hidden = {"a": "!"}
|
||||
"%(a)s %(c)s" % {"x": 1, **hidden} # Ok (cannot see through splat)
|
||||
25
resources/test/fixtures/F50x.py
vendored
Normal file
25
resources/test/fixtures/F50x.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
'%(foo)' % {'foo': 'bar'} # F501
|
||||
'%s %(foo)s' % {'foo': 'bar'} # F506
|
||||
'%(foo)s %s' % {'foo': 'bar'} # F506
|
||||
'%j' % (1,) # F509
|
||||
'%s %s' % (1,) # F507
|
||||
'%s %s' % (1, 2, 3) # F507
|
||||
'%(bar)s' % {} # F505
|
||||
'%(bar)s' % {'bar': 1, 'baz': 2} # F504
|
||||
'%(bar)s' % (1, 2, 3) # F502
|
||||
'%s %s' % {'k': 'v'} # F503
|
||||
'%(bar)*s' % {'bar': 'baz'} # F506, F508
|
||||
|
||||
# ok: single %s with mapping
|
||||
'%s' % {'foo': 'bar', 'baz': 'womp'}
|
||||
# ok: %% should not count towards placeholder count
|
||||
'%% %s %% %s' % (1, 2)
|
||||
# ok: * consumes one positional argument
|
||||
'%.*f' % (2, 1.1234)
|
||||
'%*.*f' % (5, 2, 3.1234)
|
||||
# ok *args and **kwargs
|
||||
a = []
|
||||
'%s %s' % [*a]
|
||||
'%s %s' % (*a,)
|
||||
k = {}
|
||||
'%(k)s' % {**k}
|
||||
8
resources/test/fixtures/F521.py
vendored
8
resources/test/fixtures/F521.py
vendored
@@ -8,14 +8,6 @@
|
||||
"{foo..}".format(foo=1)
|
||||
"{foo..bar}".format(foo=1)
|
||||
|
||||
# "{} {1}".format(1, 2) # F525
|
||||
# "{0} {}".format(1, 2) # F525
|
||||
# "{}".format(1, 2) # F523
|
||||
# "{}".format(1, bar=2) # F522
|
||||
# "{} {}".format(1) # F524
|
||||
# "{2}".format() # F524
|
||||
# "{bar}".format() # F524
|
||||
|
||||
# The following are all "good" uses of .format
|
||||
"{.__class__}".format("")
|
||||
"{foo[bar]}".format(foo={"bar": "barv"})
|
||||
|
||||
4
resources/test/fixtures/F522.py
vendored
Normal file
4
resources/test/fixtures/F522.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
"{}".format(1, bar=2) # F522
|
||||
"{bar}{}".format(1, bar=2, spam=3) # F522
|
||||
"{bar:{spam}}".format(bar=2, spam=3) # No issues
|
||||
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||
12
resources/test/fixtures/F523.py
vendored
Normal file
12
resources/test/fixtures/F523.py
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# With indexes
|
||||
"{0}".format(1, 2) # F523
|
||||
"{1}".format(1, 2, 3) # F523
|
||||
"{1:{0}}".format(1, 2) # No issues
|
||||
"{1:{0}}".format(1, 2, 3) # F523
|
||||
"{0}{2}".format(1, 2) # F523, # F524
|
||||
|
||||
# With no indexes
|
||||
"{}".format(1, 2) # F523
|
||||
"{}".format(1, 2, 3) # F523
|
||||
"{:{}}".format(1, 2) # No issues
|
||||
"{:{}}".format(1, 2, 3) # F523
|
||||
6
resources/test/fixtures/F524.py
vendored
Normal file
6
resources/test/fixtures/F524.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"{} {}".format(1) # F524
|
||||
"{2}".format() # F524
|
||||
"{bar}".format() # F524
|
||||
"{0} {bar}".format(1) # F524
|
||||
"{0} {bar}".format() # F524
|
||||
"{bar} {0}".format() # F524
|
||||
2
resources/test/fixtures/F525.py
vendored
Normal file
2
resources/test/fixtures/F525.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"{} {1}".format(1, 2) # F525
|
||||
"{0} {}".format(1, 2) # F523, F525
|
||||
10
resources/test/fixtures/F821_3.py
vendored
10
resources/test/fixtures/F821_3.py
vendored
@@ -12,3 +12,13 @@ x: dict["key", "value"]
|
||||
|
||||
# OK
|
||||
x: dict[str, str]
|
||||
|
||||
# OK
|
||||
def unimportant(name):
|
||||
pass
|
||||
|
||||
|
||||
def dang(dict, set, list):
|
||||
unimportant(name=dict["name"])
|
||||
unimportant(name=set["name"])
|
||||
unimportant(name=list["name"])
|
||||
|
||||
15
resources/test/fixtures/FBT.py
vendored
15
resources/test/fixtures/FBT.py
vendored
@@ -38,5 +38,20 @@ def function(
|
||||
def used(do):
|
||||
return do
|
||||
|
||||
|
||||
used("a", True)
|
||||
used(do=True)
|
||||
|
||||
|
||||
# Avoid FBT003 for explicitly allowed methods.
|
||||
"""
|
||||
FBT003 Boolean positional value on dict
|
||||
"""
|
||||
a = {"a": "b"}
|
||||
a.get("hello", False)
|
||||
{}.get("hello", False)
|
||||
{}.setdefault("hello", True)
|
||||
{}.pop("hello", False)
|
||||
{}.pop(True, False)
|
||||
dict.fromkeys(("world",), True)
|
||||
{}.deploy(True, False)
|
||||
|
||||
14
resources/test/fixtures/T100.py
vendored
Normal file
14
resources/test/fixtures/T100.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
breakpoint()
|
||||
|
||||
|
||||
import pdb
|
||||
from builtins import breakpoint
|
||||
from pdb import set_trace as st
|
||||
from celery.contrib.rdb import set_trace
|
||||
from celery.contrib import rdb
|
||||
import celery.contrib.rdb
|
||||
|
||||
|
||||
breakpoint()
|
||||
st()
|
||||
set_trace()
|
||||
2
resources/test/fixtures/U009_0.py
vendored
2
resources/test/fixtures/U009_0.py
vendored
@@ -1,3 +1,3 @@
|
||||
# coding=utf8
|
||||
|
||||
print('Hello world')
|
||||
print("Hello world")
|
||||
|
||||
3
resources/test/fixtures/U009_4.py
vendored
Normal file
3
resources/test/fixtures/U009_4.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# coding=utf8 # noqa: U009
|
||||
|
||||
print("Hello world")
|
||||
42
resources/test/fixtures/U015.py
vendored
42
resources/test/fixtures/U015.py
vendored
@@ -36,3 +36,45 @@ with open("foo", "U") as fa, open("bar", "U") as fb:
|
||||
pass
|
||||
with open("foo", "Ub") as fa, open("bar", "Ub") as fb:
|
||||
pass
|
||||
|
||||
open("foo", mode="U")
|
||||
open(name="foo", mode="U")
|
||||
open(mode="U", name="foo")
|
||||
|
||||
with open("foo", mode="U") as f:
|
||||
pass
|
||||
with open(name="foo", mode="U") as f:
|
||||
pass
|
||||
with open(mode="U", name="foo") as f:
|
||||
pass
|
||||
|
||||
open("foo", mode="Ub")
|
||||
open(name="foo", mode="Ub")
|
||||
open(mode="Ub", name="foo")
|
||||
|
||||
with open("foo", mode="Ub") as f:
|
||||
pass
|
||||
with open(name="foo", mode="Ub") as f:
|
||||
pass
|
||||
with open(mode="Ub", name="foo") as f:
|
||||
pass
|
||||
|
||||
open(file="foo", mode='U', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='U')
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='U', newline=None, closefd=True, opener=None)
|
||||
open(mode='U', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
|
||||
open(file="foo", mode='Ub', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='Ub')
|
||||
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='Ub', newline=None, closefd=True, opener=None)
|
||||
open(mode='Ub', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
|
||||
|
||||
open = 1
|
||||
open("foo", "U")
|
||||
open("foo", "Ur")
|
||||
open("foo", "Ub")
|
||||
open("foo", "rUb")
|
||||
open("foo", "r")
|
||||
open("foo", "rt")
|
||||
open("f", "r", encoding="UTF-8")
|
||||
open("f", "wt")
|
||||
|
||||
@@ -44,7 +44,7 @@ expectation.expected.add((
|
||||
@expect("D407: Missing dashed underline after section ('Returns')",
|
||||
arg_count=3)
|
||||
@expect("D413: Missing blank line after last section ('Raises')", arg_count=3)
|
||||
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
|
||||
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None, **kwargs):
|
||||
"""Fetches rows from a Bigtable.
|
||||
|
||||
Retrieves rows pertaining to the given keys from the Table instance
|
||||
@@ -57,6 +57,7 @@ def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
|
||||
to fetch.
|
||||
other_silly_variable: Another optional variable, that has a much
|
||||
longer name than the other args, and which does nothing.
|
||||
**kwargs: More keyword arguments.
|
||||
|
||||
Returns:
|
||||
A dict mapping keys to the corresponding table row data
|
||||
|
||||
@@ -73,7 +73,7 @@ expectation.expected.add((
|
||||
"(found 'A')", arg_count=3)
|
||||
@expect("D413: Missing blank line after last section ('Examples')",
|
||||
arg_count=3)
|
||||
def foo(var1, var2, long_var_name='hi'):
|
||||
def foo(var1, var2, long_var_name='hi', **kwargs):
|
||||
r"""A one-line summary that does not use variable names.
|
||||
|
||||
Several sentences providing an extended description. Refer to
|
||||
@@ -91,6 +91,8 @@ def foo(var1, var2, long_var_name='hi'):
|
||||
detail, e.g. ``(N,) ndarray`` or ``array_like``.
|
||||
long_var_name : {'hi', 'ho'}, optional
|
||||
Choices in brackets, default first when optional.
|
||||
**kwargs : int
|
||||
More keyword arguments.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
10
resources/test/fixtures/future_annotations.py
vendored
10
resources/test/fixtures/future_annotations.py
vendored
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Optional
|
||||
|
||||
from models import (
|
||||
Fruit,
|
||||
@@ -28,3 +29,12 @@ class Foo:
|
||||
@classmethod
|
||||
def d(cls) -> Fruit:
|
||||
return cls(x=0, y=0)
|
||||
|
||||
|
||||
def f(x: int) -> List[int]:
|
||||
y = List[int]()
|
||||
y.append(x)
|
||||
return y
|
||||
|
||||
|
||||
x: Optional[int] = None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.138"
|
||||
version = "0.0.145"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -117,7 +117,8 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
|
||||
// Construct the output contents.
|
||||
let mut output = String::new();
|
||||
output.push_str("//! File automatically generated by examples/generate_check_code_prefix.rs.");
|
||||
output
|
||||
.push_str("//! File automatically generated by `examples/generate_check_code_prefix.rs`.");
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
output.push_str("use serde::{{Serialize, Deserialize}};");
|
||||
@@ -129,6 +130,21 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
output.push_str(&scope.to_string());
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
// Add the list of output categories (not generated).
|
||||
output.push_str("pub const CATEGORIES: &[CheckCodePrefix] = &[");
|
||||
output.push('\n');
|
||||
for prefix in prefix_to_codes.keys() {
|
||||
if prefix.chars().all(char::is_alphabetic) {
|
||||
output.push_str(&format!("CheckCodePrefix::{prefix},"));
|
||||
output.push('\n');
|
||||
}
|
||||
}
|
||||
output.push_str("];");
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
// Write the output to `src/checks_gen.rs` (or stdout).
|
||||
if cli.dry_run {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind};
|
||||
use rustpython_ast::{
|
||||
Arguments, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind,
|
||||
};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::SourceCodeLocator;
|
||||
@@ -213,6 +215,27 @@ pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<Vec<&str>> {
|
||||
handler_names
|
||||
}
|
||||
|
||||
/// Return the set of all bound argument names.
|
||||
pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
|
||||
let mut arg_names: FxHashSet<&'a str> = FxHashSet::default();
|
||||
for arg in &arguments.posonlyargs {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
for arg in &arguments.args {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
if let Some(arg) = &arguments.vararg {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
for arg in &arguments.kwonlyargs {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
if let Some(arg) = &arguments.kwarg {
|
||||
arg_names.insert(arg.node.arg.as_str());
|
||||
}
|
||||
arg_names
|
||||
}
|
||||
|
||||
/// Returns `true` if a call is an argumented `super` invocation.
|
||||
pub fn is_super_call_with_arguments(func: &Expr, args: &[Expr]) -> bool {
|
||||
// Check: is this a `super` call?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::{Expr, Keyword};
|
||||
use rustpython_ast::{Expr, Keyword, Stmt};
|
||||
use rustpython_parser::ast::{Located, Location};
|
||||
|
||||
fn id() -> usize {
|
||||
@@ -9,6 +9,12 @@ fn id() -> usize {
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Node<'a> {
|
||||
Stmt(&'a Stmt),
|
||||
Expr(&'a Expr),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Range {
|
||||
pub location: Location,
|
||||
|
||||
297
src/check_ast.rs
297
src/check_ast.rs
@@ -19,8 +19,8 @@ use crate::ast::helpers::{
|
||||
use crate::ast::operations::extract_all_names;
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
Binding, BindingContext, BindingKind, ClassScope, FunctionScope, ImportKind, Range, Scope,
|
||||
ScopeKind,
|
||||
Binding, BindingContext, BindingKind, ClassScope, FunctionScope, ImportKind, Node, Range,
|
||||
Scope, ScopeKind,
|
||||
};
|
||||
use crate::ast::visitor::{walk_excepthandler, walk_withitem, Visitor};
|
||||
use crate::ast::{helpers, operations, visitor};
|
||||
@@ -33,11 +33,13 @@ use crate::python::typing::SubscriptKind;
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::Settings;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::vendored::cformat::{CFormatError, CFormatErrorType};
|
||||
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
||||
use crate::{
|
||||
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
|
||||
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_print,
|
||||
flake8_tidy_imports, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade, rules,
|
||||
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
|
||||
flake8_print, flake8_tidy_imports, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes,
|
||||
pyupgrade, rules,
|
||||
};
|
||||
|
||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
@@ -76,6 +78,7 @@ pub struct Checker<'a> {
|
||||
in_f_string: Option<Range>,
|
||||
in_annotation: bool,
|
||||
in_deferred_string_annotation: bool,
|
||||
in_deferred_annotation: bool,
|
||||
in_literal: bool,
|
||||
in_subscript: bool,
|
||||
in_withitem: bool,
|
||||
@@ -83,6 +86,8 @@ pub struct Checker<'a> {
|
||||
futures_allowed: bool,
|
||||
annotations_future_enabled: bool,
|
||||
except_handlers: Vec<Vec<Vec<&'a str>>>,
|
||||
// Check-specific state.
|
||||
pub(crate) seen_b023: Vec<&'a Expr>,
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -120,6 +125,7 @@ impl<'a> Checker<'a> {
|
||||
in_f_string: None,
|
||||
in_annotation: false,
|
||||
in_deferred_string_annotation: false,
|
||||
in_deferred_annotation: false,
|
||||
in_literal: false,
|
||||
in_subscript: false,
|
||||
in_withitem: false,
|
||||
@@ -127,6 +133,8 @@ impl<'a> Checker<'a> {
|
||||
futures_allowed: true,
|
||||
annotations_future_enabled: false,
|
||||
except_handlers: vec![],
|
||||
// Check-specific state.
|
||||
seen_b023: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +181,15 @@ impl<'a> Checker<'a> {
|
||||
|| (typing::in_extensions(target)
|
||||
&& match_call_path(call_path, "typing_extensions", target, &self.from_imports))
|
||||
}
|
||||
|
||||
/// Return `true` if `member` is bound as a builtin.
|
||||
pub fn is_builtin(&self, member: &str) -> bool {
|
||||
self.current_scopes()
|
||||
.find_map(|scope| scope.values.get(member))
|
||||
.map_or(false, |binding| {
|
||||
matches!(binding.kind, BindingKind::Builtin)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Visitor<'b> for Checker<'a>
|
||||
@@ -622,6 +639,15 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
// flake8-debugger
|
||||
if self.settings.enabled.contains(&CheckCode::T100) {
|
||||
if let Some(check) =
|
||||
flake8_debugger::checks::debugger_import(stmt, None, &alias.node.name)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
for alias in names {
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
@@ -690,12 +716,7 @@ where
|
||||
self.from_imports
|
||||
.entry(module)
|
||||
.or_insert_with(FxHashSet::default)
|
||||
.extend(
|
||||
names
|
||||
.iter()
|
||||
.filter(|alias| alias.node.asname.is_none())
|
||||
.map(|alias| alias.node.name.as_str()),
|
||||
);
|
||||
.extend(names.iter().map(|alias| alias.node.name.as_str()));
|
||||
}
|
||||
for alias in names {
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
@@ -851,6 +872,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// flake8-debugger
|
||||
if self.settings.enabled.contains(&CheckCode::T100) {
|
||||
if let Some(check) = flake8_debugger::checks::debugger_import(
|
||||
stmt,
|
||||
module.as_ref().map(String::as_str),
|
||||
&alias.node.name,
|
||||
) {
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
if self.settings.enabled.contains(&CheckCode::N811) {
|
||||
if let Some(check) =
|
||||
@@ -951,8 +983,16 @@ where
|
||||
flake8_bugbear::plugins::assert_raises_exception(self, stmt, items);
|
||||
}
|
||||
}
|
||||
StmtKind::While { .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::B023) {
|
||||
flake8_bugbear::plugins::function_uses_loop_variable(self, &Node::Stmt(stmt));
|
||||
}
|
||||
}
|
||||
StmtKind::For {
|
||||
target, body, iter, ..
|
||||
}
|
||||
| StmtKind::AsyncFor {
|
||||
target, body, iter, ..
|
||||
} => {
|
||||
if self.settings.enabled.contains(&CheckCode::B007) {
|
||||
flake8_bugbear::plugins::unused_loop_control_variable(self, target, body);
|
||||
@@ -960,6 +1000,9 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::B020) {
|
||||
flake8_bugbear::plugins::loop_variable_overrides_iterator(self, target, iter);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::B023) {
|
||||
flake8_bugbear::plugins::function_uses_loop_variable(self, &Node::Stmt(stmt));
|
||||
}
|
||||
}
|
||||
StmtKind::Try { handlers, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::F707) {
|
||||
@@ -1150,10 +1193,13 @@ where
|
||||
// Pre-visit.
|
||||
match &expr.node {
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
// Ex) typing.List[...]
|
||||
// Ex) Optional[...]
|
||||
if !self.in_deferred_string_annotation
|
||||
&& self.settings.enabled.contains(&CheckCode::U007)
|
||||
&& self.settings.target_version >= PythonVersion::Py310
|
||||
&& (self.settings.target_version >= PythonVersion::Py310
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& self.annotations_future_enabled
|
||||
&& self.in_deferred_annotation))
|
||||
{
|
||||
pyupgrade::plugins::use_pep604_annotation(self, expr, value, slice);
|
||||
}
|
||||
@@ -1192,7 +1238,10 @@ where
|
||||
// Ex) List[...]
|
||||
if !self.in_deferred_string_annotation
|
||||
&& self.settings.enabled.contains(&CheckCode::U006)
|
||||
&& self.settings.target_version >= PythonVersion::Py39
|
||||
&& (self.settings.target_version >= PythonVersion::Py39
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& self.annotations_future_enabled
|
||||
&& self.in_deferred_annotation))
|
||||
&& typing::is_pep585_builtin(
|
||||
expr,
|
||||
&self.from_imports,
|
||||
@@ -1227,8 +1276,12 @@ where
|
||||
}
|
||||
ExprKind::Attribute { attr, .. } => {
|
||||
// Ex) typing.List[...]
|
||||
if self.settings.enabled.contains(&CheckCode::U006)
|
||||
&& self.settings.target_version >= PythonVersion::Py39
|
||||
if !self.in_deferred_string_annotation
|
||||
&& self.settings.enabled.contains(&CheckCode::U006)
|
||||
&& (self.settings.target_version >= PythonVersion::Py39
|
||||
|| (self.settings.target_version >= PythonVersion::Py37
|
||||
&& self.annotations_future_enabled
|
||||
&& self.in_deferred_annotation))
|
||||
&& typing::is_pep585_builtin(expr, &self.from_imports, &self.import_aliases)
|
||||
{
|
||||
pyupgrade::plugins::use_pep585_annotation(self, expr, attr);
|
||||
@@ -1244,20 +1297,73 @@ where
|
||||
keywords,
|
||||
} => {
|
||||
// pyflakes
|
||||
if let ExprKind::Attribute { value, attr, .. } = &func.node {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} = &value.node
|
||||
{
|
||||
if attr == "format" {
|
||||
// "...".format(...) call
|
||||
if self.settings.enabled.contains(&CheckCode::F521) {
|
||||
if self.settings.enabled.contains(&CheckCode::F521)
|
||||
|| self.settings.enabled.contains(&CheckCode::F522)
|
||||
|| self.settings.enabled.contains(&CheckCode::F523)
|
||||
|| self.settings.enabled.contains(&CheckCode::F524)
|
||||
|| self.settings.enabled.contains(&CheckCode::F525)
|
||||
{
|
||||
if let ExprKind::Attribute { value, attr, .. } = &func.node {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} = &value.node
|
||||
{
|
||||
if attr == "format" {
|
||||
// "...".format(...) call
|
||||
let location = Range::from_located(expr);
|
||||
if let Some(check) =
|
||||
pyflakes::checks::string_dot_format_invalid(value, location)
|
||||
{
|
||||
self.add_check(check);
|
||||
match pyflakes::format::FormatSummary::try_from(value.as_ref()) {
|
||||
Err(e) => {
|
||||
if self.settings.enabled.contains(&CheckCode::F521) {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::StringDotFormatInvalidFormat(
|
||||
e.to_string(),
|
||||
),
|
||||
location,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(summary) => {
|
||||
if self.settings.enabled.contains(&CheckCode::F522) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::string_dot_format_extra_named_arguments(
|
||||
&summary, keywords, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F523) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::string_dot_format_extra_positional_arguments(
|
||||
&summary, args, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F524) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::string_dot_format_missing_argument(
|
||||
&summary, args, keywords, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F525) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::string_dot_format_mixing_automatic(
|
||||
&summary, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1544,7 +1650,7 @@ where
|
||||
// flake8-boolean-trap
|
||||
if self.settings.enabled.contains(&CheckCode::FBT003) {
|
||||
flake8_boolean_trap::plugins::check_boolean_positional_value_in_function_call(
|
||||
self, args,
|
||||
self, args, func,
|
||||
);
|
||||
}
|
||||
if let ExprKind::Name { id, ctx } = &func.node {
|
||||
@@ -1557,6 +1663,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// flake8-debugger
|
||||
if self.settings.enabled.contains(&CheckCode::T100) {
|
||||
if let Some(check) = flake8_debugger::checks::debugger_call(
|
||||
expr,
|
||||
func,
|
||||
&self.from_imports,
|
||||
&self.import_aliases,
|
||||
) {
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
// Ruff
|
||||
if self.settings.enabled.contains(&CheckCode::RUF101) {
|
||||
rules::plugins::convert_exit_to_sys_exit(self, func);
|
||||
@@ -1611,6 +1729,116 @@ where
|
||||
pyflakes::plugins::invalid_print_syntax(self, left);
|
||||
}
|
||||
}
|
||||
ExprKind::BinOp {
|
||||
left,
|
||||
op: Operator::Mod,
|
||||
right,
|
||||
} => {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} = &left.node
|
||||
{
|
||||
if self.settings.enabled.contains(&CheckCode::F501)
|
||||
|| self.settings.enabled.contains(&CheckCode::F502)
|
||||
|| self.settings.enabled.contains(&CheckCode::F503)
|
||||
|| self.settings.enabled.contains(&CheckCode::F504)
|
||||
|| self.settings.enabled.contains(&CheckCode::F505)
|
||||
|| self.settings.enabled.contains(&CheckCode::F506)
|
||||
|| self.settings.enabled.contains(&CheckCode::F507)
|
||||
|| self.settings.enabled.contains(&CheckCode::F508)
|
||||
|| self.settings.enabled.contains(&CheckCode::F509)
|
||||
{
|
||||
let location = Range::from_located(expr);
|
||||
match pyflakes::cformat::CFormatSummary::try_from(value.as_ref()) {
|
||||
Err(CFormatError {
|
||||
typ: CFormatErrorType::UnsupportedFormatChar(c),
|
||||
..
|
||||
}) => {
|
||||
if self.settings.enabled.contains(&CheckCode::F509) {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::PercentFormatUnsupportedFormatCharacter(c),
|
||||
location,
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if self.settings.enabled.contains(&CheckCode::F501) {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::PercentFormatInvalidFormat(e.to_string()),
|
||||
location,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(summary) => {
|
||||
if self.settings.enabled.contains(&CheckCode::F502) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::percent_format_expected_mapping(
|
||||
&summary, right, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::F503) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::percent_format_expected_sequence(
|
||||
&summary, right, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::F504) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::percent_format_extra_named_arguments(
|
||||
&summary, right, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::F505) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::percent_format_missing_arguments(
|
||||
&summary, right, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::F506) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::percent_format_mixed_positional_and_named(
|
||||
&summary, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::F507) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::percent_format_positional_count_mismatch(
|
||||
&summary, right, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::F508) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::percent_format_star_requires_sequence(
|
||||
&summary, right, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::UnaryOp { op, operand } => {
|
||||
let check_not_in = self.settings.enabled.contains(&CheckCode::E713);
|
||||
let check_not_is = self.settings.enabled.contains(&CheckCode::E714);
|
||||
@@ -1757,9 +1985,15 @@ where
|
||||
self.add_check(check);
|
||||
};
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::B023) {
|
||||
flake8_bugbear::plugins::function_uses_loop_variable(self, &Node::Expr(expr));
|
||||
}
|
||||
self.push_scope(Scope::new(ScopeKind::Generator));
|
||||
}
|
||||
ExprKind::GeneratorExp { .. } | ExprKind::DictComp { .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::B023) {
|
||||
flake8_bugbear::plugins::function_uses_loop_variable(self, &Node::Expr(expr));
|
||||
}
|
||||
self.push_scope(Scope::new(ScopeKind::Generator));
|
||||
}
|
||||
_ => {}
|
||||
@@ -1890,6 +2124,7 @@ where
|
||||
value,
|
||||
&self.from_imports,
|
||||
&self.import_aliases,
|
||||
|member| self.is_builtin(member),
|
||||
) {
|
||||
Some(subscript) => {
|
||||
match subscript {
|
||||
@@ -2520,7 +2755,9 @@ impl<'a> Checker<'a> {
|
||||
while let Some((expr, scopes, parents)) = self.deferred_annotations.pop() {
|
||||
self.scope_stack = scopes;
|
||||
self.parent_stack = parents;
|
||||
self.in_deferred_annotation = true;
|
||||
self.visit_expr(expr);
|
||||
self.in_deferred_annotation = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2546,9 +2783,9 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
for (expr, (scopes, parents)) in allocator.iter().zip(stacks) {
|
||||
self.in_deferred_string_annotation = true;
|
||||
self.scope_stack = scopes;
|
||||
self.parent_stack = parents;
|
||||
self.in_deferred_string_annotation = true;
|
||||
self.visit_expr(expr);
|
||||
self.in_deferred_string_annotation = false;
|
||||
}
|
||||
|
||||
@@ -14,15 +14,18 @@ use crate::settings::Settings;
|
||||
|
||||
// Regex from PEP263
|
||||
static CODING_COMMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").expect("Invalid regex"));
|
||||
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").unwrap());
|
||||
|
||||
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap());
|
||||
|
||||
/// Whether the given line is too long and should be reported.
|
||||
fn should_enforce_line_length(line: &str, length: usize, limit: usize) -> bool {
|
||||
if length > limit {
|
||||
let mut chunks = line.split_whitespace();
|
||||
if let (Some(first), Some(_)) = (chunks.next(), chunks.next()) {
|
||||
// Do not enforce the line length for commented lines with a single word
|
||||
!(first == "#" && chunks.next().is_none())
|
||||
// Do not enforce the line length for commented lines that end with a URL
|
||||
// or contain only a single word.
|
||||
!(first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)))
|
||||
} else {
|
||||
// Single word / no printable chars - no way to make the line shorter
|
||||
false
|
||||
@@ -38,6 +41,7 @@ pub fn check_lines(
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
settings: &Settings,
|
||||
autofix: bool,
|
||||
ignore_noqa: bool,
|
||||
) {
|
||||
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::U009);
|
||||
let enforce_line_too_long = settings.enabled.contains(&CheckCode::E501);
|
||||
@@ -53,6 +57,30 @@ pub fn check_lines(
|
||||
assert!(check.location.row() >= 1);
|
||||
}
|
||||
|
||||
macro_rules! add_if {
|
||||
($check:expr, $noqa:expr) => {{
|
||||
match $noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push($check.kind.code().as_ref());
|
||||
if ignore_noqa {
|
||||
line_checks.push($check);
|
||||
}
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&$check.kind.code().as_ref()) {
|
||||
matches.push($check.kind.code().as_ref());
|
||||
if ignore_noqa {
|
||||
line_checks.push($check);
|
||||
}
|
||||
} else {
|
||||
line_checks.push($check);
|
||||
}
|
||||
}
|
||||
(Directive::None, ..) => line_checks.push($check),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for (lineno, line) in lines.iter().enumerate() {
|
||||
// Grab the noqa (logical) line number for the current (physical) line.
|
||||
@@ -65,21 +93,24 @@ pub fn check_lines(
|
||||
if lineno < 2 {
|
||||
// PEP3120 makes utf-8 the default encoding.
|
||||
if CODING_COMMENT_REGEX.is_match(line) {
|
||||
let line_length = line.len();
|
||||
let mut check = Check::new(
|
||||
CheckKind::PEP3120UnnecessaryCodingComment,
|
||||
Range {
|
||||
location: Location::new(lineno + 1, 0),
|
||||
end_location: Location::new(lineno + 1, line_length + 1),
|
||||
end_location: Location::new(lineno + 2, 0),
|
||||
},
|
||||
);
|
||||
if autofix && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::deletion(
|
||||
Location::new(lineno + 1, 0),
|
||||
Location::new(lineno + 1, line_length + 1),
|
||||
Location::new(lineno + 2, 0),
|
||||
));
|
||||
}
|
||||
line_checks.push(check);
|
||||
|
||||
let noqa = noqa_directives.entry(noqa_lineno).or_insert_with(|| {
|
||||
(noqa::extract_noqa_directive(lines[noqa_lineno]), vec![])
|
||||
});
|
||||
add_if!(check, noqa);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +140,7 @@ pub fn check_lines(
|
||||
ignored.push(index);
|
||||
}
|
||||
}
|
||||
(Directive::None, _) => {}
|
||||
(Directive::None, ..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +148,6 @@ pub fn check_lines(
|
||||
if enforce_line_too_long {
|
||||
let line_length = line.chars().count();
|
||||
if should_enforce_line_length(line, line_length, settings.line_length) {
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
|
||||
let check = Check::new(
|
||||
CheckKind::LineTooLong(line_length, settings.line_length),
|
||||
Range {
|
||||
@@ -129,35 +156,19 @@ pub fn check_lines(
|
||||
},
|
||||
);
|
||||
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
} else {
|
||||
line_checks.push(check);
|
||||
}
|
||||
}
|
||||
(Directive::None, _) => line_checks.push(check),
|
||||
}
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
add_if!(check, noqa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce newlines at end of files.
|
||||
// Enforce newlines at end of files (W292).
|
||||
if settings.enabled.contains(&CheckCode::W292) && !contents.ends_with('\n') {
|
||||
// Note: if `lines.last()` is `None`, then `contents` is empty (and so we don't
|
||||
// want to raise W292 anyway).
|
||||
if let Some(line) = lines.last() {
|
||||
let lineno = lines.len() - 1;
|
||||
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
|
||||
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
|
||||
let check = Check::new(
|
||||
CheckKind::NoNewLineAtEndOfFile,
|
||||
Range {
|
||||
@@ -166,23 +177,16 @@ pub fn check_lines(
|
||||
},
|
||||
);
|
||||
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if codes.contains(&check.kind.code().as_ref()) {
|
||||
matches.push(check.kind.code().as_ref());
|
||||
} else {
|
||||
line_checks.push(check);
|
||||
}
|
||||
}
|
||||
(Directive::None, _) => line_checks.push(check),
|
||||
}
|
||||
let lineno = lines.len() - 1;
|
||||
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
|
||||
let noqa = noqa_directives
|
||||
.entry(noqa_lineno)
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
|
||||
add_if!(check, noqa);
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce that the noqa directive was actually used.
|
||||
// Enforce that the noqa directive was actually used (M001).
|
||||
if enforce_noqa {
|
||||
for (row, (directive, matches)) in noqa_directives {
|
||||
match directive {
|
||||
@@ -245,9 +249,11 @@ pub fn check_lines(
|
||||
}
|
||||
}
|
||||
|
||||
ignored.sort_unstable();
|
||||
for index in ignored.iter().rev() {
|
||||
checks.swap_remove(*index);
|
||||
if !ignore_noqa {
|
||||
ignored.sort_unstable();
|
||||
for index in ignored.iter().rev() {
|
||||
checks.swap_remove(*index);
|
||||
}
|
||||
}
|
||||
checks.extend(line_checks);
|
||||
}
|
||||
@@ -275,6 +281,7 @@ mod tests {
|
||||
..Settings::for_rule(CheckCode::E501)
|
||||
},
|
||||
true,
|
||||
false,
|
||||
);
|
||||
checks
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::checks::{Check, CheckCode};
|
||||
use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::rules::checks::Context;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::{flake8_quotes, pycodestyle, rules, Settings};
|
||||
use crate::{eradicate, flake8_quotes, pycodestyle, rules, Settings};
|
||||
|
||||
pub fn check_tokens(
|
||||
locator: &SourceCodeLocator,
|
||||
@@ -23,6 +23,7 @@ pub fn check_tokens(
|
||||
|| settings.enabled.contains(&CheckCode::Q001)
|
||||
|| settings.enabled.contains(&CheckCode::Q002)
|
||||
|| settings.enabled.contains(&CheckCode::Q003);
|
||||
let enforce_commented_out_code = settings.enabled.contains(&CheckCode::ERA001);
|
||||
let enforce_invalid_escape_sequence = settings.enabled.contains(&CheckCode::W605);
|
||||
|
||||
let mut state_machine = StateMachine::default();
|
||||
@@ -72,6 +73,17 @@ pub fn check_tokens(
|
||||
}
|
||||
}
|
||||
|
||||
// eradicate
|
||||
if enforce_commented_out_code {
|
||||
if matches!(tok, Tok::Comment) {
|
||||
if let Some(check) =
|
||||
eradicate::checks::commented_out_code(locator, start, end, settings, autofix)
|
||||
{
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W605
|
||||
if enforce_invalid_escape_sequence {
|
||||
if matches!(tok, Tok::String { .. }) {
|
||||
|
||||
511
src/checks.rs
511
src/checks.rs
@@ -7,6 +7,7 @@ use strum_macros::{AsRefStr, EnumIter, EnumString};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::flake8_debugger::types::DebuggerUsingType;
|
||||
use crate::flake8_quotes::settings::Quote;
|
||||
use crate::flake8_tidy_imports::settings::Strictness;
|
||||
use crate::pyupgrade::types::Primitive;
|
||||
@@ -52,7 +53,20 @@ pub enum CheckCode {
|
||||
F405,
|
||||
F406,
|
||||
F407,
|
||||
F501,
|
||||
F502,
|
||||
F503,
|
||||
F504,
|
||||
F505,
|
||||
F506,
|
||||
F507,
|
||||
F508,
|
||||
F509,
|
||||
F521,
|
||||
F522,
|
||||
F523,
|
||||
F524,
|
||||
F525,
|
||||
F541,
|
||||
F601,
|
||||
F602,
|
||||
@@ -100,6 +114,7 @@ pub enum CheckCode {
|
||||
B020,
|
||||
B021,
|
||||
B022,
|
||||
B023,
|
||||
B024,
|
||||
B025,
|
||||
B026,
|
||||
@@ -124,6 +139,8 @@ pub enum CheckCode {
|
||||
C415,
|
||||
C416,
|
||||
C417,
|
||||
// flake8-debugger
|
||||
T100,
|
||||
// mccabe
|
||||
C901,
|
||||
// flake8-tidy-imports
|
||||
@@ -237,6 +254,8 @@ pub enum CheckCode {
|
||||
N818,
|
||||
// isort
|
||||
I001,
|
||||
// eradicate
|
||||
ERA001,
|
||||
// flake8-bandit
|
||||
S101,
|
||||
S102,
|
||||
@@ -265,8 +284,10 @@ pub enum CheckCategory {
|
||||
Pydocstyle,
|
||||
Pyupgrade,
|
||||
PEP8Naming,
|
||||
Eradicate,
|
||||
Flake8Bandit,
|
||||
Flake8Comprehensions,
|
||||
Flake8Debugger,
|
||||
Flake8BooleanTrap,
|
||||
Flake8Bugbear,
|
||||
Flake8Builtins,
|
||||
@@ -284,65 +305,71 @@ pub enum CheckCategory {
|
||||
impl CheckCategory {
|
||||
pub fn title(&self) -> &'static str {
|
||||
match self {
|
||||
CheckCategory::Pycodestyle => "pycodestyle",
|
||||
CheckCategory::Pyflakes => "Pyflakes",
|
||||
CheckCategory::Isort => "isort",
|
||||
CheckCategory::Eradicate => "eradicate",
|
||||
CheckCategory::Flake82020 => "flake8-2020",
|
||||
CheckCategory::Flake8Annotations => "flake8-annotations",
|
||||
CheckCategory::Flake8Bandit => "flake8-bandit",
|
||||
CheckCategory::Flake8BlindExcept => "flake8-blind-except",
|
||||
CheckCategory::Flake8BooleanTrap => "flake8-boolean-trap",
|
||||
CheckCategory::Flake8Builtins => "flake8-builtins",
|
||||
CheckCategory::Flake8Bugbear => "flake8-bugbear",
|
||||
CheckCategory::Flake8Builtins => "flake8-builtins",
|
||||
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
||||
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
|
||||
CheckCategory::Flake8Debugger => "flake8-debugger",
|
||||
CheckCategory::Flake8Print => "flake8-print",
|
||||
CheckCategory::Flake8Quotes => "flake8-quotes",
|
||||
CheckCategory::Flake8Annotations => "flake8-annotations",
|
||||
CheckCategory::Flake82020 => "flake8-2020",
|
||||
CheckCategory::Flake8BlindExcept => "flake8-blind-except",
|
||||
CheckCategory::Pyupgrade => "pyupgrade",
|
||||
CheckCategory::Pydocstyle => "pydocstyle",
|
||||
CheckCategory::PEP8Naming => "pep8-naming",
|
||||
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
|
||||
CheckCategory::Isort => "isort",
|
||||
CheckCategory::McCabe => "mccabe",
|
||||
CheckCategory::Ruff => "Ruff-specific rules",
|
||||
CheckCategory::Meta => "Meta rules",
|
||||
CheckCategory::PEP8Naming => "pep8-naming",
|
||||
CheckCategory::Pycodestyle => "pycodestyle",
|
||||
CheckCategory::Pydocstyle => "pydocstyle",
|
||||
CheckCategory::Pyflakes => "Pyflakes",
|
||||
CheckCategory::Pyupgrade => "pyupgrade",
|
||||
CheckCategory::Ruff => "Ruff-specific rules",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn url(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
CheckCategory::Pycodestyle => Some("https://pypi.org/project/pycodestyle/2.9.1/"),
|
||||
CheckCategory::Pyflakes => Some("https://pypi.org/project/pyflakes/2.5.0/"),
|
||||
CheckCategory::Isort => Some("https://pypi.org/project/isort/5.10.1/"),
|
||||
CheckCategory::Flake8Builtins => {
|
||||
Some("https://pypi.org/project/flake8-builtins/2.0.1/")
|
||||
}
|
||||
CheckCategory::Flake8Bugbear => {
|
||||
Some("https://pypi.org/project/flake8-bugbear/22.10.27/")
|
||||
}
|
||||
CheckCategory::Flake8Comprehensions => {
|
||||
Some("https://pypi.org/project/flake8-comprehensions/3.10.1/")
|
||||
}
|
||||
CheckCategory::Flake8TidyImports => {
|
||||
Some("https://pypi.org/project/flake8-tidy-imports/4.8.0/")
|
||||
}
|
||||
CheckCategory::Flake8Print => Some("https://pypi.org/project/flake8-print/5.0.0/"),
|
||||
CheckCategory::Flake8Quotes => Some("https://pypi.org/project/flake8-quotes/3.3.1/"),
|
||||
CheckCategory::Eradicate => Some("https://pypi.org/project/eradicate/2.1.0/"),
|
||||
CheckCategory::Flake82020 => Some("https://pypi.org/project/flake8-2020/1.7.0/"),
|
||||
CheckCategory::Flake8Annotations => {
|
||||
Some("https://pypi.org/project/flake8-annotations/2.9.1/")
|
||||
}
|
||||
CheckCategory::Flake82020 => Some("https://pypi.org/project/flake8-2020/1.7.0/"),
|
||||
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
|
||||
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
|
||||
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
|
||||
CheckCategory::Flake8Bandit => Some("https://pypi.org/project/flake8-bandit/4.1.1/"),
|
||||
CheckCategory::Flake8BlindExcept => {
|
||||
Some("https://pypi.org/project/flake8-blind-except/0.2.1/")
|
||||
}
|
||||
CheckCategory::McCabe => Some("https://pypi.org/project/mccabe/0.7.0/"),
|
||||
CheckCategory::Flake8BooleanTrap => {
|
||||
Some("https://pypi.org/project/flake8-boolean-trap/0.1.0/")
|
||||
}
|
||||
CheckCategory::Ruff => None,
|
||||
CheckCategory::Flake8Bugbear => {
|
||||
Some("https://pypi.org/project/flake8-bugbear/22.10.27/")
|
||||
}
|
||||
CheckCategory::Flake8Builtins => {
|
||||
Some("https://pypi.org/project/flake8-builtins/2.0.1/")
|
||||
}
|
||||
CheckCategory::Flake8Comprehensions => {
|
||||
Some("https://pypi.org/project/flake8-comprehensions/3.10.1/")
|
||||
}
|
||||
CheckCategory::Flake8Debugger => {
|
||||
Some("https://pypi.org/project/flake8-debugger/4.1.2/")
|
||||
}
|
||||
CheckCategory::Flake8Print => Some("https://pypi.org/project/flake8-print/5.0.0/"),
|
||||
CheckCategory::Flake8Quotes => Some("https://pypi.org/project/flake8-quotes/3.3.1/"),
|
||||
CheckCategory::Flake8TidyImports => {
|
||||
Some("https://pypi.org/project/flake8-tidy-imports/4.8.0/")
|
||||
}
|
||||
CheckCategory::Isort => Some("https://pypi.org/project/isort/5.10.1/"),
|
||||
CheckCategory::McCabe => Some("https://pypi.org/project/mccabe/0.7.0/"),
|
||||
CheckCategory::Meta => None,
|
||||
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
|
||||
CheckCategory::Pycodestyle => Some("https://pypi.org/project/pycodestyle/2.9.1/"),
|
||||
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
|
||||
CheckCategory::Pyflakes => Some("https://pypi.org/project/pyflakes/2.5.0/"),
|
||||
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
|
||||
CheckCategory::Ruff => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,9 +429,22 @@ pub enum CheckKind {
|
||||
LateFutureImport,
|
||||
MultiValueRepeatedKeyLiteral,
|
||||
MultiValueRepeatedKeyVariable(String),
|
||||
PercentFormatExpectedMapping,
|
||||
PercentFormatExpectedSequence,
|
||||
PercentFormatExtraNamedArguments(Vec<String>),
|
||||
PercentFormatInvalidFormat(String),
|
||||
PercentFormatMissingArgument(Vec<String>),
|
||||
PercentFormatMixedPositionalAndNamed,
|
||||
PercentFormatPositionalCountMismatch(usize, usize),
|
||||
PercentFormatStarRequiresSequence,
|
||||
PercentFormatUnsupportedFormatCharacter(char),
|
||||
RaiseNotImplemented,
|
||||
ReturnOutsideFunction,
|
||||
StringDotFormatExtraNamedArguments(Vec<String>),
|
||||
StringDotFormatExtraPositionalArguments(Vec<String>),
|
||||
StringDotFormatInvalidFormat(String),
|
||||
StringDotFormatMissingArguments(Vec<String>),
|
||||
StringDotFormatMixingAutomatic,
|
||||
TwoStarredExpressions,
|
||||
UndefinedExport(String),
|
||||
UndefinedLocal(String),
|
||||
@@ -419,32 +459,33 @@ pub enum CheckKind {
|
||||
// flake8-blind-except
|
||||
BlindExcept,
|
||||
// flake8-bugbear
|
||||
UnaryPrefixIncrement,
|
||||
AssignmentToOsEnviron,
|
||||
UnreliableCallableCheck,
|
||||
StripWithMultiCharacters,
|
||||
MutableArgumentDefault,
|
||||
UnusedLoopControlVariable(String),
|
||||
FunctionCallArgumentDefault(Option<String>),
|
||||
GetAttrWithConstant,
|
||||
SetAttrWithConstant,
|
||||
DoNotAssertFalse,
|
||||
JumpStatementInFinally(String),
|
||||
RedundantTupleInExceptionHandler(String),
|
||||
DuplicateHandlerException(Vec<String>),
|
||||
UselessComparison,
|
||||
CannotRaiseLiteral,
|
||||
NoAssertRaisesException,
|
||||
UselessExpression,
|
||||
CachedInstanceMethod,
|
||||
LoopVariableOverridesIterator(String),
|
||||
FStringDocstring,
|
||||
UselessContextlibSuppress,
|
||||
AbstractBaseClassWithoutAbstractMethod(String),
|
||||
AssignmentToOsEnviron,
|
||||
CachedInstanceMethod,
|
||||
CannotRaiseLiteral,
|
||||
DoNotAssertFalse,
|
||||
DuplicateHandlerException(Vec<String>),
|
||||
DuplicateTryBlockException(String),
|
||||
StarArgUnpackingAfterKeywordArg,
|
||||
EmptyMethodWithoutAbstractDecorator(String),
|
||||
FStringDocstring,
|
||||
FunctionCallArgumentDefault(Option<String>),
|
||||
FunctionUsesLoopVariable(String),
|
||||
GetAttrWithConstant,
|
||||
JumpStatementInFinally(String),
|
||||
LoopVariableOverridesIterator(String),
|
||||
MutableArgumentDefault,
|
||||
NoAssertRaisesException,
|
||||
RaiseWithoutFromInsideExcept,
|
||||
RedundantTupleInExceptionHandler(String),
|
||||
SetAttrWithConstant,
|
||||
StarArgUnpackingAfterKeywordArg,
|
||||
StripWithMultiCharacters,
|
||||
UnaryPrefixIncrement,
|
||||
UnreliableCallableCheck,
|
||||
UnusedLoopControlVariable(String),
|
||||
UselessComparison,
|
||||
UselessContextlibSuppress,
|
||||
UselessExpression,
|
||||
// flake8-comprehensions
|
||||
UnnecessaryGeneratorList,
|
||||
UnnecessaryGeneratorSet,
|
||||
@@ -462,6 +503,8 @@ pub enum CheckKind {
|
||||
UnnecessarySubscriptReversal(String),
|
||||
UnnecessaryComprehension(String),
|
||||
UnnecessaryMap(String),
|
||||
// flake8-debugger
|
||||
Debugger(DebuggerUsingType),
|
||||
// flake8-tidy-imports
|
||||
BannedRelativeImport(Strictness),
|
||||
// flake8-print
|
||||
@@ -573,6 +616,8 @@ pub enum CheckKind {
|
||||
ErrorSuffixOnExceptionName(String),
|
||||
// isort
|
||||
UnsortedImports,
|
||||
// eradicate
|
||||
CommentedOutCode,
|
||||
// flake8-bandit
|
||||
AssertUsed,
|
||||
ExecUsed,
|
||||
@@ -603,7 +648,8 @@ impl CheckCode {
|
||||
CheckCode::E501 | CheckCode::W292 | CheckCode::M001 | CheckCode::U009 => {
|
||||
&LintSource::Lines
|
||||
}
|
||||
CheckCode::Q000
|
||||
CheckCode::ERA001
|
||||
| CheckCode::Q000
|
||||
| CheckCode::Q001
|
||||
| CheckCode::Q002
|
||||
| CheckCode::Q003
|
||||
@@ -648,7 +694,24 @@ impl CheckCode {
|
||||
}
|
||||
CheckCode::F406 => CheckKind::ImportStarNotPermitted("...".to_string()),
|
||||
CheckCode::F407 => CheckKind::FutureFeatureNotDefined("...".to_string()),
|
||||
CheckCode::F501 => CheckKind::PercentFormatInvalidFormat("...".to_string()),
|
||||
CheckCode::F502 => CheckKind::PercentFormatExpectedMapping,
|
||||
CheckCode::F503 => CheckKind::PercentFormatExpectedSequence,
|
||||
CheckCode::F504 => CheckKind::PercentFormatExtraNamedArguments(vec!["...".to_string()]),
|
||||
CheckCode::F505 => CheckKind::PercentFormatMissingArgument(vec!["...".to_string()]),
|
||||
CheckCode::F506 => CheckKind::PercentFormatMixedPositionalAndNamed,
|
||||
CheckCode::F507 => CheckKind::PercentFormatPositionalCountMismatch(4, 2),
|
||||
CheckCode::F508 => CheckKind::PercentFormatStarRequiresSequence,
|
||||
CheckCode::F509 => CheckKind::PercentFormatUnsupportedFormatCharacter('c'),
|
||||
CheckCode::F521 => CheckKind::StringDotFormatInvalidFormat("...".to_string()),
|
||||
CheckCode::F522 => {
|
||||
CheckKind::StringDotFormatExtraNamedArguments(vec!["...".to_string()])
|
||||
}
|
||||
CheckCode::F523 => {
|
||||
CheckKind::StringDotFormatExtraPositionalArguments(vec!["...".to_string()])
|
||||
}
|
||||
CheckCode::F524 => CheckKind::StringDotFormatMissingArguments(vec!["...".to_string()]),
|
||||
CheckCode::F525 => CheckKind::StringDotFormatMixingAutomatic,
|
||||
CheckCode::F541 => CheckKind::FStringMissingPlaceholders,
|
||||
CheckCode::F601 => CheckKind::MultiValueRepeatedKeyLiteral,
|
||||
CheckCode::F602 => CheckKind::MultiValueRepeatedKeyVariable("...".to_string()),
|
||||
@@ -700,6 +763,7 @@ impl CheckCode {
|
||||
CheckCode::B020 => CheckKind::LoopVariableOverridesIterator("...".to_string()),
|
||||
CheckCode::B021 => CheckKind::FStringDocstring,
|
||||
CheckCode::B022 => CheckKind::UselessContextlibSuppress,
|
||||
CheckCode::B023 => CheckKind::FunctionUsesLoopVariable("...".to_string()),
|
||||
CheckCode::B024 => CheckKind::AbstractBaseClassWithoutAbstractMethod("...".to_string()),
|
||||
CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
|
||||
CheckCode::B026 => CheckKind::StarArgUnpackingAfterKeywordArg,
|
||||
@@ -735,6 +799,8 @@ impl CheckCode {
|
||||
}
|
||||
CheckCode::C416 => CheckKind::UnnecessaryComprehension("(list|set)".to_string()),
|
||||
CheckCode::C417 => CheckKind::UnnecessaryMap("(list|set|dict)".to_string()),
|
||||
// flake8-debugger
|
||||
CheckCode::T100 => CheckKind::Debugger(DebuggerUsingType::Import("...".to_string())),
|
||||
// flake8-tidy-imports
|
||||
CheckCode::I252 => CheckKind::BannedRelativeImport(Strictness::All),
|
||||
// flake8-print
|
||||
@@ -867,6 +933,8 @@ impl CheckCode {
|
||||
CheckCode::N818 => CheckKind::ErrorSuffixOnExceptionName("...".to_string()),
|
||||
// isort
|
||||
CheckCode::I001 => CheckKind::UnsortedImports,
|
||||
// eradicate
|
||||
CheckCode::ERA001 => CheckKind::CommentedOutCode,
|
||||
// flake8-bandit
|
||||
CheckCode::S101 => CheckKind::AssertUsed,
|
||||
CheckCode::S102 => CheckKind::ExecUsed,
|
||||
@@ -892,54 +960,20 @@ impl CheckCode {
|
||||
pub fn category(&self) -> CheckCategory {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self {
|
||||
CheckCode::E402 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E501 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E711 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E712 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E713 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E714 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E721 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E722 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E731 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E741 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E742 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E743 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E902 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E999 => CheckCategory::Pycodestyle,
|
||||
CheckCode::W292 => CheckCategory::Pycodestyle,
|
||||
CheckCode::W605 => CheckCategory::Pycodestyle,
|
||||
CheckCode::F401 => CheckCategory::Pyflakes,
|
||||
CheckCode::F402 => CheckCategory::Pyflakes,
|
||||
CheckCode::F403 => CheckCategory::Pyflakes,
|
||||
CheckCode::F404 => CheckCategory::Pyflakes,
|
||||
CheckCode::F405 => CheckCategory::Pyflakes,
|
||||
CheckCode::F406 => CheckCategory::Pyflakes,
|
||||
CheckCode::F407 => CheckCategory::Pyflakes,
|
||||
CheckCode::F521 => CheckCategory::Pyflakes,
|
||||
CheckCode::F541 => CheckCategory::Pyflakes,
|
||||
CheckCode::F601 => CheckCategory::Pyflakes,
|
||||
CheckCode::F602 => CheckCategory::Pyflakes,
|
||||
CheckCode::F621 => CheckCategory::Pyflakes,
|
||||
CheckCode::F622 => CheckCategory::Pyflakes,
|
||||
CheckCode::F631 => CheckCategory::Pyflakes,
|
||||
CheckCode::F632 => CheckCategory::Pyflakes,
|
||||
CheckCode::F633 => CheckCategory::Pyflakes,
|
||||
CheckCode::F634 => CheckCategory::Pyflakes,
|
||||
CheckCode::F701 => CheckCategory::Pyflakes,
|
||||
CheckCode::F702 => CheckCategory::Pyflakes,
|
||||
CheckCode::F704 => CheckCategory::Pyflakes,
|
||||
CheckCode::F706 => CheckCategory::Pyflakes,
|
||||
CheckCode::F707 => CheckCategory::Pyflakes,
|
||||
CheckCode::F722 => CheckCategory::Pyflakes,
|
||||
CheckCode::F821 => CheckCategory::Pyflakes,
|
||||
CheckCode::F822 => CheckCategory::Pyflakes,
|
||||
CheckCode::F823 => CheckCategory::Pyflakes,
|
||||
CheckCode::F831 => CheckCategory::Pyflakes,
|
||||
CheckCode::F841 => CheckCategory::Pyflakes,
|
||||
CheckCode::F901 => CheckCategory::Pyflakes,
|
||||
CheckCode::A001 => CheckCategory::Flake8Builtins,
|
||||
CheckCode::A002 => CheckCategory::Flake8Builtins,
|
||||
CheckCode::A003 => CheckCategory::Flake8Builtins,
|
||||
CheckCode::ANN001 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN002 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN003 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN101 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN102 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN201 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN202 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN204 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN401 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::B002 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B003 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B004 => CheckCategory::Flake8Bugbear,
|
||||
@@ -961,6 +995,7 @@ impl CheckCode {
|
||||
CheckCode::B020 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B021 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B022 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B023 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B024 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B025 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B026 => CheckCategory::Flake8Bugbear,
|
||||
@@ -983,48 +1018,7 @@ impl CheckCode {
|
||||
CheckCode::C415 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C416 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C417 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::I252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::T201 => CheckCategory::Flake8Print,
|
||||
CheckCode::T203 => CheckCategory::Flake8Print,
|
||||
CheckCode::Q000 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q001 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q002 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q003 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::ANN001 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN002 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN003 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN101 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN102 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN201 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN202 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN204 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN401 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::YTT101 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT102 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT103 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT201 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT202 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT203 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT204 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT301 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT302 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT303 => CheckCategory::Flake82020,
|
||||
CheckCode::U001 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U003 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U004 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U005 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U006 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U007 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U008 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U009 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U010 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U011 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U012 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U013 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U014 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U015 => CheckCategory::Pyupgrade,
|
||||
CheckCode::C901 => CheckCategory::McCabe,
|
||||
CheckCode::D100 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D101 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D102 => CheckCategory::Pydocstyle,
|
||||
@@ -1069,6 +1063,69 @@ impl CheckCode {
|
||||
CheckCode::D417 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D418 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D419 => CheckCategory::Pydocstyle,
|
||||
CheckCode::E402 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E501 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E711 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E712 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E713 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E714 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E721 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E722 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E731 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E741 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E742 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E743 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E902 => CheckCategory::Pycodestyle,
|
||||
CheckCode::E999 => CheckCategory::Pycodestyle,
|
||||
CheckCode::ERA001 => CheckCategory::Eradicate,
|
||||
CheckCode::F401 => CheckCategory::Pyflakes,
|
||||
CheckCode::F402 => CheckCategory::Pyflakes,
|
||||
CheckCode::F403 => CheckCategory::Pyflakes,
|
||||
CheckCode::F404 => CheckCategory::Pyflakes,
|
||||
CheckCode::F405 => CheckCategory::Pyflakes,
|
||||
CheckCode::F406 => CheckCategory::Pyflakes,
|
||||
CheckCode::F407 => CheckCategory::Pyflakes,
|
||||
CheckCode::F501 => CheckCategory::Pyflakes,
|
||||
CheckCode::F502 => CheckCategory::Pyflakes,
|
||||
CheckCode::F503 => CheckCategory::Pyflakes,
|
||||
CheckCode::F504 => CheckCategory::Pyflakes,
|
||||
CheckCode::F505 => CheckCategory::Pyflakes,
|
||||
CheckCode::F506 => CheckCategory::Pyflakes,
|
||||
CheckCode::F507 => CheckCategory::Pyflakes,
|
||||
CheckCode::F508 => CheckCategory::Pyflakes,
|
||||
CheckCode::F509 => CheckCategory::Pyflakes,
|
||||
CheckCode::F521 => CheckCategory::Pyflakes,
|
||||
CheckCode::F522 => CheckCategory::Pyflakes,
|
||||
CheckCode::F523 => CheckCategory::Pyflakes,
|
||||
CheckCode::F524 => CheckCategory::Pyflakes,
|
||||
CheckCode::F525 => CheckCategory::Pyflakes,
|
||||
CheckCode::F541 => CheckCategory::Pyflakes,
|
||||
CheckCode::F601 => CheckCategory::Pyflakes,
|
||||
CheckCode::F602 => CheckCategory::Pyflakes,
|
||||
CheckCode::F621 => CheckCategory::Pyflakes,
|
||||
CheckCode::F622 => CheckCategory::Pyflakes,
|
||||
CheckCode::F631 => CheckCategory::Pyflakes,
|
||||
CheckCode::F632 => CheckCategory::Pyflakes,
|
||||
CheckCode::F633 => CheckCategory::Pyflakes,
|
||||
CheckCode::F634 => CheckCategory::Pyflakes,
|
||||
CheckCode::F701 => CheckCategory::Pyflakes,
|
||||
CheckCode::F702 => CheckCategory::Pyflakes,
|
||||
CheckCode::F704 => CheckCategory::Pyflakes,
|
||||
CheckCode::F706 => CheckCategory::Pyflakes,
|
||||
CheckCode::F707 => CheckCategory::Pyflakes,
|
||||
CheckCode::F722 => CheckCategory::Pyflakes,
|
||||
CheckCode::F821 => CheckCategory::Pyflakes,
|
||||
CheckCode::F822 => CheckCategory::Pyflakes,
|
||||
CheckCode::F823 => CheckCategory::Pyflakes,
|
||||
CheckCode::F831 => CheckCategory::Pyflakes,
|
||||
CheckCode::F841 => CheckCategory::Pyflakes,
|
||||
CheckCode::F901 => CheckCategory::Pyflakes,
|
||||
CheckCode::FBT001 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::I001 => CheckCategory::Isort,
|
||||
CheckCode::I252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::M001 => CheckCategory::Meta,
|
||||
CheckCode::N801 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N802 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N803 => CheckCategory::PEP8Naming,
|
||||
@@ -1084,22 +1141,49 @@ impl CheckCode {
|
||||
CheckCode::N816 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N817 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N818 => CheckCategory::PEP8Naming,
|
||||
CheckCode::I001 => CheckCategory::Isort,
|
||||
CheckCode::Q000 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q001 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q002 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q003 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::RUF001 => CheckCategory::Ruff,
|
||||
CheckCode::RUF002 => CheckCategory::Ruff,
|
||||
CheckCode::RUF003 => CheckCategory::Ruff,
|
||||
CheckCode::RUF101 => CheckCategory::Ruff,
|
||||
CheckCode::S101 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S102 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S104 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S105 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S106 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S107 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::C901 => CheckCategory::McCabe,
|
||||
CheckCode::FBT001 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::RUF001 => CheckCategory::Ruff,
|
||||
CheckCode::RUF002 => CheckCategory::Ruff,
|
||||
CheckCode::RUF003 => CheckCategory::Ruff,
|
||||
CheckCode::RUF101 => CheckCategory::Ruff,
|
||||
CheckCode::M001 => CheckCategory::Meta,
|
||||
CheckCode::T100 => CheckCategory::Flake8Debugger,
|
||||
CheckCode::T201 => CheckCategory::Flake8Print,
|
||||
CheckCode::T203 => CheckCategory::Flake8Print,
|
||||
CheckCode::U001 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U003 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U004 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U005 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U006 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U007 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U008 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U009 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U010 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U011 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U012 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U013 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U014 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U015 => CheckCategory::Pyupgrade,
|
||||
CheckCode::W292 => CheckCategory::Pycodestyle,
|
||||
CheckCode::W605 => CheckCategory::Pycodestyle,
|
||||
CheckCode::YTT101 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT102 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT103 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT201 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT202 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT203 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT204 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT301 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT302 => CheckCategory::Flake82020,
|
||||
CheckCode::YTT303 => CheckCategory::Flake82020,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1138,9 +1222,22 @@ impl CheckKind {
|
||||
CheckKind::NoneComparison(_) => &CheckCode::E711,
|
||||
CheckKind::NotInTest => &CheckCode::E713,
|
||||
CheckKind::NotIsTest => &CheckCode::E714,
|
||||
CheckKind::PercentFormatExpectedMapping => &CheckCode::F502,
|
||||
CheckKind::PercentFormatExpectedSequence => &CheckCode::F503,
|
||||
CheckKind::PercentFormatExtraNamedArguments(_) => &CheckCode::F504,
|
||||
CheckKind::PercentFormatInvalidFormat(_) => &CheckCode::F501,
|
||||
CheckKind::PercentFormatMissingArgument(_) => &CheckCode::F505,
|
||||
CheckKind::PercentFormatMixedPositionalAndNamed => &CheckCode::F506,
|
||||
CheckKind::PercentFormatPositionalCountMismatch(..) => &CheckCode::F507,
|
||||
CheckKind::PercentFormatStarRequiresSequence => &CheckCode::F508,
|
||||
CheckKind::PercentFormatUnsupportedFormatCharacter(_) => &CheckCode::F509,
|
||||
CheckKind::RaiseNotImplemented => &CheckCode::F901,
|
||||
CheckKind::ReturnOutsideFunction => &CheckCode::F706,
|
||||
CheckKind::StringDotFormatExtraNamedArguments(_) => &CheckCode::F522,
|
||||
CheckKind::StringDotFormatExtraPositionalArguments(_) => &CheckCode::F523,
|
||||
CheckKind::StringDotFormatInvalidFormat(_) => &CheckCode::F521,
|
||||
CheckKind::StringDotFormatMissingArguments(_) => &CheckCode::F524,
|
||||
CheckKind::StringDotFormatMixingAutomatic => &CheckCode::F525,
|
||||
CheckKind::SyntaxError(_) => &CheckCode::E999,
|
||||
CheckKind::ExpressionsInStarAssignment => &CheckCode::F621,
|
||||
CheckKind::TrueFalseComparison(..) => &CheckCode::E712,
|
||||
@@ -1160,32 +1257,33 @@ impl CheckKind {
|
||||
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
|
||||
CheckKind::BuiltinAttributeShadowing(_) => &CheckCode::A003,
|
||||
// flake8-bugbear
|
||||
CheckKind::UnaryPrefixIncrement => &CheckCode::B002,
|
||||
CheckKind::AssignmentToOsEnviron => &CheckCode::B003,
|
||||
CheckKind::UnreliableCallableCheck => &CheckCode::B004,
|
||||
CheckKind::StripWithMultiCharacters => &CheckCode::B005,
|
||||
CheckKind::MutableArgumentDefault => &CheckCode::B006,
|
||||
CheckKind::UnusedLoopControlVariable(_) => &CheckCode::B007,
|
||||
CheckKind::FunctionCallArgumentDefault(_) => &CheckCode::B008,
|
||||
CheckKind::GetAttrWithConstant => &CheckCode::B009,
|
||||
CheckKind::SetAttrWithConstant => &CheckCode::B010,
|
||||
CheckKind::DoNotAssertFalse => &CheckCode::B011,
|
||||
CheckKind::JumpStatementInFinally(_) => &CheckCode::B012,
|
||||
CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013,
|
||||
CheckKind::DuplicateHandlerException(_) => &CheckCode::B014,
|
||||
CheckKind::UselessComparison => &CheckCode::B015,
|
||||
CheckKind::CannotRaiseLiteral => &CheckCode::B016,
|
||||
CheckKind::NoAssertRaisesException => &CheckCode::B017,
|
||||
CheckKind::UselessExpression => &CheckCode::B018,
|
||||
CheckKind::CachedInstanceMethod => &CheckCode::B019,
|
||||
CheckKind::LoopVariableOverridesIterator(_) => &CheckCode::B020,
|
||||
CheckKind::FStringDocstring => &CheckCode::B021,
|
||||
CheckKind::UselessContextlibSuppress => &CheckCode::B022,
|
||||
CheckKind::AbstractBaseClassWithoutAbstractMethod(_) => &CheckCode::B024,
|
||||
CheckKind::AssignmentToOsEnviron => &CheckCode::B003,
|
||||
CheckKind::CachedInstanceMethod => &CheckCode::B019,
|
||||
CheckKind::CannotRaiseLiteral => &CheckCode::B016,
|
||||
CheckKind::DoNotAssertFalse => &CheckCode::B011,
|
||||
CheckKind::DuplicateHandlerException(_) => &CheckCode::B014,
|
||||
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
|
||||
CheckKind::EmptyMethodWithoutAbstractDecorator(_) => &CheckCode::B027,
|
||||
CheckKind::FStringDocstring => &CheckCode::B021,
|
||||
CheckKind::FunctionCallArgumentDefault(_) => &CheckCode::B008,
|
||||
CheckKind::FunctionUsesLoopVariable(_) => &CheckCode::B023,
|
||||
CheckKind::GetAttrWithConstant => &CheckCode::B009,
|
||||
CheckKind::JumpStatementInFinally(_) => &CheckCode::B012,
|
||||
CheckKind::LoopVariableOverridesIterator(_) => &CheckCode::B020,
|
||||
CheckKind::MutableArgumentDefault => &CheckCode::B006,
|
||||
CheckKind::NoAssertRaisesException => &CheckCode::B017,
|
||||
CheckKind::RaiseWithoutFromInsideExcept => &CheckCode::B904,
|
||||
CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013,
|
||||
CheckKind::SetAttrWithConstant => &CheckCode::B010,
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
|
||||
CheckKind::StripWithMultiCharacters => &CheckCode::B005,
|
||||
CheckKind::UnaryPrefixIncrement => &CheckCode::B002,
|
||||
CheckKind::UnreliableCallableCheck => &CheckCode::B004,
|
||||
CheckKind::UnusedLoopControlVariable(_) => &CheckCode::B007,
|
||||
CheckKind::UselessComparison => &CheckCode::B015,
|
||||
CheckKind::UselessContextlibSuppress => &CheckCode::B022,
|
||||
CheckKind::UselessExpression => &CheckCode::B018,
|
||||
// flake8-blind-except
|
||||
CheckKind::BlindExcept => &CheckCode::BLE001,
|
||||
// flake8-comprehensions
|
||||
@@ -1205,6 +1303,8 @@ impl CheckKind {
|
||||
CheckKind::UnnecessarySubscriptReversal(_) => &CheckCode::C415,
|
||||
CheckKind::UnnecessaryComprehension(..) => &CheckCode::C416,
|
||||
CheckKind::UnnecessaryMap(_) => &CheckCode::C417,
|
||||
// flake8-debugger
|
||||
CheckKind::Debugger(_) => &CheckCode::T100,
|
||||
// flake8-tidy-imports
|
||||
CheckKind::BannedRelativeImport(_) => &CheckCode::I252,
|
||||
// flake8-print
|
||||
@@ -1316,6 +1416,8 @@ impl CheckKind {
|
||||
CheckKind::ErrorSuffixOnExceptionName(..) => &CheckCode::N818,
|
||||
// isort
|
||||
CheckKind::UnsortedImports => &CheckCode::I001,
|
||||
// eradicate
|
||||
CheckKind::CommentedOutCode => &CheckCode::ERA001,
|
||||
// flake8-bandit
|
||||
CheckKind::AssertUsed => &CheckCode::S101,
|
||||
CheckKind::ExecUsed => &CheckCode::S102,
|
||||
@@ -1421,15 +1523,59 @@ impl CheckKind {
|
||||
},
|
||||
CheckKind::NotInTest => "Test for membership should be `not in`".to_string(),
|
||||
CheckKind::NotIsTest => "Test for object identity should be `is not`".to_string(),
|
||||
CheckKind::PercentFormatInvalidFormat(message) => {
|
||||
format!("'...' % ... has invalid format string: {message}")
|
||||
}
|
||||
CheckKind::PercentFormatUnsupportedFormatCharacter(char) => {
|
||||
format!("'...' % ... has unsupported format character '{char}'")
|
||||
}
|
||||
CheckKind::PercentFormatExpectedMapping => {
|
||||
"'...' % ... expected mapping but got sequence".to_string()
|
||||
}
|
||||
CheckKind::PercentFormatExpectedSequence => {
|
||||
"'...' % ... expected sequence but got mapping".to_string()
|
||||
}
|
||||
CheckKind::PercentFormatExtraNamedArguments(missing) => {
|
||||
let message = missing.join(", ");
|
||||
format!("'...' % ... has unused named argument(s): {message}")
|
||||
}
|
||||
CheckKind::PercentFormatMissingArgument(missing) => {
|
||||
let message = missing.join(", ");
|
||||
format!("'...' % ... is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
CheckKind::PercentFormatMixedPositionalAndNamed => {
|
||||
"'...' % ... has mixed positional and named placeholders".to_string()
|
||||
}
|
||||
CheckKind::PercentFormatPositionalCountMismatch(wanted, got) => {
|
||||
format!("'...' % ... has {wanted} placeholder(s) but {got} substitution(s)")
|
||||
}
|
||||
CheckKind::PercentFormatStarRequiresSequence => {
|
||||
"'...' % ... `*` specifier requires sequence".to_string()
|
||||
}
|
||||
CheckKind::RaiseNotImplemented => {
|
||||
"`raise NotImplemented` should be `raise NotImplementedError`".to_string()
|
||||
}
|
||||
CheckKind::ReturnOutsideFunction => {
|
||||
"`return` statement outside of a function/method".to_string()
|
||||
}
|
||||
CheckKind::StringDotFormatExtraNamedArguments(missing) => {
|
||||
let message = missing.join(", ");
|
||||
format!("'...'.format(...) has unused named argument(s): {message}")
|
||||
}
|
||||
CheckKind::StringDotFormatExtraPositionalArguments(missing) => {
|
||||
let message = missing.join(", ");
|
||||
format!("'...'.format(...) has unused arguments at position(s): {message}")
|
||||
}
|
||||
CheckKind::StringDotFormatInvalidFormat(message) => {
|
||||
format!("'...'.format(...) has invalid format string: {message}")
|
||||
}
|
||||
CheckKind::StringDotFormatMissingArguments(missing) => {
|
||||
let message = missing.join(", ");
|
||||
format!("'...'.format(...) is missing argument(s) for placeholder(s): {message}")
|
||||
}
|
||||
CheckKind::StringDotFormatMixingAutomatic => {
|
||||
"'...'.format(...) mixes automatic and manual numbering".to_string()
|
||||
}
|
||||
CheckKind::SyntaxError(message) => format!("SyntaxError: {message}"),
|
||||
CheckKind::ExpressionsInStarAssignment => {
|
||||
"Too many expressions in star-unpacking assignment".to_string()
|
||||
@@ -1515,6 +1661,9 @@ impl CheckKind {
|
||||
"Do not perform function call in argument defaults".to_string()
|
||||
}
|
||||
}
|
||||
CheckKind::FunctionUsesLoopVariable(name) => {
|
||||
format!("Function definition does not bind loop variable `{name}`")
|
||||
}
|
||||
CheckKind::GetAttrWithConstant => "Do not call `getattr` with a constant attribute \
|
||||
value. It is not any safer than normal property \
|
||||
access."
|
||||
@@ -1671,6 +1820,11 @@ impl CheckKind {
|
||||
format!("Unnecessary `map` usage (rewrite using a `{obj_type}` comprehension)")
|
||||
}
|
||||
}
|
||||
// flake8-debugger
|
||||
CheckKind::Debugger(using_type) => match using_type {
|
||||
DebuggerUsingType::Call(name) => format!("Trace found: `{name}` used"),
|
||||
DebuggerUsingType::Import(name) => format!("Import for `{name}` found"),
|
||||
},
|
||||
// flake8-tidy-imports
|
||||
CheckKind::BannedRelativeImport(strictness) => match strictness {
|
||||
Strictness::Parents => {
|
||||
@@ -1976,6 +2130,8 @@ impl CheckKind {
|
||||
}
|
||||
// isort
|
||||
CheckKind::UnsortedImports => "Import block is un-sorted or un-formatted".to_string(),
|
||||
// eradicate
|
||||
CheckKind::CommentedOutCode => "Found commented-out code".to_string(),
|
||||
// flake8-bandit
|
||||
CheckKind::AssertUsed => "Use of `assert` detected".to_string(),
|
||||
CheckKind::ExecUsed => "Use of `exec` detected".to_string(),
|
||||
@@ -2080,6 +2236,7 @@ impl CheckKind {
|
||||
| CheckKind::BlankLineAfterSummary
|
||||
| CheckKind::BlankLineBeforeSection(..)
|
||||
| CheckKind::CapitalizeSectionName(..)
|
||||
| CheckKind::CommentedOutCode
|
||||
| CheckKind::ConvertExitToSysExit
|
||||
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass(..)
|
||||
|
||||
@@ -59,6 +59,7 @@ pub enum CheckCodePrefix {
|
||||
B020,
|
||||
B021,
|
||||
B022,
|
||||
B023,
|
||||
B024,
|
||||
B025,
|
||||
B026,
|
||||
@@ -175,6 +176,10 @@ pub enum CheckCodePrefix {
|
||||
E902,
|
||||
E99,
|
||||
E999,
|
||||
ERA,
|
||||
ERA8,
|
||||
ERA80,
|
||||
ERA001,
|
||||
F,
|
||||
F4,
|
||||
F40,
|
||||
@@ -186,6 +191,22 @@ pub enum CheckCodePrefix {
|
||||
F406,
|
||||
F407,
|
||||
F5,
|
||||
F50,
|
||||
F501,
|
||||
F502,
|
||||
F503,
|
||||
F504,
|
||||
F505,
|
||||
F506,
|
||||
F507,
|
||||
F508,
|
||||
F509,
|
||||
F52,
|
||||
F521,
|
||||
F522,
|
||||
F523,
|
||||
F524,
|
||||
F525,
|
||||
F54,
|
||||
F541,
|
||||
F6,
|
||||
@@ -283,6 +304,9 @@ pub enum CheckCodePrefix {
|
||||
S106,
|
||||
S107,
|
||||
T,
|
||||
T1,
|
||||
T10,
|
||||
T100,
|
||||
T2,
|
||||
T20,
|
||||
T201,
|
||||
@@ -415,6 +439,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::B020,
|
||||
CheckCode::B021,
|
||||
CheckCode::B022,
|
||||
CheckCode::B023,
|
||||
CheckCode::B024,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
@@ -443,6 +468,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::B020,
|
||||
CheckCode::B021,
|
||||
CheckCode::B022,
|
||||
CheckCode::B023,
|
||||
CheckCode::B024,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
@@ -492,6 +518,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::B020,
|
||||
CheckCode::B021,
|
||||
CheckCode::B022,
|
||||
CheckCode::B023,
|
||||
CheckCode::B024,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
@@ -500,6 +527,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B020 => vec![CheckCode::B020],
|
||||
CheckCodePrefix::B021 => vec![CheckCode::B021],
|
||||
CheckCodePrefix::B022 => vec![CheckCode::B022],
|
||||
CheckCodePrefix::B023 => vec![CheckCode::B023],
|
||||
CheckCodePrefix::B024 => vec![CheckCode::B024],
|
||||
CheckCodePrefix::B025 => vec![CheckCode::B025],
|
||||
CheckCodePrefix::B026 => vec![CheckCode::B026],
|
||||
@@ -839,6 +867,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::E902 => vec![CheckCode::E902],
|
||||
CheckCodePrefix::E99 => vec![CheckCode::E999],
|
||||
CheckCodePrefix::E999 => vec![CheckCode::E999],
|
||||
CheckCodePrefix::ERA => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::ERA8 => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::ERA80 => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::ERA001 => vec![CheckCode::ERA001],
|
||||
CheckCodePrefix::F => vec![
|
||||
CheckCode::F401,
|
||||
CheckCode::F402,
|
||||
@@ -847,7 +879,20 @@ impl CheckCodePrefix {
|
||||
CheckCode::F405,
|
||||
CheckCode::F406,
|
||||
CheckCode::F407,
|
||||
CheckCode::F501,
|
||||
CheckCode::F502,
|
||||
CheckCode::F503,
|
||||
CheckCode::F504,
|
||||
CheckCode::F505,
|
||||
CheckCode::F506,
|
||||
CheckCode::F507,
|
||||
CheckCode::F508,
|
||||
CheckCode::F509,
|
||||
CheckCode::F521,
|
||||
CheckCode::F522,
|
||||
CheckCode::F523,
|
||||
CheckCode::F524,
|
||||
CheckCode::F525,
|
||||
CheckCode::F541,
|
||||
CheckCode::F601,
|
||||
CheckCode::F602,
|
||||
@@ -895,7 +940,55 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F405 => vec![CheckCode::F405],
|
||||
CheckCodePrefix::F406 => vec![CheckCode::F406],
|
||||
CheckCodePrefix::F407 => vec![CheckCode::F407],
|
||||
CheckCodePrefix::F5 => vec![CheckCode::F541],
|
||||
CheckCodePrefix::F5 => vec![
|
||||
CheckCode::F501,
|
||||
CheckCode::F502,
|
||||
CheckCode::F503,
|
||||
CheckCode::F504,
|
||||
CheckCode::F505,
|
||||
CheckCode::F506,
|
||||
CheckCode::F507,
|
||||
CheckCode::F508,
|
||||
CheckCode::F509,
|
||||
CheckCode::F521,
|
||||
CheckCode::F522,
|
||||
CheckCode::F523,
|
||||
CheckCode::F524,
|
||||
CheckCode::F525,
|
||||
CheckCode::F541,
|
||||
],
|
||||
CheckCodePrefix::F50 => vec![
|
||||
CheckCode::F501,
|
||||
CheckCode::F502,
|
||||
CheckCode::F503,
|
||||
CheckCode::F504,
|
||||
CheckCode::F505,
|
||||
CheckCode::F506,
|
||||
CheckCode::F507,
|
||||
CheckCode::F508,
|
||||
CheckCode::F509,
|
||||
],
|
||||
CheckCodePrefix::F501 => vec![CheckCode::F501],
|
||||
CheckCodePrefix::F502 => vec![CheckCode::F502],
|
||||
CheckCodePrefix::F503 => vec![CheckCode::F503],
|
||||
CheckCodePrefix::F504 => vec![CheckCode::F504],
|
||||
CheckCodePrefix::F505 => vec![CheckCode::F505],
|
||||
CheckCodePrefix::F506 => vec![CheckCode::F506],
|
||||
CheckCodePrefix::F507 => vec![CheckCode::F507],
|
||||
CheckCodePrefix::F508 => vec![CheckCode::F508],
|
||||
CheckCodePrefix::F509 => vec![CheckCode::F509],
|
||||
CheckCodePrefix::F52 => vec![
|
||||
CheckCode::F521,
|
||||
CheckCode::F522,
|
||||
CheckCode::F523,
|
||||
CheckCode::F524,
|
||||
CheckCode::F525,
|
||||
],
|
||||
CheckCodePrefix::F521 => vec![CheckCode::F521],
|
||||
CheckCodePrefix::F522 => vec![CheckCode::F522],
|
||||
CheckCodePrefix::F523 => vec![CheckCode::F523],
|
||||
CheckCodePrefix::F524 => vec![CheckCode::F524],
|
||||
CheckCodePrefix::F525 => vec![CheckCode::F525],
|
||||
CheckCodePrefix::F54 => vec![CheckCode::F541],
|
||||
CheckCodePrefix::F541 => vec![CheckCode::F541],
|
||||
CheckCodePrefix::F6 => vec![
|
||||
@@ -1115,7 +1208,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::S105 => vec![CheckCode::S105],
|
||||
CheckCodePrefix::S106 => vec![CheckCode::S106],
|
||||
CheckCodePrefix::S107 => vec![CheckCode::S107],
|
||||
CheckCodePrefix::T => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T => vec![CheckCode::T100, CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T1 => vec![CheckCode::T100],
|
||||
CheckCodePrefix::T10 => vec![CheckCode::T100],
|
||||
CheckCodePrefix::T100 => vec![CheckCode::T100],
|
||||
CheckCodePrefix::T2 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T201 => vec![CheckCode::T201],
|
||||
@@ -1289,6 +1385,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B020 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B021 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B022 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B023 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B024 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
|
||||
@@ -1405,6 +1502,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::E902 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::E99 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::E999 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::ERA => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::ERA8 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::ERA80 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::ERA001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::F4 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::F40 => PrefixSpecificity::Tens,
|
||||
@@ -1416,6 +1517,22 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F406 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F407 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F5 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::F50 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::F501 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F502 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F503 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F504 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F505 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F506 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F507 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F508 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F509 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F52 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::F521 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F522 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F523 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F524 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F525 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F54 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::F541 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::F6 => PrefixSpecificity::Hundreds,
|
||||
@@ -1513,6 +1630,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::S106 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S107 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::T => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::T1 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::T10 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::T100 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::T2 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::T20 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::T201 => PrefixSpecificity::Explicit,
|
||||
@@ -1562,3 +1682,26 @@ impl CheckCodePrefix {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::ANN,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
CheckCodePrefix::D,
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::ERA,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::FBT,
|
||||
CheckCodePrefix::I,
|
||||
CheckCodePrefix::M,
|
||||
CheckCodePrefix::N,
|
||||
CheckCodePrefix::Q,
|
||||
CheckCodePrefix::RUF,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
];
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::settings::types::{FilePattern, PatternPrefixPair, PerFileIgnore, Pyth
|
||||
#[command(version)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Cli {
|
||||
#[arg(required = true)]
|
||||
#[arg(required_unless_present_any = ["explain", "generate_shell_completion"])]
|
||||
pub files: Vec<PathBuf>,
|
||||
/// Path to the `pyproject.toml` file to use for configuration.
|
||||
#[arg(long)]
|
||||
@@ -114,6 +114,9 @@ pub struct Cli {
|
||||
/// Explain a rule.
|
||||
#[arg(long)]
|
||||
pub explain: Option<CheckCode>,
|
||||
/// Generate shell completion
|
||||
#[arg(long, hide = true, value_name = "SHELL")]
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
|
||||
51
src/eradicate/checks.rs
Normal file
51
src/eradicate/checks.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use rustpython_ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::{CheckCode, CheckKind};
|
||||
use crate::eradicate::detection::comment_contains_code;
|
||||
use crate::{Check, Settings, SourceCodeLocator};
|
||||
|
||||
fn is_standalone_comment(line: &str) -> bool {
|
||||
for char in line.chars() {
|
||||
if char == '#' {
|
||||
return true;
|
||||
} else if !char.is_whitespace() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
unreachable!("Comment should contain '#' character")
|
||||
}
|
||||
|
||||
/// ERA001
|
||||
pub fn commented_out_code(
|
||||
locator: &SourceCodeLocator,
|
||||
start: Location,
|
||||
end: Location,
|
||||
settings: &Settings,
|
||||
autofix: bool,
|
||||
) -> Option<Check> {
|
||||
let location = Location::new(start.row(), 0);
|
||||
let end_location = Location::new(end.row() + 1, 0);
|
||||
let line = locator.slice_source_code_range(&Range {
|
||||
location,
|
||||
end_location,
|
||||
});
|
||||
|
||||
// Verify that the comment is on its own line, and that it contains code.
|
||||
if is_standalone_comment(&line) && comment_contains_code(&line) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::CommentedOutCode,
|
||||
Range {
|
||||
location: start,
|
||||
end_location: end,
|
||||
},
|
||||
);
|
||||
if autofix && settings.fixable.contains(&CheckCode::ERA001) {
|
||||
check.amend(Fix::deletion(location, end_location));
|
||||
}
|
||||
Some(check)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
223
src/eradicate/detection.rs
Normal file
223
src/eradicate/detection.rs
Normal file
@@ -0,0 +1,223 @@
|
||||
/// See: [eradicate.py](https://github.com/myint/eradicate/blob/98f199940979c94447a461d50d27862b118b282d/eradicate.py)
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
static ALLOWLIST_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
r"(?i)pylint|pyright|noqa|nosec|type:\s*ignore|fmt:\s*(on|off)|isort:\s*(on|off|skip|skip_file|split|dont-add-imports(:\s*\[.*?])?)|TODO|FIXME|XXX"
|
||||
).unwrap()
|
||||
});
|
||||
static BRACKET_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[()\[\]{}\s]+$").unwrap());
|
||||
static CODE_INDICATORS: &[&str] = &[
|
||||
"(", ")", "[", "]", "{", "}", ":", "=", "%", "print", "return", "break", "continue", "import",
|
||||
];
|
||||
static CODE_KEYWORDS: Lazy<Vec<Regex>> = Lazy::new(|| {
|
||||
vec![
|
||||
Regex::new(r"^\s*elif\s+.*\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*else\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*try\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*finally\s*:\s*$").unwrap(),
|
||||
Regex::new(r"^\s*except\s+.*\s*:\s*$").unwrap(),
|
||||
]
|
||||
});
|
||||
static CODING_COMMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)").unwrap());
|
||||
static HASH_NUMBER: Lazy<Regex> = Lazy::new(|| Regex::new(r"#\d").unwrap());
|
||||
static MULTILINE_ASSIGNMENT_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^\s*\w+\s*=.*[(\[{]$").unwrap());
|
||||
static PARTIAL_DICTIONARY_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r#"^\s*['"]\w+['"]\s*:.+[,{]\s*$"#).unwrap());
|
||||
static PRINT_RETURN_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(print|return)\b\s*").unwrap());
|
||||
|
||||
/// Returns `true` if a comment contains Python code.
|
||||
pub fn comment_contains_code(line: &str) -> bool {
|
||||
let line = if let Some(line) = line.trim().strip_prefix('#') {
|
||||
line.trim()
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Ignore non-comment related hashes (e.g., "# Issue #999").
|
||||
if HASH_NUMBER.is_match(line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore whitelisted comments.
|
||||
if ALLOWLIST_REGEX.is_match(line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if CODING_COMMENT_REGEX.is_match(line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that this is possibly code.
|
||||
if CODE_INDICATORS.iter().all(|symbol| !line.contains(symbol)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if multiline_case(line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if CODE_KEYWORDS.iter().any(|symbol| symbol.is_match(line)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let line = PRINT_RETURN_REGEX.replace_all(line, "");
|
||||
|
||||
if PARTIAL_DICTIONARY_REGEX.is_match(&line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finally, compile the source code.
|
||||
rustpython_parser::parser::parse_program(&line, "<filename>").is_ok()
|
||||
}
|
||||
|
||||
/// Returns `true` if a line is probably part of some multiline code.
|
||||
fn multiline_case(line: &str) -> bool {
|
||||
if line.ends_with('\\') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if MULTILINE_ASSIGNMENT_REGEX.is_match(line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if BRACKET_REGEX.is_match(line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::eradicate::detection::comment_contains_code;
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_basic() {
|
||||
assert!(comment_contains_code("# x = 1"));
|
||||
assert!(comment_contains_code("#from foo import eradicate"));
|
||||
assert!(comment_contains_code("#import eradicate"));
|
||||
assert!(comment_contains_code(r#"#"key": value,"#));
|
||||
assert!(comment_contains_code(r#"#"key": "value","#));
|
||||
assert!(comment_contains_code(r#"#"key": 1 + 1,"#));
|
||||
assert!(comment_contains_code("#'key': 1 + 1,"));
|
||||
assert!(comment_contains_code(r#"#"key": {"#));
|
||||
assert!(comment_contains_code("#}"));
|
||||
assert!(comment_contains_code("#} )]"));
|
||||
|
||||
assert!(!comment_contains_code("#"));
|
||||
assert!(!comment_contains_code("# This is a (real) comment."));
|
||||
assert!(!comment_contains_code("# 123"));
|
||||
assert!(!comment_contains_code("# 123.1"));
|
||||
assert!(!comment_contains_code("# 1, 2, 3"));
|
||||
assert!(!comment_contains_code("x = 1 # x = 1"));
|
||||
assert!(!comment_contains_code(
|
||||
"# pylint: disable=redefined-outer-name"
|
||||
));
|
||||
assert!(!comment_contains_code("# Issue #999: This is not code"));
|
||||
|
||||
// TODO(charlie): This should be `true` under aggressive mode.
|
||||
assert!(!comment_contains_code("#},"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_print() {
|
||||
assert!(comment_contains_code("#print"));
|
||||
assert!(comment_contains_code("#print(1)"));
|
||||
assert!(comment_contains_code("#print 1"));
|
||||
|
||||
assert!(!comment_contains_code("#to print"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_return() {
|
||||
assert!(comment_contains_code("#return x"));
|
||||
|
||||
assert!(!comment_contains_code("#to print"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_multiline() {
|
||||
assert!(comment_contains_code("#else:"));
|
||||
assert!(comment_contains_code("# else : "));
|
||||
assert!(comment_contains_code(r#"# "foo %d" % \\"#));
|
||||
assert!(comment_contains_code("#elif True:"));
|
||||
assert!(comment_contains_code("#x = foo("));
|
||||
assert!(comment_contains_code("#except Exception:"));
|
||||
|
||||
assert!(!comment_contains_code("# this is = to that :("));
|
||||
assert!(!comment_contains_code("#else"));
|
||||
assert!(!comment_contains_code("#or else:"));
|
||||
assert!(!comment_contains_code("#else True:"));
|
||||
|
||||
// TODO(charlie): This should be `true` under aggressive mode.
|
||||
assert!(!comment_contains_code("#def foo():"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_sentences() {
|
||||
assert!(!comment_contains_code("#code is good"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_encoding() {
|
||||
assert!(comment_contains_code("# codings=utf-8"));
|
||||
|
||||
assert!(!comment_contains_code("# coding=utf-8"));
|
||||
assert!(!comment_contains_code("#coding= utf-8"));
|
||||
assert!(!comment_contains_code("# coding: utf-8"));
|
||||
assert!(!comment_contains_code("# encoding: utf8"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment_contains_code_with_default_allowlist() {
|
||||
assert!(!comment_contains_code("# pylint: disable=A0123"));
|
||||
assert!(!comment_contains_code("# pylint:disable=A0123"));
|
||||
assert!(!comment_contains_code("# pylint: disable = A0123"));
|
||||
assert!(!comment_contains_code("# pylint:disable = A0123"));
|
||||
assert!(!comment_contains_code("# pyright: reportErrorName=true"));
|
||||
assert!(!comment_contains_code("# noqa"));
|
||||
assert!(!comment_contains_code("# NOQA"));
|
||||
assert!(!comment_contains_code("# noqa: A123"));
|
||||
assert!(!comment_contains_code("# noqa:A123"));
|
||||
assert!(!comment_contains_code("# nosec"));
|
||||
assert!(!comment_contains_code("# fmt: on"));
|
||||
assert!(!comment_contains_code("# fmt: off"));
|
||||
assert!(!comment_contains_code("# fmt:on"));
|
||||
assert!(!comment_contains_code("# fmt:off"));
|
||||
assert!(!comment_contains_code("# isort: on"));
|
||||
assert!(!comment_contains_code("# isort:on"));
|
||||
assert!(!comment_contains_code("# isort: off"));
|
||||
assert!(!comment_contains_code("# isort:off"));
|
||||
assert!(!comment_contains_code("# isort: skip"));
|
||||
assert!(!comment_contains_code("# isort:skip"));
|
||||
assert!(!comment_contains_code("# isort: skip_file"));
|
||||
assert!(!comment_contains_code("# isort:skip_file"));
|
||||
assert!(!comment_contains_code("# isort: split"));
|
||||
assert!(!comment_contains_code("# isort:split"));
|
||||
assert!(!comment_contains_code("# isort: dont-add-imports"));
|
||||
assert!(!comment_contains_code("# isort:dont-add-imports"));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort: dont-add-imports: [\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort:dont-add-imports: [\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort: dont-add-imports:[\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code(
|
||||
"# isort:dont-add-imports:[\"import os\"]"
|
||||
));
|
||||
assert!(!comment_contains_code("# type: ignore"));
|
||||
assert!(!comment_contains_code("# type:ignore"));
|
||||
assert!(!comment_contains_code("# type: ignore[import]"));
|
||||
assert!(!comment_contains_code("# type:ignore[import]"));
|
||||
assert!(!comment_contains_code("# TODO: Do that"));
|
||||
assert!(!comment_contains_code("# FIXME: Fix that"));
|
||||
assert!(!comment_contains_code("# XXX: What ever"));
|
||||
}
|
||||
}
|
||||
2
src/eradicate/mod.rs
Normal file
2
src/eradicate/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod checks;
|
||||
pub mod detection;
|
||||
@@ -21,7 +21,7 @@ where
|
||||
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
// No recurse.
|
||||
// Don't recurse.
|
||||
}
|
||||
StmtKind::Return { value } => self.returns.push(value.as_ref().map(|expr| &**expr)),
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
|
||||
@@ -5,6 +5,19 @@ use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
const FUNC_NAME_ALLOWLIST: &[&str] = &["get", "setdefault", "pop", "fromkeys"];
|
||||
|
||||
/// Returns `true` if an argument is allowed to use a boolean trap. To return
|
||||
/// `true`, the function name must be explicitly allowed, and the argument must
|
||||
/// be either the first or second argument in the call.
|
||||
fn allow_boolean_trap(func: &Expr) -> bool {
|
||||
if let ExprKind::Attribute { attr, .. } = &func.node {
|
||||
FUNC_NAME_ALLOWLIST.contains(&attr.as_ref())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_boolean_arg(arg: &Expr) -> bool {
|
||||
matches!(
|
||||
&arg.node,
|
||||
@@ -60,8 +73,15 @@ pub fn check_boolean_default_value_in_function_definition(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_boolean_positional_value_in_function_call(checker: &mut Checker, args: &[Expr]) {
|
||||
for arg in args {
|
||||
pub fn check_boolean_positional_value_in_function_call(
|
||||
checker: &mut Checker,
|
||||
args: &[Expr],
|
||||
func: &Expr,
|
||||
) {
|
||||
for (index, arg) in args.iter().enumerate() {
|
||||
if index < 2 && allow_boolean_trap(func) {
|
||||
continue;
|
||||
}
|
||||
add_if_boolean(
|
||||
checker,
|
||||
arg,
|
||||
|
||||
232
src/flake8_bugbear/plugins/function_uses_loop_variable.rs
Normal file
232
src/flake8_bugbear/plugins/function_uses_loop_variable.rs
Normal file
@@ -0,0 +1,232 @@
|
||||
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::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
#[derive(Default)]
|
||||
struct LoadedNamesVisitor<'a> {
|
||||
// Tuple of: name, defining expression, and defining range.
|
||||
names: Vec<(&'a str, &'a Expr, Range)>,
|
||||
// If we're in an f-string, the range of the defining expression.
|
||||
in_f_string: Option<Range>,
|
||||
}
|
||||
|
||||
/// `Visitor` to collect all used identifiers in a statement.
|
||||
impl<'a, 'b> Visitor<'b> for LoadedNamesVisitor<'a>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
match &expr.node {
|
||||
ExprKind::JoinedStr { .. } => {
|
||||
let prev_in_f_string = self.in_f_string;
|
||||
self.in_f_string = Some(Range::from_located(expr));
|
||||
visitor::walk_expr(self, expr);
|
||||
self.in_f_string = prev_in_f_string;
|
||||
}
|
||||
ExprKind::Name { id, ctx } if matches!(ctx, ExprContext::Load) => {
|
||||
self.names.push((
|
||||
id,
|
||||
expr,
|
||||
self.in_f_string
|
||||
.unwrap_or_else(|| Range::from_located(expr)),
|
||||
));
|
||||
}
|
||||
_ => visitor::walk_expr(self, expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SuspiciousVariablesVisitor<'a> {
|
||||
names: Vec<(&'a str, &'a Expr, Range)>,
|
||||
}
|
||||
|
||||
/// `Visitor` to collect all suspicious variables (those referenced in
|
||||
/// functions, but not bound as arguments).
|
||||
impl<'a, 'b> Visitor<'b> for SuspiciousVariablesVisitor<'a>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { args, body, .. }
|
||||
| StmtKind::AsyncFunctionDef { args, body, .. } => {
|
||||
// Collect all loaded variable names.
|
||||
let mut visitor = LoadedNamesVisitor::default();
|
||||
for stmt in body {
|
||||
visitor.visit_stmt(stmt);
|
||||
}
|
||||
|
||||
// Collect all argument names.
|
||||
let arg_names = collect_arg_names(args);
|
||||
|
||||
// Treat any non-arguments as "suspicious".
|
||||
self.names.extend(
|
||||
visitor
|
||||
.names
|
||||
.into_iter()
|
||||
.filter(|(id, ..)| !arg_names.contains(id)),
|
||||
);
|
||||
}
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
match &expr.node {
|
||||
ExprKind::Lambda { args, body } => {
|
||||
// Collect all loaded variable names.
|
||||
let mut visitor = LoadedNamesVisitor::default();
|
||||
visitor.visit_expr(body);
|
||||
|
||||
// Collect all argument names.
|
||||
let arg_names = collect_arg_names(args);
|
||||
|
||||
// Treat any non-arguments as "suspicious".
|
||||
self.names.extend(
|
||||
visitor
|
||||
.names
|
||||
.into_iter()
|
||||
.filter(|(id, ..)| !arg_names.contains(id)),
|
||||
);
|
||||
}
|
||||
_ => visitor::walk_expr(self, expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct NamesFromAssignmentsVisitor<'a> {
|
||||
names: FxHashSet<&'a str>,
|
||||
}
|
||||
|
||||
/// `Visitor` to collect all names used in an assignment expression.
|
||||
impl<'a, 'b> Visitor<'b> for NamesFromAssignmentsVisitor<'a>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
match &expr.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
self.names.insert(id.as_str());
|
||||
}
|
||||
ExprKind::Starred { value, .. } => {
|
||||
self.visit_expr(value);
|
||||
}
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
|
||||
for expr in elts {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct AssignedNamesVisitor<'a> {
|
||||
names: FxHashSet<&'a str>,
|
||||
}
|
||||
|
||||
/// `Visitor` to collect all used identifiers in a statement.
|
||||
impl<'a, 'b> Visitor<'b> for AssignedNamesVisitor<'a>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
||||
if matches!(
|
||||
&stmt.node,
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. }
|
||||
) {
|
||||
// Don't recurse.
|
||||
return;
|
||||
}
|
||||
|
||||
match &stmt.node {
|
||||
StmtKind::Assign { targets, .. } => {
|
||||
let mut visitor = NamesFromAssignmentsVisitor::default();
|
||||
for expr in targets {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
self.names.extend(visitor.names);
|
||||
}
|
||||
StmtKind::AugAssign { target, .. }
|
||||
| StmtKind::AnnAssign { target, .. }
|
||||
| StmtKind::For { target, .. }
|
||||
| StmtKind::AsyncFor { target, .. } => {
|
||||
let mut visitor = NamesFromAssignmentsVisitor::default();
|
||||
visitor.visit_expr(target);
|
||||
self.names.extend(visitor.names);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
visitor::walk_stmt(self, stmt);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
if matches!(&expr.node, ExprKind::Lambda { .. }) {
|
||||
// Don't recurse.
|
||||
return;
|
||||
}
|
||||
|
||||
visitor::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn visit_comprehension(&mut self, comprehension: &'b Comprehension) {
|
||||
let mut visitor = NamesFromAssignmentsVisitor::default();
|
||||
visitor.visit_expr(&comprehension.target);
|
||||
self.names.extend(visitor.names);
|
||||
|
||||
visitor::walk_comprehension(self, comprehension);
|
||||
}
|
||||
}
|
||||
|
||||
/// B023
|
||||
pub fn function_uses_loop_variable<'a, 'b>(checker: &'a mut Checker<'b>, node: &Node<'b>)
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
// Identify any "suspicious" variables. These are defined as variables that are
|
||||
// referenced in a function or lambda body, but aren't bound as arguments.
|
||||
let suspicious_variables = {
|
||||
let mut visitor = SuspiciousVariablesVisitor::<'b>::default();
|
||||
match node {
|
||||
Node::Stmt(stmt) => visitor.visit_stmt(stmt),
|
||||
Node::Expr(expr) => visitor.visit_expr(expr),
|
||||
}
|
||||
visitor.names
|
||||
};
|
||||
|
||||
if !suspicious_variables.is_empty() {
|
||||
// Identify any variables that are assigned in the loop (ignoring functions).
|
||||
let reassigned_in_loop = {
|
||||
let mut visitor = AssignedNamesVisitor::<'b>::default();
|
||||
match node {
|
||||
Node::Stmt(stmt) => visitor.visit_stmt(stmt),
|
||||
Node::Expr(expr) => visitor.visit_expr(expr),
|
||||
}
|
||||
visitor.names
|
||||
};
|
||||
|
||||
// If a variable was used in a function or lambda body, and assigned in the
|
||||
// loop, flag it.
|
||||
for (name, expr, range) in suspicious_variables {
|
||||
if reassigned_in_loop.contains(name) {
|
||||
if !checker.seen_b023.contains(&expr) {
|
||||
checker.seen_b023.push(expr);
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::FunctionUsesLoopVariable(name.to_string()),
|
||||
range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ pub use cannot_raise_literal::cannot_raise_literal;
|
||||
pub use duplicate_exceptions::duplicate_exceptions;
|
||||
pub use f_string_docstring::f_string_docstring;
|
||||
pub use function_call_argument_default::function_call_argument_default;
|
||||
pub use function_uses_loop_variable::function_uses_loop_variable;
|
||||
pub use getattr_with_constant::getattr_with_constant;
|
||||
pub use jump_statement_in_finally::jump_statement_in_finally;
|
||||
pub use loop_variable_overrides_iterator::loop_variable_overrides_iterator;
|
||||
@@ -32,6 +33,7 @@ mod cannot_raise_literal;
|
||||
mod duplicate_exceptions;
|
||||
mod f_string_docstring;
|
||||
mod function_call_argument_default;
|
||||
mod function_uses_loop_variable;
|
||||
mod getattr_with_constant;
|
||||
mod jump_statement_in_finally;
|
||||
mod loop_variable_overrides_iterator;
|
||||
|
||||
@@ -39,9 +39,7 @@ pub fn fix_unnecessary_generator_list(
|
||||
let call = match_call(body)?;
|
||||
let arg = match_arg(call)?;
|
||||
|
||||
let generator_exp = if let Expression::GeneratorExp(generator_exp) = &arg.value {
|
||||
generator_exp
|
||||
} else {
|
||||
let Expression::GeneratorExp(generator_exp) = &arg.value else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected node to be: Expression::GeneratorExp"
|
||||
));
|
||||
@@ -82,9 +80,7 @@ pub fn fix_unnecessary_generator_set(
|
||||
let call = match_call(body)?;
|
||||
let arg = match_arg(call)?;
|
||||
|
||||
let generator_exp = if let Expression::GeneratorExp(generator_exp) = &arg.value {
|
||||
generator_exp
|
||||
} else {
|
||||
let Expression::GeneratorExp(generator_exp) = &arg.value else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected node to be: Expression::GeneratorExp"
|
||||
));
|
||||
@@ -126,28 +122,20 @@ pub fn fix_unnecessary_generator_dict(
|
||||
let arg = match_arg(call)?;
|
||||
|
||||
// Extract the (k, v) from `(k, v) for ...`.
|
||||
let generator_exp = if let Expression::GeneratorExp(generator_exp) = &arg.value {
|
||||
generator_exp
|
||||
} else {
|
||||
let Expression::GeneratorExp(generator_exp) = &arg.value else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected node to be: Expression::GeneratorExp"
|
||||
));
|
||||
};
|
||||
let tuple = if let Expression::Tuple(tuple) = &generator_exp.elt.as_ref() {
|
||||
tuple
|
||||
} else {
|
||||
let Expression::Tuple(tuple) = &generator_exp.elt.as_ref() else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Expression::Tuple"));
|
||||
};
|
||||
let key = if let Some(Element::Simple { value, .. }) = &tuple.elements.get(0) {
|
||||
value
|
||||
} else {
|
||||
let Some(Element::Simple { value: key, .. }) = &tuple.elements.get(0) else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected tuple to contain a key as the first element"
|
||||
));
|
||||
};
|
||||
let value = if let Some(Element::Simple { value, .. }) = &tuple.elements.get(1) {
|
||||
value
|
||||
} else {
|
||||
let Some(Element::Simple { value, .. }) = &tuple.elements.get(1) else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected tuple to contain a key as the second element"
|
||||
));
|
||||
@@ -192,9 +180,7 @@ pub fn fix_unnecessary_list_comprehension_set(
|
||||
let call = match_call(body)?;
|
||||
let arg = match_arg(call)?;
|
||||
|
||||
let list_comp = if let Expression::ListComp(list_comp) = &arg.value {
|
||||
list_comp
|
||||
} else {
|
||||
let Expression::ListComp(list_comp) = &arg.value else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Expression::ListComp"));
|
||||
};
|
||||
|
||||
@@ -233,25 +219,18 @@ pub fn fix_unnecessary_list_comprehension_dict(
|
||||
let call = match_call(body)?;
|
||||
let arg = match_arg(call)?;
|
||||
|
||||
let list_comp = if let Expression::ListComp(list_comp) = &arg.value {
|
||||
list_comp
|
||||
} else {
|
||||
let Expression::ListComp(list_comp) = &arg.value else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Expression::ListComp"));
|
||||
};
|
||||
|
||||
let tuple = if let Expression::Tuple(tuple) = &*list_comp.elt {
|
||||
tuple
|
||||
} else {
|
||||
let Expression::Tuple(tuple) = &*list_comp.elt else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Expression::Tuple"));
|
||||
};
|
||||
|
||||
let (key, comma, value) = match &tuple.elements[..] {
|
||||
[Element::Simple {
|
||||
let [Element::Simple {
|
||||
value: key,
|
||||
comma: Some(comma),
|
||||
}, Element::Simple { value, .. }] => (key, comma, value),
|
||||
_ => return Err(anyhow::anyhow!("Expected tuple with two elements")),
|
||||
};
|
||||
}, Element::Simple { value, .. }] = &tuple.elements[..] else { return Err(anyhow::anyhow!("Expected tuple with two elements")) };
|
||||
|
||||
body.value = Expression::DictComp(Box::new(DictComp {
|
||||
key: Box::new(key.clone()),
|
||||
@@ -409,9 +388,7 @@ pub fn fix_unnecessary_collection_call(
|
||||
let mut tree = match_module(&module_text)?;
|
||||
let mut body = match_expr(&mut tree)?;
|
||||
let call = match_call(body)?;
|
||||
let name = if let Expression::Name(name) = &call.func.as_ref() {
|
||||
name
|
||||
} else {
|
||||
let Expression::Name(name) = &call.func.as_ref() else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Expression::Name"));
|
||||
};
|
||||
|
||||
|
||||
64
src/flake8_debugger/checks.rs
Normal file
64
src/flake8_debugger/checks.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Expr, Stmt};
|
||||
|
||||
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::flake8_debugger::types::DebuggerUsingType;
|
||||
|
||||
const DEBUGGERS: &[(&str, &str)] = &[
|
||||
("pdb", "set_trace"),
|
||||
("pudb", "set_trace"),
|
||||
("ipdb", "set_trace"),
|
||||
("ipdb", "sset_trace"),
|
||||
("IPython.terminal.embed", "InteractiveShellEmbed"),
|
||||
("IPython.frontend.terminal.embed", "InteractiveShellEmbed"),
|
||||
("celery.contrib.rdb", "set_trace"),
|
||||
("builtins", "breakpoint"),
|
||||
("", "breakpoint"),
|
||||
];
|
||||
|
||||
/// Checks for the presence of a debugger call.
|
||||
pub fn debugger_call(
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||
import_aliases: &FxHashMap<&str, &str>,
|
||||
) -> Option<Check> {
|
||||
let call_path = dealias_call_path(collect_call_paths(func), import_aliases);
|
||||
if DEBUGGERS
|
||||
.iter()
|
||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||
{
|
||||
Some(Check::new(
|
||||
CheckKind::Debugger(DebuggerUsingType::Call(call_path.join("."))),
|
||||
Range::from_located(expr),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for the presence of a debugger import.
|
||||
pub fn debugger_import(stmt: &Stmt, module: Option<&str>, name: &str) -> Option<Check> {
|
||||
if let Some(module) = module {
|
||||
if let Some((module_name, member)) = DEBUGGERS
|
||||
.iter()
|
||||
.find(|(module_name, member)| module_name == &module && member == &name)
|
||||
{
|
||||
return Some(Check::new(
|
||||
CheckKind::Debugger(DebuggerUsingType::Import(format!("{module_name}.{member}"))),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
} else if DEBUGGERS
|
||||
.iter()
|
||||
.any(|(module_name, ..)| module_name == &name)
|
||||
{
|
||||
return Some(Check::new(
|
||||
CheckKind::Debugger(DebuggerUsingType::Import(name.to_string())),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
2
src/flake8_debugger/mod.rs
Normal file
2
src/flake8_debugger/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod checks;
|
||||
pub mod types;
|
||||
7
src/flake8_debugger/types.rs
Normal file
7
src/flake8_debugger/types.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DebuggerUsingType {
|
||||
Call(String),
|
||||
Import(String),
|
||||
}
|
||||
@@ -39,6 +39,7 @@ pub mod commands;
|
||||
mod cst;
|
||||
mod directives;
|
||||
mod docstrings;
|
||||
mod eradicate;
|
||||
mod flake8_2020;
|
||||
pub mod flake8_annotations;
|
||||
pub mod flake8_bandit;
|
||||
@@ -47,6 +48,7 @@ pub mod flake8_boolean_trap;
|
||||
pub mod flake8_bugbear;
|
||||
mod flake8_builtins;
|
||||
mod flake8_comprehensions;
|
||||
mod flake8_debugger;
|
||||
mod flake8_print;
|
||||
pub mod flake8_quotes;
|
||||
pub mod flake8_tidy_imports;
|
||||
@@ -115,6 +117,7 @@ pub fn check(path: &Path, contents: &str, autofix: bool) -> Result<Vec<Check>> {
|
||||
&directives,
|
||||
&settings,
|
||||
autofix,
|
||||
false,
|
||||
)?;
|
||||
|
||||
Ok(checks)
|
||||
|
||||
127
src/linter.rs
127
src/linter.rs
@@ -7,6 +7,7 @@ use std::path::Path;
|
||||
use anyhow::Result;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use log::debug;
|
||||
use nohash_hasher::IntMap;
|
||||
use rustpython_parser::lexer::LexResult;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
@@ -46,6 +47,7 @@ impl AddAssign for Diagnostics {
|
||||
|
||||
/// Generate a list of `Check` violations from the source code contents at the
|
||||
/// given `Path`.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn check_path(
|
||||
path: &Path,
|
||||
contents: &str,
|
||||
@@ -54,6 +56,7 @@ pub(crate) fn check_path(
|
||||
directives: &Directives,
|
||||
settings: &Settings,
|
||||
autofix: bool,
|
||||
ignore_noqa: bool,
|
||||
) -> Result<Vec<Check>> {
|
||||
// Aggregate all checks.
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
@@ -113,6 +116,7 @@ pub(crate) fn check_path(
|
||||
&directives.noqa_line_for,
|
||||
settings,
|
||||
autofix,
|
||||
ignore_noqa,
|
||||
);
|
||||
|
||||
// Create path ignores.
|
||||
@@ -179,6 +183,7 @@ pub fn lint_path(
|
||||
&directives,
|
||||
settings,
|
||||
autofix.into(),
|
||||
false,
|
||||
)?;
|
||||
|
||||
// Apply autofix.
|
||||
@@ -242,15 +247,19 @@ pub fn add_noqa_to_path(path: &Path, settings: &Settings) -> Result<usize> {
|
||||
directives::Flags::from_settings(settings),
|
||||
);
|
||||
|
||||
// Generate checks.
|
||||
// Generate checks, ignoring any existing `noqa` directives.
|
||||
let checks = check_path(
|
||||
path,
|
||||
&contents,
|
||||
tokens,
|
||||
&locator,
|
||||
&directives,
|
||||
&Directives {
|
||||
noqa_line_for: IntMap::default(),
|
||||
isort_exclusions: directives.isort_exclusions,
|
||||
},
|
||||
settings,
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
|
||||
add_noqa(&checks, &contents, &directives.noqa_line_for, path)
|
||||
@@ -313,6 +322,7 @@ pub fn lint_stdin(
|
||||
&directives,
|
||||
settings,
|
||||
autofix.into(),
|
||||
false,
|
||||
)?;
|
||||
|
||||
// Apply autofix.
|
||||
@@ -373,6 +383,7 @@ pub fn test_path(path: &Path, settings: &Settings, autofix: bool) -> Result<Vec<
|
||||
&directives,
|
||||
settings,
|
||||
autofix,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -388,6 +399,7 @@ mod tests {
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
use crate::settings;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
#[test_case(CheckCode::A001, Path::new("A001.py"); "A001")]
|
||||
#[test_case(CheckCode::A002, Path::new("A002.py"); "A002")]
|
||||
@@ -413,6 +425,7 @@ mod tests {
|
||||
#[test_case(CheckCode::B020, Path::new("B020.py"); "B020")]
|
||||
#[test_case(CheckCode::B021, Path::new("B021.py"); "B021")]
|
||||
#[test_case(CheckCode::B022, Path::new("B022.py"); "B022")]
|
||||
#[test_case(CheckCode::B023, Path::new("B023.py"); "B023")]
|
||||
#[test_case(CheckCode::B024, Path::new("B024.py"); "B024")]
|
||||
#[test_case(CheckCode::B025, Path::new("B025.py"); "B025")]
|
||||
#[test_case(CheckCode::B026, Path::new("B026.py"); "B026")]
|
||||
@@ -475,9 +488,9 @@ mod tests {
|
||||
#[test_case(CheckCode::D414, Path::new("sections.py"); "D414")]
|
||||
#[test_case(CheckCode::D415, Path::new("D.py"); "D415")]
|
||||
#[test_case(CheckCode::D416, Path::new("D.py"); "D416")]
|
||||
#[test_case(CheckCode::D417, Path::new("sections.py"); "D417_0")]
|
||||
#[test_case(CheckCode::D417, Path::new("canonical_numpy_examples.py"); "D417_1")]
|
||||
#[test_case(CheckCode::D417, Path::new("canonical_google_examples.py"); "D417_2")]
|
||||
#[test_case(CheckCode::D417, Path::new("canonical_numpy_examples.py"); "D417_1")]
|
||||
#[test_case(CheckCode::D417, Path::new("sections.py"); "D417_0")]
|
||||
#[test_case(CheckCode::D418, Path::new("D.py"); "D418")]
|
||||
#[test_case(CheckCode::D419, Path::new("D.py"); "D419")]
|
||||
#[test_case(CheckCode::E402, Path::new("E402.py"); "E402")]
|
||||
@@ -493,6 +506,7 @@ mod tests {
|
||||
#[test_case(CheckCode::E742, Path::new("E742.py"); "E742")]
|
||||
#[test_case(CheckCode::E743, Path::new("E743.py"); "E743")]
|
||||
#[test_case(CheckCode::E999, Path::new("E999.py"); "E999")]
|
||||
#[test_case(CheckCode::ERA001, Path::new("ERA001.py"); "ERA001")]
|
||||
#[test_case(CheckCode::F401, Path::new("F401_0.py"); "F401_0")]
|
||||
#[test_case(CheckCode::F401, Path::new("F401_1.py"); "F401_1")]
|
||||
#[test_case(CheckCode::F401, Path::new("F401_2.py"); "F401_2")]
|
||||
@@ -506,7 +520,24 @@ mod tests {
|
||||
#[test_case(CheckCode::F405, Path::new("F405.py"); "F405")]
|
||||
#[test_case(CheckCode::F406, Path::new("F406.py"); "F406")]
|
||||
#[test_case(CheckCode::F407, Path::new("F407.py"); "F407")]
|
||||
#[test_case(CheckCode::F501, Path::new("F50x.py"); "F501")]
|
||||
#[test_case(CheckCode::F502, Path::new("F502.py"); "F502_1")]
|
||||
#[test_case(CheckCode::F502, Path::new("F50x.py"); "F502_0")]
|
||||
#[test_case(CheckCode::F503, Path::new("F503.py"); "F503_1")]
|
||||
#[test_case(CheckCode::F503, Path::new("F50x.py"); "F503_0")]
|
||||
#[test_case(CheckCode::F504, Path::new("F504.py"); "F504_1")]
|
||||
#[test_case(CheckCode::F504, Path::new("F50x.py"); "F504_0")]
|
||||
#[test_case(CheckCode::F505, Path::new("F504.py"); "F505_1")]
|
||||
#[test_case(CheckCode::F505, Path::new("F50x.py"); "F505_0")]
|
||||
#[test_case(CheckCode::F506, Path::new("F50x.py"); "F506")]
|
||||
#[test_case(CheckCode::F507, Path::new("F50x.py"); "F507")]
|
||||
#[test_case(CheckCode::F508, Path::new("F50x.py"); "F508")]
|
||||
#[test_case(CheckCode::F509, Path::new("F50x.py"); "F509")]
|
||||
#[test_case(CheckCode::F521, Path::new("F521.py"); "F521")]
|
||||
#[test_case(CheckCode::F522, Path::new("F522.py"); "F522")]
|
||||
#[test_case(CheckCode::F523, Path::new("F523.py"); "F523")]
|
||||
#[test_case(CheckCode::F524, Path::new("F524.py"); "F524")]
|
||||
#[test_case(CheckCode::F525, Path::new("F525.py"); "F525")]
|
||||
#[test_case(CheckCode::F541, Path::new("F541.py"); "F541")]
|
||||
#[test_case(CheckCode::F601, Path::new("F601.py"); "F601")]
|
||||
#[test_case(CheckCode::F602, Path::new("F602.py"); "F602")]
|
||||
@@ -532,6 +563,9 @@ mod tests {
|
||||
#[test_case(CheckCode::F831, Path::new("F831.py"); "F831")]
|
||||
#[test_case(CheckCode::F841, Path::new("F841.py"); "F841")]
|
||||
#[test_case(CheckCode::F901, Path::new("F901.py"); "F901")]
|
||||
#[test_case(CheckCode::FBT001, Path::new("FBT.py"); "FBT001")]
|
||||
#[test_case(CheckCode::FBT002, Path::new("FBT.py"); "FBT002")]
|
||||
#[test_case(CheckCode::FBT003, Path::new("FBT.py"); "FBT003")]
|
||||
#[test_case(CheckCode::N801, Path::new("N801.py"); "N801")]
|
||||
#[test_case(CheckCode::N802, Path::new("N802.py"); "N802")]
|
||||
#[test_case(CheckCode::N803, Path::new("N803.py"); "N803")]
|
||||
@@ -547,12 +581,23 @@ mod tests {
|
||||
#[test_case(CheckCode::N816, Path::new("N816.py"); "N816")]
|
||||
#[test_case(CheckCode::N817, Path::new("N817.py"); "N817")]
|
||||
#[test_case(CheckCode::N818, Path::new("N818.py"); "N818")]
|
||||
#[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")]
|
||||
#[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")]
|
||||
#[test_case(CheckCode::RUF003, Path::new("RUF003.py"); "RUF003")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_0.py"); "RUF101_0")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_1.py"); "RUF101_1")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_2.py"); "RUF101_2")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_3.py"); "RUF101_3")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_4.py"); "RUF101_4")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_5.py"); "RUF101_5")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_6.py"); "RUF101_6")]
|
||||
#[test_case(CheckCode::S101, Path::new("S101.py"); "S101")]
|
||||
#[test_case(CheckCode::S102, Path::new("S102.py"); "S102")]
|
||||
#[test_case(CheckCode::S104, Path::new("S104.py"); "S104")]
|
||||
#[test_case(CheckCode::S105, Path::new("S105.py"); "S105")]
|
||||
#[test_case(CheckCode::S106, Path::new("S106.py"); "S106")]
|
||||
#[test_case(CheckCode::S107, Path::new("S107.py"); "S107")]
|
||||
#[test_case(CheckCode::T100, Path::new("T100.py"); "T100")]
|
||||
#[test_case(CheckCode::T201, Path::new("T201.py"); "T201")]
|
||||
#[test_case(CheckCode::T203, Path::new("T203.py"); "T203")]
|
||||
#[test_case(CheckCode::U001, Path::new("U001.py"); "U001")]
|
||||
@@ -566,6 +611,7 @@ mod tests {
|
||||
#[test_case(CheckCode::U009, Path::new("U009_1.py"); "U009_1")]
|
||||
#[test_case(CheckCode::U009, Path::new("U009_2.py"); "U009_2")]
|
||||
#[test_case(CheckCode::U009, Path::new("U009_3.py"); "U009_3")]
|
||||
#[test_case(CheckCode::U009, Path::new("U009_4.py"); "U009_4")]
|
||||
#[test_case(CheckCode::U010, Path::new("U010.py"); "U010")]
|
||||
#[test_case(CheckCode::U011, Path::new("U011_0.py"); "U011_0")]
|
||||
#[test_case(CheckCode::U011, Path::new("U011_1.py"); "U011_1")]
|
||||
@@ -578,16 +624,6 @@ mod tests {
|
||||
#[test_case(CheckCode::W292, Path::new("W292_2.py"); "W292_2")]
|
||||
#[test_case(CheckCode::W605, Path::new("W605_0.py"); "W605_0")]
|
||||
#[test_case(CheckCode::W605, Path::new("W605_1.py"); "W605_1")]
|
||||
#[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")]
|
||||
#[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")]
|
||||
#[test_case(CheckCode::RUF003, Path::new("RUF003.py"); "RUF003")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_0.py"); "RUF101_0")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_1.py"); "RUF101_1")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_2.py"); "RUF101_2")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_3.py"); "RUF101_3")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_4.py"); "RUF101_4")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_5.py"); "RUF101_5")]
|
||||
#[test_case(CheckCode::RUF101, Path::new("RUF101_6.py"); "RUF101_6")]
|
||||
#[test_case(CheckCode::YTT101, Path::new("YTT101.py"); "YTT101")]
|
||||
#[test_case(CheckCode::YTT102, Path::new("YTT102.py"); "YTT102")]
|
||||
#[test_case(CheckCode::YTT103, Path::new("YTT103.py"); "YTT103")]
|
||||
@@ -598,9 +634,6 @@ mod tests {
|
||||
#[test_case(CheckCode::YTT301, Path::new("YTT301.py"); "YTT301")]
|
||||
#[test_case(CheckCode::YTT302, Path::new("YTT302.py"); "YTT302")]
|
||||
#[test_case(CheckCode::YTT303, Path::new("YTT303.py"); "YTT303")]
|
||||
#[test_case(CheckCode::FBT001, Path::new("FBT.py"); "FBT001")]
|
||||
#[test_case(CheckCode::FBT002, Path::new("FBT.py"); "FBT002")]
|
||||
#[test_case(CheckCode::FBT003, Path::new("FBT.py"); "FBT003")]
|
||||
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
@@ -663,4 +696,64 @@ mod tests {
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotations_pep_585_p37() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/future_annotations.py"),
|
||||
&settings::Settings {
|
||||
target_version: PythonVersion::Py37,
|
||||
..settings::Settings::for_rule(CheckCode::U006)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotations_pep_585_py310() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/future_annotations.py"),
|
||||
&settings::Settings {
|
||||
target_version: PythonVersion::Py310,
|
||||
..settings::Settings::for_rule(CheckCode::U006)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotations_pep_604_p37() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/future_annotations.py"),
|
||||
&settings::Settings {
|
||||
target_version: PythonVersion::Py37,
|
||||
..settings::Settings::for_rule(CheckCode::U007)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_annotations_pep_604_py310() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/future_annotations.py"),
|
||||
&settings::Settings {
|
||||
target_version: PythonVersion::Py310,
|
||||
..settings::Settings::for_rule(CheckCode::U007)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
17
src/main.rs
17
src/main.rs
@@ -30,7 +30,7 @@ use ::ruff::settings::{pyproject, Settings};
|
||||
use ::ruff::updates;
|
||||
use ::ruff::{cache, commands};
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use clap::{CommandFactory, Parser};
|
||||
use colored::Colorize;
|
||||
use log::{debug, error};
|
||||
use notify::{raw_watcher, RecursiveMode, Watcher};
|
||||
@@ -193,6 +193,16 @@ fn inner_main() -> Result<ExitCode> {
|
||||
let log_level = extract_log_level(&cli);
|
||||
set_up_logging(&log_level)?;
|
||||
|
||||
if let Some(code) = cli.explain {
|
||||
commands::explain(&code, cli.format)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
if let Some(shell) = cli.generate_shell_completion {
|
||||
shell.generate(&mut Cli::command(), &mut std::io::stdout());
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
// Find the project root and pyproject.toml.
|
||||
let project_root = pyproject::find_project_root(&cli.files);
|
||||
match &project_root {
|
||||
@@ -256,11 +266,6 @@ fn inner_main() -> Result<ExitCode> {
|
||||
configuration.show_source = true;
|
||||
}
|
||||
|
||||
if let Some(code) = cli.explain {
|
||||
commands::explain(&code, cli.format)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
if cli.show_settings && cli.show_files {
|
||||
eprintln!("Error: specify --show-settings or show-files (not both).");
|
||||
return Ok(ExitCode::FAILURE);
|
||||
|
||||
42
src/noqa.rs
42
src/noqa.rs
@@ -13,9 +13,9 @@ static NO_QA_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
r"(?P<spaces>\s*)(?P<noqa>(?i:# noqa)(?::\s?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?)",
|
||||
)
|
||||
.expect("Invalid regex")
|
||||
.unwrap()
|
||||
});
|
||||
static SPLIT_COMMA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").expect("Invalid regex"));
|
||||
static SPLIT_COMMA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").unwrap());
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Directive<'a> {
|
||||
@@ -99,18 +99,42 @@ fn add_noqa_inner(
|
||||
Some(codes) => {
|
||||
match extract_noqa_directive(line) {
|
||||
Directive::None => {
|
||||
output.push_str(line);
|
||||
// Add existing content.
|
||||
output.push_str(line.trim_end());
|
||||
|
||||
// Add `noqa` directive.
|
||||
output.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = codes.iter().map(AsRef::as_ref).collect();
|
||||
let suffix = codes.join(", ");
|
||||
output.push_str(&suffix);
|
||||
output.push('\n');
|
||||
count += 1;
|
||||
}
|
||||
Directive::All(_, start, _) | Directive::Codes(_, start, ..) => {
|
||||
output.push_str(&line[..start]);
|
||||
output.push_str("# noqa: ");
|
||||
let mut new_line = String::new();
|
||||
|
||||
// Add existing content.
|
||||
new_line.push_str(line[..start].trim_end());
|
||||
|
||||
// Add `noqa` directive.
|
||||
new_line.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = codes.iter().map(AsRef::as_ref).collect();
|
||||
let suffix = codes.join(", ");
|
||||
new_line.push_str(&suffix);
|
||||
|
||||
output.push_str(&new_line);
|
||||
output.push('\n');
|
||||
|
||||
// Only count if the new line is an actual edit.
|
||||
if &new_line != line {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
let codes: Vec<&str> = codes.iter().map(AsRef::as_ref).collect();
|
||||
output.push_str(&codes.join(", "));
|
||||
output.push('\n');
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{Arg, Constant, ExprKind, Location, StmtKind};
|
||||
use rustpython_ast::{Constant, ExprKind, Location, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::whitespace;
|
||||
@@ -15,7 +15,7 @@ use crate::docstrings::constants;
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
use crate::docstrings::sections::{section_contexts, SectionContext};
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
use crate::visibility::{is_init, is_magic, is_overload, is_staticmethod, Visibility};
|
||||
use crate::visibility::{is_init, is_magic, is_overload, is_override, is_staticmethod, Visibility};
|
||||
|
||||
/// D100, D101, D102, D103, D104, D105, D106, D107
|
||||
pub fn not_missing(
|
||||
@@ -88,7 +88,7 @@ pub fn not_missing(
|
||||
}
|
||||
}
|
||||
DefinitionKind::Method(stmt) => {
|
||||
if is_overload(stmt) {
|
||||
if is_overload(stmt) || is_override(stmt) {
|
||||
true
|
||||
} else if is_magic(stmt) {
|
||||
if checker.settings.enabled.contains(&CheckCode::D105) {
|
||||
@@ -1303,8 +1303,9 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||
args: arguments, ..
|
||||
} = &parent.node
|
||||
{
|
||||
// Collect all the arguments into a single vector.
|
||||
let mut all_arguments: Vec<&Arg> = arguments
|
||||
// Look for arguments that weren't included in the docstring.
|
||||
let mut missing_arg_names: BTreeSet<String> = BTreeSet::default();
|
||||
for arg in arguments
|
||||
.args
|
||||
.iter()
|
||||
.chain(arguments.posonlyargs.iter())
|
||||
@@ -1316,33 +1317,38 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||
&& !is_staticmethod(parent),
|
||||
),
|
||||
)
|
||||
.collect();
|
||||
{
|
||||
let arg_name = arg.node.arg.as_str();
|
||||
if !arg_name.starts_with('_') && !docstrings_args.contains(&arg_name) {
|
||||
missing_arg_names.insert(arg_name.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// Check specifically for `vararg` and `kwarg`, which can be prefixed with a
|
||||
// single or double star, respectively.
|
||||
if let Some(arg) = &arguments.vararg {
|
||||
all_arguments.push(arg);
|
||||
let arg_name = arg.node.arg.as_str();
|
||||
let starred_arg_name = format!("*{arg_name}");
|
||||
if !arg_name.starts_with('_')
|
||||
&& !docstrings_args.contains(&arg_name)
|
||||
&& !docstrings_args.contains(&starred_arg_name.as_str())
|
||||
{
|
||||
missing_arg_names.insert(starred_arg_name);
|
||||
}
|
||||
}
|
||||
if let Some(arg) = &arguments.kwarg {
|
||||
all_arguments.push(arg);
|
||||
}
|
||||
|
||||
// Look for arguments that weren't included in the docstring.
|
||||
let mut missing_args: BTreeSet<&str> = BTreeSet::default();
|
||||
for arg in all_arguments {
|
||||
let arg_name = arg.node.arg.as_str();
|
||||
if arg_name.starts_with('_') {
|
||||
continue;
|
||||
let starred_arg_name = format!("**{arg_name}");
|
||||
if !arg_name.starts_with('_')
|
||||
&& !docstrings_args.contains(&arg_name)
|
||||
&& !docstrings_args.contains(&starred_arg_name.as_str())
|
||||
{
|
||||
missing_arg_names.insert(starred_arg_name);
|
||||
}
|
||||
if docstrings_args.contains(&arg_name) {
|
||||
continue;
|
||||
}
|
||||
missing_args.insert(arg_name);
|
||||
}
|
||||
|
||||
if !missing_args.is_empty() {
|
||||
let names = missing_args
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.sorted()
|
||||
.collect();
|
||||
if !missing_arg_names.is_empty() {
|
||||
let names = missing_arg_names.into_iter().sorted().collect();
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::DocumentAllArguments(names),
|
||||
Range::from_located(parent),
|
||||
@@ -1354,7 +1360,7 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||
|
||||
// See: `GOOGLE_ARGS_REGEX` in `pydocstyle/checker.py`.
|
||||
static GOOGLE_ARGS_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^\s*(\w+)\s*(\(.*?\))?\s*:\n?\s*.+").expect("Invalid regex"));
|
||||
Lazy::new(|| Regex::new(r"^\s*(\w+)\s*(\(.*?\))?\s*:\n?\s*.+").unwrap());
|
||||
|
||||
fn args_section(checker: &mut Checker, definition: &Definition, context: &SectionContext) {
|
||||
let mut args_sections: Vec<String> = vec![];
|
||||
|
||||
102
src/pyflakes/cformat.rs
Normal file
102
src/pyflakes/cformat.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
//! Implements helper functions for using vendored/cformat.rs
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::vendored::cformat::{
|
||||
CFormatError, CFormatPart, CFormatQuantity, CFormatSpec, CFormatString,
|
||||
};
|
||||
|
||||
pub(crate) struct CFormatSummary {
|
||||
pub starred: bool,
|
||||
pub num_positional: usize,
|
||||
pub keywords: FxHashSet<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for CFormatSummary {
|
||||
type Error = CFormatError;
|
||||
|
||||
fn try_from(literal: &str) -> Result<Self, Self::Error> {
|
||||
let format_string = CFormatString::from_str(literal)?;
|
||||
|
||||
let mut starred = false;
|
||||
let mut num_positional = 0;
|
||||
let mut keywords = FxHashSet::default();
|
||||
|
||||
for format_part in format_string.parts {
|
||||
if let CFormatPart::Spec(CFormatSpec {
|
||||
mapping_key,
|
||||
min_field_width,
|
||||
precision,
|
||||
..
|
||||
}) = format_part.1
|
||||
{
|
||||
match mapping_key {
|
||||
Some(k) => {
|
||||
keywords.insert(k);
|
||||
}
|
||||
None => {
|
||||
num_positional += 1;
|
||||
}
|
||||
};
|
||||
if min_field_width == Some(CFormatQuantity::FromValuesTuple) {
|
||||
num_positional += 1;
|
||||
starred = true;
|
||||
}
|
||||
if precision == Some(CFormatQuantity::FromValuesTuple) {
|
||||
num_positional += 1;
|
||||
starred = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CFormatSummary {
|
||||
starred,
|
||||
num_positional,
|
||||
keywords,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cformat_summary() {
|
||||
let literal = "%(foo)s %s %d %(bar)x";
|
||||
|
||||
let expected_positional = 2;
|
||||
let expected_keywords = ["foo", "bar"].into_iter().map(String::from).collect();
|
||||
|
||||
let format_summary = CFormatSummary::try_from(literal).unwrap();
|
||||
assert!(!format_summary.starred);
|
||||
assert_eq!(format_summary.num_positional, expected_positional);
|
||||
assert_eq!(format_summary.keywords, expected_keywords);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cformat_summary_starred() {
|
||||
let format_summary1 = CFormatSummary::try_from("%*s %*d").unwrap();
|
||||
assert!(format_summary1.starred);
|
||||
assert_eq!(format_summary1.num_positional, 4);
|
||||
|
||||
let format_summary2 = CFormatSummary::try_from("%s %.*d").unwrap();
|
||||
assert!(format_summary2.starred);
|
||||
assert_eq!(format_summary2.num_positional, 3);
|
||||
|
||||
let format_summary3 = CFormatSummary::try_from("%s %*.*d").unwrap();
|
||||
assert!(format_summary3.starred);
|
||||
assert_eq!(format_summary3.num_positional, 4);
|
||||
|
||||
let format_summary4 = CFormatSummary::try_from("%s %1d").unwrap();
|
||||
assert!(!format_summary4.starred);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cformat_summary_invalid() {
|
||||
assert!(CFormatSummary::try_from("%").is_err());
|
||||
assert!(CFormatSummary::try_from("%(foo).").is_err());
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,351 @@
|
||||
use std::string::ToString;
|
||||
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{Keyword, KeywordData};
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind,
|
||||
};
|
||||
|
||||
use crate::ast::types::{BindingKind, FunctionScope, Range, Scope, ScopeKind};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::vendored::format::{FieldName, FormatPart, FormatString, FromTemplate};
|
||||
use crate::pyflakes::cformat::CFormatSummary;
|
||||
use crate::pyflakes::format::FormatSummary;
|
||||
|
||||
// F521
|
||||
pub fn string_dot_format_invalid(literal: &str, location: Range) -> Option<Check> {
|
||||
match FormatString::from_str(literal) {
|
||||
Err(e) => Some(Check::new(
|
||||
CheckKind::StringDotFormatInvalidFormat(e.to_string()),
|
||||
location,
|
||||
)),
|
||||
Ok(format_string) => {
|
||||
for part in format_string.format_parts {
|
||||
if let FormatPart::Field { field_name, .. } = &part {
|
||||
if let Err(e) = FieldName::parse(field_name) {
|
||||
return Some(Check::new(
|
||||
CheckKind::StringDotFormatInvalidFormat(e.to_string()),
|
||||
location,
|
||||
));
|
||||
fn has_star_star_kwargs(keywords: &[Keyword]) -> bool {
|
||||
keywords.iter().any(|k| {
|
||||
let KeywordData { arg, .. } = &k.node;
|
||||
arg.is_none()
|
||||
})
|
||||
}
|
||||
|
||||
fn has_star_args(args: &[Expr]) -> bool {
|
||||
args.iter()
|
||||
.any(|a| matches!(&a.node, ExprKind::Starred { .. }))
|
||||
}
|
||||
|
||||
/// F502
|
||||
pub(crate) fn percent_format_expected_mapping(
|
||||
summary: &CFormatSummary,
|
||||
right: &Expr,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if summary.keywords.is_empty() {
|
||||
None
|
||||
} else {
|
||||
// Tuple, List, Set (+comprehensions)
|
||||
match right.node {
|
||||
ExprKind::List { .. }
|
||||
| ExprKind::Tuple { .. }
|
||||
| ExprKind::Set { .. }
|
||||
| ExprKind::ListComp { .. }
|
||||
| ExprKind::SetComp { .. }
|
||||
| ExprKind::GeneratorExp { .. } => Some(Check::new(
|
||||
CheckKind::PercentFormatExpectedMapping,
|
||||
location,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// F503
|
||||
pub(crate) fn percent_format_expected_sequence(
|
||||
summary: &CFormatSummary,
|
||||
right: &Expr,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if summary.num_positional <= 1 {
|
||||
None
|
||||
} else {
|
||||
match right.node {
|
||||
ExprKind::Dict { .. } | ExprKind::DictComp { .. } => Some(Check::new(
|
||||
CheckKind::PercentFormatExpectedSequence,
|
||||
location,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// F504
|
||||
pub(crate) fn percent_format_extra_named_arguments(
|
||||
summary: &CFormatSummary,
|
||||
right: &Expr,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if summary.num_positional > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let ExprKind::Dict { keys, values } = &right.node {
|
||||
if values.len() > keys.len() {
|
||||
return None; // contains **x splat
|
||||
}
|
||||
|
||||
let missing: Vec<&String> = keys
|
||||
.iter()
|
||||
.filter_map(|k| match &k.node {
|
||||
// We can only check that string literals exist
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} => {
|
||||
if summary.keywords.contains(value) {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
if missing.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::PercentFormatExtraNamedArguments(
|
||||
missing.iter().map(|&s| s.clone()).collect(),
|
||||
),
|
||||
location,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// F505
|
||||
pub(crate) fn percent_format_missing_arguments(
|
||||
summary: &CFormatSummary,
|
||||
right: &Expr,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if summary.num_positional > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let ExprKind::Dict { keys, values } = &right.node {
|
||||
if values.len() > keys.len() {
|
||||
return None; // contains **x splat
|
||||
}
|
||||
|
||||
let mut keywords = FxHashSet::default();
|
||||
for key in keys {
|
||||
match &key.node {
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} => {
|
||||
keywords.insert(value);
|
||||
}
|
||||
_ => {
|
||||
return None; // Dynamic keys present
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let missing: Vec<&String> = summary
|
||||
.keywords
|
||||
.iter()
|
||||
.filter(|k| !keywords.contains(k))
|
||||
.collect();
|
||||
|
||||
if missing.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::PercentFormatMissingArgument(
|
||||
missing.iter().map(|&s| s.clone()).collect(),
|
||||
),
|
||||
location,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// F506
|
||||
pub(crate) fn percent_format_mixed_positional_and_named(
|
||||
summary: &CFormatSummary,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if summary.num_positional == 0 || summary.keywords.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::PercentFormatMixedPositionalAndNamed,
|
||||
location,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// F507
|
||||
pub(crate) fn percent_format_positional_count_mismatch(
|
||||
summary: &CFormatSummary,
|
||||
right: &Expr,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if !summary.keywords.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match &right.node {
|
||||
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } | ExprKind::Set { elts, .. } => {
|
||||
let mut found = 0;
|
||||
for elt in elts {
|
||||
if let ExprKind::Starred { .. } = &elt.node {
|
||||
return None;
|
||||
}
|
||||
found += 1;
|
||||
}
|
||||
|
||||
if found == summary.num_positional {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::PercentFormatPositionalCountMismatch(summary.num_positional, found),
|
||||
location,
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// F508
|
||||
pub(crate) fn percent_format_star_requires_sequence(
|
||||
summary: &CFormatSummary,
|
||||
right: &Expr,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if summary.starred {
|
||||
match &right.node {
|
||||
ExprKind::Dict { .. } | ExprKind::DictComp { .. } => Some(Check::new(
|
||||
CheckKind::PercentFormatStarRequiresSequence,
|
||||
location,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// F522
|
||||
pub(crate) fn string_dot_format_extra_named_arguments(
|
||||
summary: &FormatSummary,
|
||||
keywords: &[Keyword],
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if has_star_star_kwargs(keywords) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let keywords = keywords.iter().filter_map(|k| {
|
||||
let KeywordData { arg, .. } = &k.node;
|
||||
arg.as_ref()
|
||||
});
|
||||
|
||||
let missing: Vec<&String> = keywords
|
||||
.filter(|&k| !summary.keywords.contains(k))
|
||||
.collect();
|
||||
|
||||
if missing.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::StringDotFormatExtraNamedArguments(
|
||||
missing.iter().map(|&s| s.clone()).collect(),
|
||||
),
|
||||
location,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// F523
|
||||
pub(crate) fn string_dot_format_extra_positional_arguments(
|
||||
summary: &FormatSummary,
|
||||
args: &[Expr],
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if has_star_args(args) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let missing: Vec<String> = (0..args.len())
|
||||
.filter(|i| !(summary.autos.contains(i) || summary.indexes.contains(i)))
|
||||
.map(|i| i.to_string())
|
||||
.collect();
|
||||
|
||||
if missing.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::StringDotFormatExtraPositionalArguments(missing),
|
||||
location,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// F524
|
||||
pub(crate) fn string_dot_format_missing_argument(
|
||||
summary: &FormatSummary,
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if has_star_args(args) || has_star_star_kwargs(keywords) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let keywords: FxHashSet<_> = keywords
|
||||
.iter()
|
||||
.filter_map(|k| {
|
||||
let KeywordData { arg, .. } = &k.node;
|
||||
arg.as_ref()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let missing: Vec<String> = summary
|
||||
.autos
|
||||
.iter()
|
||||
.chain(summary.indexes.iter())
|
||||
.filter(|&&i| i >= args.len())
|
||||
.map(ToString::to_string)
|
||||
.chain(
|
||||
summary
|
||||
.keywords
|
||||
.iter()
|
||||
.filter(|k| !keywords.contains(k))
|
||||
.cloned(),
|
||||
)
|
||||
.collect();
|
||||
|
||||
if missing.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::StringDotFormatMissingArguments(missing),
|
||||
location,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// F525
|
||||
pub(crate) fn string_dot_format_mixing_automatic(
|
||||
summary: &FormatSummary,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if summary.autos.is_empty() || summary.indexes.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Check::new(
|
||||
CheckKind::StringDotFormatMixingAutomatic,
|
||||
location,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,14 +22,10 @@ pub fn remove_unused_imports(
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let mut tree = match_module(&module_text)?;
|
||||
|
||||
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple"));
|
||||
};
|
||||
let body = if let Some(SmallStatement::Import(body)) = body.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
let Some(SmallStatement::Import(body)) = body.body.first_mut() else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected node to be: SmallStatement::ImportFrom"
|
||||
));
|
||||
@@ -80,22 +76,16 @@ pub fn remove_unused_import_froms(
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let mut tree = match_module(&module_text)?;
|
||||
|
||||
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple"));
|
||||
};
|
||||
let body = if let Some(SmallStatement::ImportFrom(body)) = body.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
let Some(SmallStatement::ImportFrom(body)) = body.body.first_mut() else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected node to be: SmallStatement::ImportFrom"
|
||||
));
|
||||
};
|
||||
|
||||
let aliases = if let ImportNames::Aliases(aliases) = &mut body.names {
|
||||
aliases
|
||||
} else {
|
||||
let ImportNames::Aliases(aliases) = &mut body.names else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Aliases"));
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
//! Implements helper functions for using vendored/format.rs
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
||||
use crate::vendored::format::FormatParseError;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::vendored::format::{
|
||||
FieldName, FieldType, FormatParseError, FormatPart, FormatString, FromTemplate,
|
||||
};
|
||||
|
||||
impl fmt::Display for FormatParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@@ -20,3 +25,110 @@ impl fmt::Display for FormatParseError {
|
||||
write!(f, "{message}")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FormatSummary {
|
||||
pub autos: FxHashSet<usize>,
|
||||
pub indexes: FxHashSet<usize>,
|
||||
pub keywords: FxHashSet<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for FormatSummary {
|
||||
type Error = FormatParseError;
|
||||
|
||||
fn try_from(literal: &str) -> Result<Self, Self::Error> {
|
||||
let format_string = FormatString::from_str(literal)?;
|
||||
|
||||
let mut autos = FxHashSet::default();
|
||||
let mut indexes = FxHashSet::default();
|
||||
let mut keywords = FxHashSet::default();
|
||||
|
||||
for format_part in format_string.format_parts {
|
||||
if let FormatPart::Field {
|
||||
field_name,
|
||||
format_spec,
|
||||
..
|
||||
} = format_part
|
||||
{
|
||||
let parsed = FieldName::parse(&field_name)?;
|
||||
match parsed.field_type {
|
||||
FieldType::Auto => autos.insert(autos.len()),
|
||||
FieldType::Index(i) => indexes.insert(i),
|
||||
FieldType::Keyword(k) => keywords.insert(k),
|
||||
};
|
||||
|
||||
let nested = FormatString::from_str(&format_spec)?;
|
||||
for nested_part in nested.format_parts {
|
||||
if let FormatPart::Field { field_name, .. } = nested_part {
|
||||
let parsed = FieldName::parse(&field_name)?;
|
||||
match parsed.field_type {
|
||||
FieldType::Auto => autos.insert(autos.len()),
|
||||
FieldType::Index(i) => indexes.insert(i),
|
||||
FieldType::Keyword(k) => keywords.insert(k),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(FormatSummary {
|
||||
autos,
|
||||
indexes,
|
||||
keywords,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::vendored::format::FromTemplate;
|
||||
|
||||
#[test]
|
||||
fn test_format_summary() {
|
||||
let literal = "foo{foo}a{}b{2}c{2}d{1}{}{}e{bar}{foo}f{spam}";
|
||||
|
||||
let expected_autos = [0usize, 1usize, 2usize].into_iter().collect();
|
||||
let expected_indexes = [1usize, 2usize].into_iter().collect();
|
||||
let expected_keywords = ["foo", "bar", "spam"]
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect();
|
||||
|
||||
let format_summary = FormatSummary::try_from(literal).unwrap();
|
||||
|
||||
assert_eq!(format_summary.autos, expected_autos);
|
||||
assert_eq!(format_summary.indexes, expected_indexes);
|
||||
assert_eq!(format_summary.keywords, expected_keywords);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_summary_nested() {
|
||||
let literal = "foo{foo}a{:{}{}}b{2:{3}{4}}c{2}d{1}{}e{bar:{spam}{eggs}}";
|
||||
|
||||
let expected_autos = [0usize, 1usize, 2usize, 3usize].into_iter().collect();
|
||||
let expected_indexes = [1usize, 2usize, 3usize, 4usize].into_iter().collect();
|
||||
let expected_keywords = ["foo", "bar", "spam", "eggs"]
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.collect();
|
||||
|
||||
let format_summary = FormatSummary::try_from(literal).unwrap();
|
||||
|
||||
assert_eq!(format_summary.autos, expected_autos);
|
||||
assert_eq!(format_summary.indexes, expected_indexes);
|
||||
assert_eq!(format_summary.keywords, expected_keywords);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_summary_invalid() {
|
||||
assert!(FormatSummary::try_from("{").is_err());
|
||||
|
||||
let literal = "{foo}a{}b{bar..}";
|
||||
assert!(FormatString::from_str(literal).is_ok());
|
||||
assert!(FormatSummary::try_from(literal).is_err());
|
||||
|
||||
let literal_nested = "{foo}a{}b{bar:{spam..}}";
|
||||
assert!(FormatString::from_str(literal_nested).is_ok());
|
||||
assert!(FormatSummary::try_from(literal_nested).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod cformat;
|
||||
pub mod checks;
|
||||
pub mod fixes;
|
||||
mod format;
|
||||
pub mod format;
|
||||
pub mod plugins;
|
||||
|
||||
@@ -209,15 +209,21 @@ pub enum SubscriptKind {
|
||||
PEP593AnnotatedSubscript,
|
||||
}
|
||||
|
||||
pub fn match_annotated_subscript(
|
||||
pub fn match_annotated_subscript<F>(
|
||||
expr: &Expr,
|
||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||
import_aliases: &FxHashMap<&str, &str>,
|
||||
) -> Option<SubscriptKind> {
|
||||
is_builtin: F,
|
||||
) -> Option<SubscriptKind>
|
||||
where
|
||||
F: Fn(&str) -> bool,
|
||||
{
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
if !call_path.is_empty() {
|
||||
for (module, member) in SUBSCRIPTS {
|
||||
if match_call_path(&call_path, module, member, from_imports) {
|
||||
if match_call_path(&call_path, module, member, from_imports)
|
||||
&& (!module.is_empty() || is_builtin(member))
|
||||
{
|
||||
return Some(SubscriptKind::AnnotatedSubscript);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +111,7 @@ pub fn remove_super_arguments(locator: &SourceCodeLocator, expr: &Expr) -> Optio
|
||||
let range = Range::from_located(expr);
|
||||
let contents = locator.slice_source_code_range(&range);
|
||||
|
||||
let mut tree = match libcst_native::parse_module(&contents, None) {
|
||||
Ok(m) => m,
|
||||
Err(_) => return None,
|
||||
};
|
||||
let mut tree = libcst_native::parse_module(&contents, None).ok()?;
|
||||
|
||||
if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||
if let Some(SmallStatement::Expr(body)) = body.body.first_mut() {
|
||||
@@ -150,22 +147,16 @@ pub fn remove_unnecessary_future_import(
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let mut tree = match_module(&module_text)?;
|
||||
|
||||
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple"));
|
||||
};
|
||||
let body = if let Some(SmallStatement::ImportFrom(body)) = body.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
let Some(SmallStatement::ImportFrom(body)) = body.body.first_mut() else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected node to be: SmallStatement::ImportFrom"
|
||||
));
|
||||
};
|
||||
|
||||
let aliases = if let ImportNames::Aliases(aliases) = &mut body.names {
|
||||
aliases
|
||||
} else {
|
||||
let ImportNames::Aliases(aliases) = &mut body.names else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Aliases"));
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use log::error;
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Located, Location};
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, KeywordData, Location};
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::token::Tok;
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::checks::{Check, CheckCode, CheckKind};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
const OPEN_FUNC_NAME: &str = "open";
|
||||
const MODE_KEYWORD_ARGUMENT: &str = "mode";
|
||||
|
||||
enum OpenMode {
|
||||
U,
|
||||
@@ -56,15 +57,19 @@ impl OpenMode {
|
||||
}
|
||||
}
|
||||
|
||||
fn match_open(expr: &Expr) -> Option<&Expr> {
|
||||
if let ExprKind::Call { func, args, .. } = &expr.node {
|
||||
// TODO(andberger): Verify that "open" is still bound to the built-in function.
|
||||
fn match_open(expr: &Expr) -> (Option<&Expr>, Vec<Keyword>) {
|
||||
if let ExprKind::Call {
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
} = &expr.node
|
||||
{
|
||||
if match_name_or_attr(func, OPEN_FUNC_NAME) {
|
||||
// Return the "open mode" parameter.
|
||||
return args.get(1);
|
||||
// Return the "open mode" parameter and keywords.
|
||||
return (args.get(1), keywords.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
(None, vec![])
|
||||
}
|
||||
|
||||
fn create_check(
|
||||
@@ -101,19 +106,36 @@ fn create_remove_param_fix(
|
||||
location: expr.location,
|
||||
end_location: expr.end_location.unwrap(),
|
||||
});
|
||||
// Find the last comma before mode_param
|
||||
// and delete that comma as well as mode_param.
|
||||
// Find the last comma before mode_param and create a deletion fix
|
||||
// starting from the comma and ending after mode_param.
|
||||
let mut fix_start: Option<Location> = None;
|
||||
let mut fix_end: Option<Location> = None;
|
||||
let mut is_first_arg: bool = false;
|
||||
let mut delete_first_arg: bool = false;
|
||||
for (start, tok, end) in lexer::make_tokenizer(&content).flatten() {
|
||||
let start = helpers::to_absolute(start, expr.location);
|
||||
let end = helpers::to_absolute(end, expr.location);
|
||||
if start == mode_param.location {
|
||||
if is_first_arg {
|
||||
delete_first_arg = true;
|
||||
continue;
|
||||
}
|
||||
fix_end = Some(end);
|
||||
break;
|
||||
}
|
||||
if delete_first_arg && matches!(tok, Tok::Name { .. }) {
|
||||
fix_end = Some(start);
|
||||
break;
|
||||
}
|
||||
if matches!(tok, Tok::Lpar) {
|
||||
is_first_arg = true;
|
||||
fix_start = Some(end);
|
||||
}
|
||||
if matches!(tok, Tok::Comma) {
|
||||
fix_start = Some(start);
|
||||
is_first_arg = false;
|
||||
if !delete_first_arg {
|
||||
fix_start = Some(start);
|
||||
}
|
||||
}
|
||||
}
|
||||
match (fix_start, fix_end) {
|
||||
@@ -126,20 +148,45 @@ fn create_remove_param_fix(
|
||||
|
||||
/// U015
|
||||
pub fn redundant_open_modes(checker: &mut Checker, expr: &Expr) {
|
||||
// TODO(andberger): Add "mode" keyword argument handling to handle invocations
|
||||
// on the following formats:
|
||||
// - `open("foo", mode="U")`
|
||||
// - `open(name="foo", mode="U")`
|
||||
// - `open(mode="U", name="foo")`
|
||||
if let Some(mode_param) = match_open(expr) {
|
||||
if let Located {
|
||||
node:
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(mode_param_value),
|
||||
..
|
||||
},
|
||||
// If `open` has been rebound, skip this check entirely.
|
||||
if !checker.is_builtin(OPEN_FUNC_NAME) {
|
||||
return;
|
||||
}
|
||||
let (mode_param, keywords): (Option<&Expr>, Vec<Keyword>) = match_open(expr);
|
||||
if mode_param.is_none() && !keywords.is_empty() {
|
||||
if let Some(value) = keywords.iter().find_map(|keyword| {
|
||||
let KeywordData { arg, value } = &keyword.node;
|
||||
if arg
|
||||
.as_ref()
|
||||
.map(|arg| arg == MODE_KEYWORD_ARGUMENT)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(mode_param_value),
|
||||
..
|
||||
} = &value.node
|
||||
{
|
||||
if let Ok(mode) = OpenMode::from_str(mode_param_value.as_str()) {
|
||||
checker.add_check(create_check(
|
||||
expr,
|
||||
value,
|
||||
mode.replacement_value(),
|
||||
checker.locator,
|
||||
checker.patch(&CheckCode::U015),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(mode_param) = mode_param {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(mode_param_value),
|
||||
..
|
||||
} = mode_param
|
||||
} = &mode_param.node
|
||||
{
|
||||
if let Ok(mode) = OpenMode::from_str(mode_param_value.as_str()) {
|
||||
checker.add_check(create_check(
|
||||
|
||||
@@ -21,12 +21,7 @@ fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
|
||||
|
||||
/// Return `true` if `exit` is (still) bound as a built-in in the current scope.
|
||||
fn has_builtin_exit_in_scope(checker: &Checker) -> bool {
|
||||
!is_module_star_imported(checker, "sys")
|
||||
&& checker
|
||||
.current_scopes()
|
||||
.find_map(|scope| scope.values.get("exit"))
|
||||
.map(|binding| matches!(binding.kind, BindingKind::Builtin))
|
||||
.unwrap_or_default()
|
||||
!is_module_star_imported(checker, "sys") && checker.is_builtin("exit")
|
||||
}
|
||||
|
||||
/// Return the appropriate `sys.exit` reference based on the current set of
|
||||
|
||||
@@ -9,7 +9,7 @@ use once_cell::sync::Lazy;
|
||||
use path_absolutize::path_dedot;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::checks_gen::{CheckCodePrefix, CATEGORIES};
|
||||
use crate::settings::pyproject::load_options;
|
||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||
use crate::{
|
||||
@@ -117,28 +117,7 @@ impl Configuration {
|
||||
.unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]),
|
||||
extend_select: options.extend_select.unwrap_or_default(),
|
||||
fix: options.fix.unwrap_or_default(),
|
||||
fixable: options.fixable.unwrap_or_else(|| {
|
||||
// TODO(charlie): Autogenerate this list.
|
||||
vec![
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
CheckCodePrefix::D,
|
||||
CheckCodePrefix::E,
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::I,
|
||||
CheckCodePrefix::M,
|
||||
CheckCodePrefix::N,
|
||||
CheckCodePrefix::Q,
|
||||
CheckCodePrefix::RUF,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
]
|
||||
}),
|
||||
fixable: options.fixable.unwrap_or_else(|| CATEGORIES.to_vec()),
|
||||
unfixable: options.unfixable.unwrap_or_default(),
|
||||
ignore: options.ignore.unwrap_or_default(),
|
||||
line_length: options.line_length.unwrap_or(88),
|
||||
|
||||
158
src/snapshots/ruff__linter__tests__B023_B023.py.snap
Normal file
158
src/snapshots/ruff__linter__tests__B023_B023.py.snap
Normal file
@@ -0,0 +1,158 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 12
|
||||
column: 29
|
||||
end_location:
|
||||
row: 12
|
||||
column: 30
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: y
|
||||
location:
|
||||
row: 13
|
||||
column: 29
|
||||
end_location:
|
||||
row: 13
|
||||
column: 30
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 16
|
||||
column: 15
|
||||
end_location:
|
||||
row: 16
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 28
|
||||
column: 18
|
||||
end_location:
|
||||
row: 28
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 29
|
||||
column: 18
|
||||
end_location:
|
||||
row: 29
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 30
|
||||
column: 18
|
||||
end_location:
|
||||
row: 30
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 31
|
||||
column: 21
|
||||
end_location:
|
||||
row: 31
|
||||
column: 22
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 40
|
||||
column: 33
|
||||
end_location:
|
||||
row: 40
|
||||
column: 34
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: x
|
||||
location:
|
||||
row: 42
|
||||
column: 13
|
||||
end_location:
|
||||
row: 42
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: a
|
||||
location:
|
||||
row: 50
|
||||
column: 29
|
||||
end_location:
|
||||
row: 50
|
||||
column: 30
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: a_
|
||||
location:
|
||||
row: 51
|
||||
column: 29
|
||||
end_location:
|
||||
row: 51
|
||||
column: 31
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: b
|
||||
location:
|
||||
row: 52
|
||||
column: 29
|
||||
end_location:
|
||||
row: 52
|
||||
column: 30
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: c
|
||||
location:
|
||||
row: 53
|
||||
column: 29
|
||||
end_location:
|
||||
row: 53
|
||||
column: 30
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: j
|
||||
location:
|
||||
row: 61
|
||||
column: 16
|
||||
end_location:
|
||||
row: 61
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: k
|
||||
location:
|
||||
row: 61
|
||||
column: 20
|
||||
end_location:
|
||||
row: 61
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: l
|
||||
location:
|
||||
row: 68
|
||||
column: 9
|
||||
end_location:
|
||||
row: 68
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind:
|
||||
FunctionUsesLoopVariable: i
|
||||
location:
|
||||
row: 82
|
||||
column: 12
|
||||
end_location:
|
||||
row: 82
|
||||
column: 18
|
||||
fix: ~
|
||||
|
||||
@@ -4,10 +4,10 @@ expression: checks
|
||||
---
|
||||
- kind: PublicClass
|
||||
location:
|
||||
row: 14
|
||||
row: 15
|
||||
column: 0
|
||||
end_location:
|
||||
row: 67
|
||||
row: 72
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,26 +4,26 @@ expression: checks
|
||||
---
|
||||
- kind: PublicMethod
|
||||
location:
|
||||
row: 22
|
||||
row: 23
|
||||
column: 4
|
||||
end_location:
|
||||
row: 25
|
||||
row: 26
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: PublicMethod
|
||||
location:
|
||||
row: 51
|
||||
row: 56
|
||||
column: 4
|
||||
end_location:
|
||||
row: 54
|
||||
row: 59
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: PublicMethod
|
||||
location:
|
||||
row: 63
|
||||
row: 68
|
||||
column: 4
|
||||
end_location:
|
||||
row: 67
|
||||
row: 72
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ expression: checks
|
||||
---
|
||||
- kind: PublicFunction
|
||||
location:
|
||||
row: 395
|
||||
row: 400
|
||||
column: 0
|
||||
end_location:
|
||||
row: 396
|
||||
row: 401
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ expression: checks
|
||||
---
|
||||
- kind: MagicMethod
|
||||
location:
|
||||
row: 59
|
||||
row: 64
|
||||
column: 4
|
||||
end_location:
|
||||
row: 62
|
||||
row: 67
|
||||
column: 4
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,18 +4,18 @@ expression: checks
|
||||
---
|
||||
- kind: PublicInit
|
||||
location:
|
||||
row: 55
|
||||
row: 60
|
||||
column: 4
|
||||
end_location:
|
||||
row: 58
|
||||
row: 63
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: PublicInit
|
||||
location:
|
||||
row: 529
|
||||
row: 534
|
||||
column: 4
|
||||
end_location:
|
||||
row: 533
|
||||
row: 538
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -5,69 +5,69 @@ expression: checks
|
||||
- kind:
|
||||
NoBlankLineBeforeFunction: 1
|
||||
location:
|
||||
row: 132
|
||||
row: 137
|
||||
column: 4
|
||||
end_location:
|
||||
row: 132
|
||||
row: 137
|
||||
column: 24
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 131
|
||||
row: 136
|
||||
column: 0
|
||||
end_location:
|
||||
row: 132
|
||||
row: 137
|
||||
column: 0
|
||||
- kind:
|
||||
NoBlankLineBeforeFunction: 1
|
||||
location:
|
||||
row: 146
|
||||
row: 151
|
||||
column: 4
|
||||
end_location:
|
||||
row: 146
|
||||
row: 151
|
||||
column: 37
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 145
|
||||
row: 150
|
||||
column: 0
|
||||
end_location:
|
||||
row: 146
|
||||
row: 151
|
||||
column: 0
|
||||
- kind:
|
||||
NoBlankLineBeforeFunction: 1
|
||||
location:
|
||||
row: 541
|
||||
row: 546
|
||||
column: 4
|
||||
end_location:
|
||||
row: 544
|
||||
row: 549
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 540
|
||||
row: 545
|
||||
column: 0
|
||||
end_location:
|
||||
row: 541
|
||||
row: 546
|
||||
column: 0
|
||||
- kind:
|
||||
NoBlankLineBeforeFunction: 1
|
||||
location:
|
||||
row: 563
|
||||
row: 568
|
||||
column: 4
|
||||
end_location:
|
||||
row: 566
|
||||
row: 571
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 562
|
||||
row: 567
|
||||
column: 0
|
||||
end_location:
|
||||
row: 563
|
||||
row: 568
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -5,69 +5,69 @@ expression: checks
|
||||
- kind:
|
||||
NoBlankLineAfterFunction: 1
|
||||
location:
|
||||
row: 137
|
||||
row: 142
|
||||
column: 4
|
||||
end_location:
|
||||
row: 137
|
||||
row: 142
|
||||
column: 24
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 138
|
||||
row: 143
|
||||
column: 0
|
||||
end_location:
|
||||
row: 139
|
||||
row: 144
|
||||
column: 0
|
||||
- kind:
|
||||
NoBlankLineAfterFunction: 1
|
||||
location:
|
||||
row: 146
|
||||
row: 151
|
||||
column: 4
|
||||
end_location:
|
||||
row: 146
|
||||
row: 151
|
||||
column: 37
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 147
|
||||
row: 152
|
||||
column: 0
|
||||
end_location:
|
||||
row: 148
|
||||
row: 153
|
||||
column: 0
|
||||
- kind:
|
||||
NoBlankLineAfterFunction: 1
|
||||
location:
|
||||
row: 550
|
||||
row: 555
|
||||
column: 4
|
||||
end_location:
|
||||
row: 553
|
||||
row: 558
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 554
|
||||
row: 559
|
||||
column: 0
|
||||
end_location:
|
||||
row: 555
|
||||
row: 560
|
||||
column: 0
|
||||
- kind:
|
||||
NoBlankLineAfterFunction: 1
|
||||
location:
|
||||
row: 563
|
||||
row: 568
|
||||
column: 4
|
||||
end_location:
|
||||
row: 566
|
||||
row: 571
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 567
|
||||
row: 572
|
||||
column: 0
|
||||
end_location:
|
||||
row: 568
|
||||
row: 573
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -5,52 +5,52 @@ expression: checks
|
||||
- kind:
|
||||
OneBlankLineBeforeClass: 0
|
||||
location:
|
||||
row: 156
|
||||
row: 161
|
||||
column: 4
|
||||
end_location:
|
||||
row: 156
|
||||
row: 161
|
||||
column: 32
|
||||
fix:
|
||||
patch:
|
||||
content: "\n"
|
||||
location:
|
||||
row: 156
|
||||
row: 161
|
||||
column: 0
|
||||
end_location:
|
||||
row: 156
|
||||
row: 161
|
||||
column: 0
|
||||
- kind:
|
||||
OneBlankLineBeforeClass: 0
|
||||
location:
|
||||
row: 187
|
||||
row: 192
|
||||
column: 4
|
||||
end_location:
|
||||
row: 187
|
||||
row: 192
|
||||
column: 45
|
||||
fix:
|
||||
patch:
|
||||
content: "\n"
|
||||
location:
|
||||
row: 187
|
||||
row: 192
|
||||
column: 0
|
||||
end_location:
|
||||
row: 187
|
||||
row: 192
|
||||
column: 0
|
||||
- kind:
|
||||
OneBlankLineBeforeClass: 0
|
||||
location:
|
||||
row: 521
|
||||
row: 526
|
||||
column: 4
|
||||
end_location:
|
||||
row: 527
|
||||
row: 532
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: "\n"
|
||||
location:
|
||||
row: 521
|
||||
row: 526
|
||||
column: 0
|
||||
end_location:
|
||||
row: 521
|
||||
row: 526
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -5,35 +5,35 @@ expression: checks
|
||||
- kind:
|
||||
OneBlankLineAfterClass: 0
|
||||
location:
|
||||
row: 176
|
||||
row: 181
|
||||
column: 4
|
||||
end_location:
|
||||
row: 176
|
||||
row: 181
|
||||
column: 24
|
||||
fix:
|
||||
patch:
|
||||
content: "\n"
|
||||
location:
|
||||
row: 177
|
||||
row: 182
|
||||
column: 0
|
||||
end_location:
|
||||
row: 177
|
||||
row: 182
|
||||
column: 0
|
||||
- kind:
|
||||
OneBlankLineAfterClass: 0
|
||||
location:
|
||||
row: 187
|
||||
row: 192
|
||||
column: 4
|
||||
end_location:
|
||||
row: 187
|
||||
row: 192
|
||||
column: 45
|
||||
fix:
|
||||
patch:
|
||||
content: "\n"
|
||||
location:
|
||||
row: 188
|
||||
row: 193
|
||||
column: 0
|
||||
end_location:
|
||||
row: 188
|
||||
row: 193
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -4,34 +4,34 @@ expression: checks
|
||||
---
|
||||
- kind: BlankLineAfterSummary
|
||||
location:
|
||||
row: 195
|
||||
row: 200
|
||||
column: 4
|
||||
end_location:
|
||||
row: 198
|
||||
row: 203
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: "\n"
|
||||
location:
|
||||
row: 196
|
||||
row: 201
|
||||
column: 0
|
||||
end_location:
|
||||
row: 196
|
||||
row: 201
|
||||
column: 0
|
||||
- kind: BlankLineAfterSummary
|
||||
location:
|
||||
row: 205
|
||||
row: 210
|
||||
column: 4
|
||||
end_location:
|
||||
row: 210
|
||||
row: 215
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: "\n"
|
||||
location:
|
||||
row: 206
|
||||
row: 211
|
||||
column: 0
|
||||
end_location:
|
||||
row: 208
|
||||
row: 213
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -4,34 +4,34 @@ expression: checks
|
||||
---
|
||||
- kind: NoUnderIndentation
|
||||
location:
|
||||
row: 227
|
||||
row: 232
|
||||
column: 0
|
||||
end_location:
|
||||
row: 227
|
||||
row: 232
|
||||
column: 0
|
||||
fix:
|
||||
patch:
|
||||
content: " "
|
||||
location:
|
||||
row: 227
|
||||
row: 232
|
||||
column: 0
|
||||
end_location:
|
||||
row: 227
|
||||
row: 232
|
||||
column: 0
|
||||
- kind: NoUnderIndentation
|
||||
location:
|
||||
row: 435
|
||||
row: 440
|
||||
column: 0
|
||||
end_location:
|
||||
row: 435
|
||||
row: 440
|
||||
column: 0
|
||||
fix:
|
||||
patch:
|
||||
content: " "
|
||||
location:
|
||||
row: 435
|
||||
row: 440
|
||||
column: 0
|
||||
end_location:
|
||||
row: 435
|
||||
row: 440
|
||||
column: 4
|
||||
|
||||
|
||||
@@ -4,50 +4,50 @@ expression: checks
|
||||
---
|
||||
- kind: NoOverIndentation
|
||||
location:
|
||||
row: 247
|
||||
row: 252
|
||||
column: 0
|
||||
end_location:
|
||||
row: 247
|
||||
row: 252
|
||||
column: 0
|
||||
fix:
|
||||
patch:
|
||||
content: " "
|
||||
location:
|
||||
row: 247
|
||||
row: 252
|
||||
column: 0
|
||||
end_location:
|
||||
row: 247
|
||||
row: 252
|
||||
column: 7
|
||||
- kind: NoOverIndentation
|
||||
location:
|
||||
row: 259
|
||||
row: 264
|
||||
column: 0
|
||||
end_location:
|
||||
row: 259
|
||||
row: 264
|
||||
column: 0
|
||||
fix:
|
||||
patch:
|
||||
content: " "
|
||||
location:
|
||||
row: 259
|
||||
row: 264
|
||||
column: 0
|
||||
end_location:
|
||||
row: 259
|
||||
row: 264
|
||||
column: 8
|
||||
- kind: NoOverIndentation
|
||||
location:
|
||||
row: 267
|
||||
row: 272
|
||||
column: 0
|
||||
end_location:
|
||||
row: 267
|
||||
row: 272
|
||||
column: 0
|
||||
fix:
|
||||
patch:
|
||||
content: " "
|
||||
location:
|
||||
row: 267
|
||||
row: 272
|
||||
column: 0
|
||||
end_location:
|
||||
row: 267
|
||||
row: 272
|
||||
column: 8
|
||||
|
||||
|
||||
@@ -4,18 +4,18 @@ expression: checks
|
||||
---
|
||||
- kind: NewLineAfterLastParagraph
|
||||
location:
|
||||
row: 276
|
||||
row: 281
|
||||
column: 4
|
||||
end_location:
|
||||
row: 278
|
||||
row: 283
|
||||
column: 19
|
||||
fix:
|
||||
patch:
|
||||
content: "\n "
|
||||
location:
|
||||
row: 278
|
||||
row: 283
|
||||
column: 16
|
||||
end_location:
|
||||
row: 278
|
||||
row: 283
|
||||
column: 16
|
||||
|
||||
|
||||
@@ -4,50 +4,50 @@ expression: checks
|
||||
---
|
||||
- kind: NoSurroundingWhitespace
|
||||
location:
|
||||
row: 283
|
||||
row: 288
|
||||
column: 4
|
||||
end_location:
|
||||
row: 283
|
||||
row: 288
|
||||
column: 33
|
||||
fix:
|
||||
patch:
|
||||
content: Whitespace at the end.
|
||||
location:
|
||||
row: 283
|
||||
row: 288
|
||||
column: 7
|
||||
end_location:
|
||||
row: 283
|
||||
row: 288
|
||||
column: 30
|
||||
- kind: NoSurroundingWhitespace
|
||||
location:
|
||||
row: 288
|
||||
row: 293
|
||||
column: 4
|
||||
end_location:
|
||||
row: 288
|
||||
row: 293
|
||||
column: 37
|
||||
fix:
|
||||
patch:
|
||||
content: Whitespace at everywhere.
|
||||
location:
|
||||
row: 288
|
||||
row: 293
|
||||
column: 7
|
||||
end_location:
|
||||
row: 288
|
||||
row: 293
|
||||
column: 34
|
||||
- kind: NoSurroundingWhitespace
|
||||
location:
|
||||
row: 294
|
||||
row: 299
|
||||
column: 4
|
||||
end_location:
|
||||
row: 297
|
||||
row: 302
|
||||
column: 7
|
||||
fix:
|
||||
patch:
|
||||
content: Whitespace at the beginning.
|
||||
location:
|
||||
row: 294
|
||||
row: 299
|
||||
column: 7
|
||||
end_location:
|
||||
row: 294
|
||||
row: 299
|
||||
column: 36
|
||||
|
||||
|
||||
@@ -5,35 +5,35 @@ expression: checks
|
||||
- kind:
|
||||
NoBlankLineBeforeClass: 1
|
||||
location:
|
||||
row: 165
|
||||
row: 170
|
||||
column: 4
|
||||
end_location:
|
||||
row: 165
|
||||
row: 170
|
||||
column: 29
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 164
|
||||
row: 169
|
||||
column: 0
|
||||
end_location:
|
||||
row: 165
|
||||
row: 170
|
||||
column: 0
|
||||
- kind:
|
||||
NoBlankLineBeforeClass: 1
|
||||
location:
|
||||
row: 176
|
||||
row: 181
|
||||
column: 4
|
||||
end_location:
|
||||
row: 176
|
||||
row: 181
|
||||
column: 24
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 175
|
||||
row: 180
|
||||
column: 0
|
||||
end_location:
|
||||
row: 176
|
||||
row: 181
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ expression: checks
|
||||
---
|
||||
- kind: MultiLineSummaryFirstLine
|
||||
location:
|
||||
row: 124
|
||||
row: 129
|
||||
column: 4
|
||||
end_location:
|
||||
row: 126
|
||||
row: 131
|
||||
column: 7
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,154 +4,154 @@ expression: checks
|
||||
---
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 195
|
||||
row: 200
|
||||
column: 4
|
||||
end_location:
|
||||
row: 198
|
||||
row: 203
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 205
|
||||
column: 4
|
||||
end_location:
|
||||
row: 210
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
column: 4
|
||||
end_location:
|
||||
row: 215
|
||||
column: 4
|
||||
end_location:
|
||||
row: 219
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 225
|
||||
row: 220
|
||||
column: 4
|
||||
end_location:
|
||||
row: 229
|
||||
row: 224
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 235
|
||||
row: 230
|
||||
column: 4
|
||||
end_location:
|
||||
row: 239
|
||||
row: 234
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 240
|
||||
column: 4
|
||||
end_location:
|
||||
row: 244
|
||||
column: 3
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 245
|
||||
row: 250
|
||||
column: 4
|
||||
end_location:
|
||||
row: 249
|
||||
row: 254
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 255
|
||||
row: 260
|
||||
column: 4
|
||||
end_location:
|
||||
row: 259
|
||||
row: 264
|
||||
column: 11
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 265
|
||||
row: 270
|
||||
column: 4
|
||||
end_location:
|
||||
row: 269
|
||||
row: 274
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 276
|
||||
row: 281
|
||||
column: 4
|
||||
end_location:
|
||||
row: 278
|
||||
row: 283
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 294
|
||||
row: 299
|
||||
column: 4
|
||||
end_location:
|
||||
row: 297
|
||||
row: 302
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 338
|
||||
column: 4
|
||||
end_location:
|
||||
row: 343
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 378
|
||||
column: 4
|
||||
end_location:
|
||||
row: 381
|
||||
row: 348
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 387
|
||||
row: 383
|
||||
column: 4
|
||||
end_location:
|
||||
row: 391
|
||||
row: 386
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 433
|
||||
row: 392
|
||||
column: 4
|
||||
end_location:
|
||||
row: 396
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 438
|
||||
column: 36
|
||||
end_location:
|
||||
row: 436
|
||||
row: 441
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 445
|
||||
row: 450
|
||||
column: 4
|
||||
end_location:
|
||||
row: 449
|
||||
row: 454
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 521
|
||||
row: 526
|
||||
column: 4
|
||||
end_location:
|
||||
row: 527
|
||||
row: 532
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 541
|
||||
row: 546
|
||||
column: 4
|
||||
end_location:
|
||||
row: 544
|
||||
row: 549
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 550
|
||||
row: 555
|
||||
column: 4
|
||||
end_location:
|
||||
row: 553
|
||||
row: 558
|
||||
column: 7
|
||||
fix: ~
|
||||
- kind: MultiLineSummarySecondLine
|
||||
location:
|
||||
row: 563
|
||||
row: 568
|
||||
column: 4
|
||||
end_location:
|
||||
row: 566
|
||||
row: 571
|
||||
column: 7
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 302
|
||||
column: 4
|
||||
end_location:
|
||||
row: 302
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 307
|
||||
@@ -24,7 +16,7 @@ expression: checks
|
||||
column: 4
|
||||
end_location:
|
||||
row: 312
|
||||
column: 15
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
@@ -36,10 +28,18 @@ expression: checks
|
||||
fix: ~
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 323
|
||||
row: 322
|
||||
column: 4
|
||||
end_location:
|
||||
row: 323
|
||||
row: 322
|
||||
column: 15
|
||||
fix: ~
|
||||
- kind: UsesTripleQuotes
|
||||
location:
|
||||
row: 328
|
||||
column: 4
|
||||
end_location:
|
||||
row: 328
|
||||
column: 16
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,60 +4,52 @@ expression: checks
|
||||
---
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 350
|
||||
row: 355
|
||||
column: 4
|
||||
end_location:
|
||||
row: 350
|
||||
row: 355
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 401
|
||||
row: 406
|
||||
column: 24
|
||||
end_location:
|
||||
row: 401
|
||||
row: 406
|
||||
column: 39
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 405
|
||||
row: 410
|
||||
column: 4
|
||||
end_location:
|
||||
row: 405
|
||||
row: 410
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 411
|
||||
row: 416
|
||||
column: 4
|
||||
end_location:
|
||||
row: 411
|
||||
row: 416
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 417
|
||||
row: 422
|
||||
column: 34
|
||||
end_location:
|
||||
row: 417
|
||||
row: 422
|
||||
column: 49
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 424
|
||||
row: 429
|
||||
column: 48
|
||||
end_location:
|
||||
row: 424
|
||||
row: 429
|
||||
column: 63
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 465
|
||||
column: 4
|
||||
end_location:
|
||||
row: 465
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 470
|
||||
@@ -76,34 +68,42 @@ expression: checks
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 482
|
||||
row: 480
|
||||
column: 4
|
||||
end_location:
|
||||
row: 482
|
||||
row: 480
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 504
|
||||
row: 487
|
||||
column: 4
|
||||
end_location:
|
||||
row: 504
|
||||
row: 487
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 509
|
||||
column: 4
|
||||
end_location:
|
||||
row: 509
|
||||
column: 34
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 509
|
||||
row: 514
|
||||
column: 4
|
||||
end_location:
|
||||
row: 509
|
||||
row: 514
|
||||
column: 33
|
||||
fix: ~
|
||||
- kind: EndsInPeriod
|
||||
location:
|
||||
row: 515
|
||||
row: 520
|
||||
column: 4
|
||||
end_location:
|
||||
row: 515
|
||||
row: 520
|
||||
column: 32
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ expression: checks
|
||||
---
|
||||
- kind: NoSignature
|
||||
location:
|
||||
row: 373
|
||||
row: 378
|
||||
column: 4
|
||||
end_location:
|
||||
row: 373
|
||||
row: 378
|
||||
column: 30
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,60 +4,52 @@ expression: checks
|
||||
---
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 350
|
||||
row: 355
|
||||
column: 4
|
||||
end_location:
|
||||
row: 350
|
||||
row: 355
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 401
|
||||
row: 406
|
||||
column: 24
|
||||
end_location:
|
||||
row: 401
|
||||
row: 406
|
||||
column: 39
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 405
|
||||
row: 410
|
||||
column: 4
|
||||
end_location:
|
||||
row: 405
|
||||
row: 410
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 411
|
||||
row: 416
|
||||
column: 4
|
||||
end_location:
|
||||
row: 411
|
||||
row: 416
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 417
|
||||
row: 422
|
||||
column: 34
|
||||
end_location:
|
||||
row: 417
|
||||
row: 422
|
||||
column: 49
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 424
|
||||
row: 429
|
||||
column: 48
|
||||
end_location:
|
||||
row: 424
|
||||
row: 429
|
||||
column: 63
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 465
|
||||
column: 4
|
||||
end_location:
|
||||
row: 465
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 470
|
||||
@@ -76,26 +68,34 @@ expression: checks
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 482
|
||||
row: 480
|
||||
column: 4
|
||||
end_location:
|
||||
row: 482
|
||||
row: 480
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 504
|
||||
row: 487
|
||||
column: 4
|
||||
end_location:
|
||||
row: 504
|
||||
row: 487
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 509
|
||||
column: 4
|
||||
end_location:
|
||||
row: 509
|
||||
column: 34
|
||||
fix: ~
|
||||
- kind: EndsInPunctuation
|
||||
location:
|
||||
row: 515
|
||||
row: 520
|
||||
column: 4
|
||||
end_location:
|
||||
row: 515
|
||||
row: 520
|
||||
column: 32
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,26 +4,26 @@ expression: checks
|
||||
---
|
||||
- kind: SkipDocstring
|
||||
location:
|
||||
row: 33
|
||||
row: 34
|
||||
column: 4
|
||||
end_location:
|
||||
row: 37
|
||||
row: 38
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: SkipDocstring
|
||||
location:
|
||||
row: 85
|
||||
row: 90
|
||||
column: 4
|
||||
end_location:
|
||||
row: 89
|
||||
row: 94
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: SkipDocstring
|
||||
location:
|
||||
row: 105
|
||||
row: 110
|
||||
column: 0
|
||||
end_location:
|
||||
row: 110
|
||||
row: 115
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -4,26 +4,26 @@ expression: checks
|
||||
---
|
||||
- kind: NonEmpty
|
||||
location:
|
||||
row: 19
|
||||
row: 20
|
||||
column: 8
|
||||
end_location:
|
||||
row: 19
|
||||
row: 20
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind: NonEmpty
|
||||
location:
|
||||
row: 69
|
||||
row: 74
|
||||
column: 4
|
||||
end_location:
|
||||
row: 69
|
||||
row: 74
|
||||
column: 11
|
||||
fix: ~
|
||||
- kind: NonEmpty
|
||||
location:
|
||||
row: 75
|
||||
row: 80
|
||||
column: 8
|
||||
end_location:
|
||||
row: 75
|
||||
row: 80
|
||||
column: 10
|
||||
fix: ~
|
||||
|
||||
|
||||
85
src/snapshots/ruff__linter__tests__ERA001_ERA001.py.snap
Normal file
85
src/snapshots/ruff__linter__tests__ERA001_ERA001.py.snap
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: CommentedOutCode
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 10
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 0
|
||||
- kind: CommentedOutCode
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 22
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 0
|
||||
- kind: CommentedOutCode
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 6
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 0
|
||||
- kind: CommentedOutCode
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 13
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
- kind: CommentedOutCode
|
||||
location:
|
||||
row: 12
|
||||
column: 4
|
||||
end_location:
|
||||
row: 12
|
||||
column: 16
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
column: 0
|
||||
|
||||
14
src/snapshots/ruff__linter__tests__F501_F50x.py.snap
Normal file
14
src/snapshots/ruff__linter__tests__F501_F50x.py.snap
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
PercentFormatInvalidFormat: incomplete format
|
||||
location:
|
||||
row: 1
|
||||
column: 9
|
||||
end_location:
|
||||
row: 1
|
||||
column: 25
|
||||
fix: ~
|
||||
|
||||
61
src/snapshots/ruff__linter__tests__F502_F502.py.snap
Normal file
61
src/snapshots/ruff__linter__tests__F502_F502.py.snap
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 6
|
||||
column: 10
|
||||
end_location:
|
||||
row: 6
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 7
|
||||
column: 10
|
||||
end_location:
|
||||
row: 7
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 8
|
||||
column: 10
|
||||
end_location:
|
||||
row: 8
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 9
|
||||
column: 10
|
||||
end_location:
|
||||
row: 9
|
||||
column: 22
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 11
|
||||
column: 10
|
||||
end_location:
|
||||
row: 11
|
||||
column: 37
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 12
|
||||
column: 10
|
||||
end_location:
|
||||
row: 12
|
||||
column: 37
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 13
|
||||
column: 10
|
||||
end_location:
|
||||
row: 13
|
||||
column: 37
|
||||
fix: ~
|
||||
|
||||
13
src/snapshots/ruff__linter__tests__F502_F50x.py.snap
Normal file
13
src/snapshots/ruff__linter__tests__F502_F50x.py.snap
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: PercentFormatExpectedMapping
|
||||
location:
|
||||
row: 9
|
||||
column: 10
|
||||
end_location:
|
||||
row: 9
|
||||
column: 21
|
||||
fix: ~
|
||||
|
||||
29
src/snapshots/ruff__linter__tests__F503_F503.py.snap
Normal file
29
src/snapshots/ruff__linter__tests__F503_F503.py.snap
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: PercentFormatExpectedSequence
|
||||
location:
|
||||
row: 17
|
||||
column: 8
|
||||
end_location:
|
||||
row: 17
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedSequence
|
||||
location:
|
||||
row: 18
|
||||
column: 8
|
||||
end_location:
|
||||
row: 18
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: PercentFormatExpectedSequence
|
||||
location:
|
||||
row: 23
|
||||
column: 8
|
||||
end_location:
|
||||
row: 23
|
||||
column: 42
|
||||
fix: ~
|
||||
|
||||
13
src/snapshots/ruff__linter__tests__F503_F50x.py.snap
Normal file
13
src/snapshots/ruff__linter__tests__F503_F50x.py.snap
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: PercentFormatExpectedSequence
|
||||
location:
|
||||
row: 10
|
||||
column: 8
|
||||
end_location:
|
||||
row: 10
|
||||
column: 20
|
||||
fix: ~
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__F504_F504.py.snap
Normal file
15
src/snapshots/ruff__linter__tests__F504_F504.py.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
PercentFormatExtraNamedArguments:
|
||||
- b
|
||||
location:
|
||||
row: 3
|
||||
column: 14
|
||||
end_location:
|
||||
row: 3
|
||||
column: 34
|
||||
fix: ~
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__F504_F50x.py.snap
Normal file
15
src/snapshots/ruff__linter__tests__F504_F50x.py.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
PercentFormatExtraNamedArguments:
|
||||
- baz
|
||||
location:
|
||||
row: 8
|
||||
column: 10
|
||||
end_location:
|
||||
row: 8
|
||||
column: 32
|
||||
fix: ~
|
||||
|
||||
6
src/snapshots/ruff__linter__tests__F505_F504.py.snap
Normal file
6
src/snapshots/ruff__linter__tests__F505_F504.py.snap
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
|
||||
15
src/snapshots/ruff__linter__tests__F505_F50x.py.snap
Normal file
15
src/snapshots/ruff__linter__tests__F505_F50x.py.snap
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
PercentFormatMissingArgument:
|
||||
- bar
|
||||
location:
|
||||
row: 7
|
||||
column: 10
|
||||
end_location:
|
||||
row: 7
|
||||
column: 14
|
||||
fix: ~
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user