Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63b3e00c97 | ||
|
|
39440aa274 | ||
|
|
add96d3dc5 | ||
|
|
6f8e0224d0 | ||
|
|
b8bbafd85b | ||
|
|
40b54d3e8c | ||
|
|
2b44941d63 | ||
|
|
257bd7f1d7 | ||
|
|
5728dceef0 | ||
|
|
6739602806 | ||
|
|
305326f7d7 | ||
|
|
69866f5461 | ||
|
|
41ca29c4f4 | ||
|
|
b35a804f9d | ||
|
|
e594ed6528 | ||
|
|
197645d90d | ||
|
|
26d3ff5a3a | ||
|
|
0dacf61153 | ||
|
|
a6251360b7 | ||
|
|
2965e2561d | ||
|
|
a19050b8a4 | ||
|
|
dfd6225d85 | ||
|
|
a0a6327fae | ||
|
|
db815a565f | ||
|
|
3bacdafd1c | ||
|
|
6403e3630d | ||
|
|
229eab6f42 | ||
|
|
e33582fb0e | ||
|
|
aaeab0ecf1 | ||
|
|
f9a16d9c44 | ||
|
|
2aa884eb9b | ||
|
|
84fa64d98c | ||
|
|
c1b1ac069e | ||
|
|
a710e35ebc | ||
|
|
49df43bb78 | ||
|
|
e338d9acbe | ||
|
|
5c8655f479 | ||
|
|
60987888a2 | ||
|
|
a81581c781 | ||
|
|
3152dd7a8e | ||
|
|
528416f07a | ||
|
|
4405a6a903 | ||
|
|
35fa2a3c32 | ||
|
|
bb67fbb73a |
5
.github/workflows/ruff.yaml
vendored
5
.github/workflows/ruff.yaml
vendored
@@ -1,9 +1,8 @@
|
||||
name: "[ruff] Release"
|
||||
|
||||
on:
|
||||
create:
|
||||
tags:
|
||||
- v*
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.167
|
||||
rev: v0.0.174
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.167-dev.0"
|
||||
version = "0.0.174-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1821,7 +1821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.167"
|
||||
version = "0.0.174"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1874,7 +1874,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.167"
|
||||
version = "0.0.174"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1892,7 +1892,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.161"
|
||||
version = "0.0.174"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.167"
|
||||
version = "0.0.174"
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -41,7 +41,7 @@ quick-junit = { version = "0.3.2" }
|
||||
rayon = { version = "1.5.3" }
|
||||
regex = { version = "1.6.0" }
|
||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||
ruff_macros = { version = "0.0.161", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.174", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
|
||||
|
||||
50
LICENSE
50
LICENSE
@@ -388,6 +388,56 @@ are:
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
- flake8-import-conventions, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 João Palmeiro
|
||||
|
||||
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-unused-arguments, licensed as follows:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Nathan Hoad
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
- isort, licensed as follows:
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
62
README.md
62
README.md
@@ -72,7 +72,7 @@ of [Conda](https://docs.conda.io/en/latest/):
|
||||
1. [Pyflakes (F)](#pyflakes-f)
|
||||
1. [pycodestyle (E, W)](#pycodestyle-e-w)
|
||||
1. [mccabe (C90)](#mccabe-c90)
|
||||
1. [isort (I00)](#isort-i00)
|
||||
1. [isort (I)](#isort-i)
|
||||
1. [pydocstyle (D)](#pydocstyle-d)
|
||||
1. [pyupgrade (UP)](#pyupgrade-up)
|
||||
1. [pep8-naming (N)](#pep8-naming-n)
|
||||
@@ -89,7 +89,8 @@ of [Conda](https://docs.conda.io/en/latest/):
|
||||
1. [flake8-print (T20)](#flake8-print-t20)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes-q)
|
||||
1. [flake8-return (RET)](#flake8-return-ret)
|
||||
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports-i25)
|
||||
1. [flake8-tidy-imports (TID)](#flake8-tidy-imports-tid)
|
||||
1. [flake8-unused-arguments (ARG)](#flake8-unused-arguments-arg)
|
||||
1. [eradicate (ERA)](#eradicate-era)
|
||||
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
|
||||
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
|
||||
@@ -119,12 +120,18 @@ For **macOS Homebrew** and **Linuxbrew** users, Ruff is also available as [`ruff
|
||||
brew install ruff
|
||||
```
|
||||
|
||||
For Conda users, Ruff is also available as [`ruff`](https://anaconda.org/conda-forge/ruff) on `conda-forge`:
|
||||
For **Conda** users, Ruff is also available as [`ruff`](https://anaconda.org/conda-forge/ruff) on `conda-forge`:
|
||||
|
||||
```shell
|
||||
conda install -c conda-forge ruff
|
||||
```
|
||||
|
||||
For **Arch Linux** users, Ruff is also available as [`ruff`](https://archlinux.org/packages/community/x86_64/ruff/) on the official repositories:
|
||||
|
||||
```shell
|
||||
pacman -S ruff
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
To run Ruff, try any of the following:
|
||||
@@ -146,7 +153,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.167
|
||||
rev: v0.0.174
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -437,11 +444,13 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
|
||||
| F706 | ReturnOutsideFunction | `return` statement outside of a function/method | |
|
||||
| F707 | DefaultExceptNotLast | An `except` block as not the last exception handler | |
|
||||
| F722 | ForwardAnnotationSyntaxError | Syntax error in forward annotation: `...` | |
|
||||
| F811 | RedefinedWhileUnused | Redefinition of unused `...` from line 1 | |
|
||||
| F821 | UndefinedName | Undefined name `...` | |
|
||||
| F822 | UndefinedExport | Undefined name `...` in `__all__` | |
|
||||
| F823 | UndefinedLocal | Local variable `...` referenced before assignment | |
|
||||
| F831 | DuplicateArgumentName | Duplicate argument name in function definition | |
|
||||
| F841 | UnusedVariable | Local variable `...` is assigned to but never used | |
|
||||
| F842 | UnusedAnnotation | Local variable `...` is annotated but never used | |
|
||||
| F901 | RaiseNotImplemented | `raise NotImplemented` should be `raise NotImplementedError` | 🛠 |
|
||||
|
||||
### pycodestyle (E, W)
|
||||
@@ -475,7 +484,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| C901 | FunctionIsTooComplex | `...` is too complex (10) | |
|
||||
|
||||
### isort (I00)
|
||||
### isort (I)
|
||||
|
||||
For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
|
||||
|
||||
@@ -514,6 +523,7 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
|
||||
| D214 | SectionNotOverIndented | Section is over-indented ("Returns") | 🛠 |
|
||||
| D215 | SectionUnderlineNotOverIndented | Section underline is over-indented ("Returns") | 🛠 |
|
||||
| D300 | UsesTripleQuotes | Use """triple double quotes""" | |
|
||||
| D301 | UsesRPrefixForBackslashedContent | Use r""" if any backslashes in a docstring | |
|
||||
| D400 | EndsInPeriod | First line should end with a period | 🛠 |
|
||||
| D402 | NoSignature | First line should not be the function's signature | |
|
||||
| D403 | FirstLineCapitalized | First word of the first line should be properly capitalized | |
|
||||
@@ -760,13 +770,25 @@ For more, see [flake8-return](https://pypi.org/project/flake8-return/1.2.0/) on
|
||||
| RET507 | SuperfluousElseContinue | Unnecessary `else` after `continue` statement | |
|
||||
| RET508 | SuperfluousElseBreak | Unnecessary `else` after `break` statement | |
|
||||
|
||||
### flake8-tidy-imports (I25)
|
||||
### flake8-tidy-imports (TID)
|
||||
|
||||
For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/4.8.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| I252 | BannedRelativeImport | Relative imports are banned | |
|
||||
| TID252 | BannedRelativeImport | Relative imports are banned | |
|
||||
|
||||
### flake8-unused-arguments (ARG)
|
||||
|
||||
For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-arguments/0.0.12/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ARG001 | UnusedFunctionArgument | Unused function argument: `...` | |
|
||||
| ARG002 | UnusedMethodArgument | Unused method argument: `...` | |
|
||||
| ARG003 | UnusedClassMethodArgument | Unused class method argument: `...` | |
|
||||
| ARG004 | UnusedStaticMethodArgument | Unused static method argument: `...` | |
|
||||
| ARG005 | UnusedLambdaArgument | Unused lambda argument: `...` | |
|
||||
|
||||
### eradicate (ERA)
|
||||
|
||||
@@ -793,11 +815,12 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
|
||||
| PLC0414 | UselessImportAlias | Import alias does not rename original package | 🛠 |
|
||||
| PLC2201 | MisplacedComparisonConstant | Comparison should be ... | 🛠 |
|
||||
| PLC3002 | UnnecessaryDirectLambdaCall | Lambda expression called directly. Execute the expression inline instead. | |
|
||||
| PLE0118 | UsedPriorGlobalDeclaration | Name `...` is used prior to global declaration on line 1 | |
|
||||
| PLE1142 | AwaitOutsideAsync | `await` should be used within an async function | |
|
||||
| PLR0206 | PropertyWithParameters | Cannot have defined parameters for properties | |
|
||||
| PLR0402 | ConsiderUsingFromImport | Consider using `from ... import ...` | |
|
||||
| PLR1701 | ConsiderMergingIsinstance | Consider merging these isinstance calls: `isinstance(..., (...))` | |
|
||||
| PLR1722 | ConsiderUsingSysExit | Consider using `sys.exit()` | 🛠 |
|
||||
| PLR0402 | ConsiderUsingFromImport | Use `from ... import ...` in lieu of alias | |
|
||||
| PLR1701 | ConsiderMergingIsinstance | Merge these isinstance calls: `isinstance(..., (...))` | |
|
||||
| PLR1722 | UseSysExit | Use `sys.exit()` instead of `exit` | 🛠 |
|
||||
| PLW0120 | UselessElseOnLoop | Else clause on loop without a break statement, remove the else and de-indent all the code inside it | |
|
||||
|
||||
### Ruff-specific rules (RUF)
|
||||
@@ -935,7 +958,7 @@ automatically convert your existing configuration.)
|
||||
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 implements every rule in Flake8, with the exception of `F811`.
|
||||
Under those conditions, Ruff implements every rule in Flake8.
|
||||
|
||||
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
|
||||
natively, including:
|
||||
@@ -967,6 +990,12 @@ natively, including:
|
||||
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (1/10)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
Note that, in some cases, Ruff uses different error code prefixes than would be found in the
|
||||
originating Flake8 plugins. For example, Ruff uses `TID252` to represent the `I252` rule from
|
||||
`flake8-tidy-imports`. This helps minimize conflicts across plugins and allows any individual plugin
|
||||
to be toggled on or off with a single (e.g.) `--select TID`, as opposed to `--select I2` (to avoid
|
||||
conflicts with the `isort` rules, like `I001`).
|
||||
|
||||
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
|
||||
@@ -1465,7 +1494,7 @@ fix = true
|
||||
|
||||
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"]`
|
||||
**Default value**: `["A", "ANN", "ARG", "B", "BLE", "C", "D", "E", "ERA", "F", "FBT", "I", "ICN", "N", "PGH", "PLC", "PLE", "PLR", "PLW", "Q", "RET", "RUF", "S", "T", "TID", "UP", "W", "YTT"]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
@@ -1782,7 +1811,7 @@ The conventional aliases for imports. These aliases can be extended by the `exte
|
||||
|
||||
**Default value**: `{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}`
|
||||
|
||||
**Type**: `BTreeMap<String, String>`
|
||||
**Type**: `FxHashMap<String, String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -1804,7 +1833,7 @@ A mapping of modules to their conventional import aliases. These aliases will be
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
**Type**: `BTreeMap<String, String>`
|
||||
**Type**: `FxHashMap<String, String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -1961,6 +1990,10 @@ from .utils import (
|
||||
)
|
||||
```
|
||||
|
||||
Note that this setting is only effective when combined with `combine-as-imports = true`.
|
||||
When `combine-as-imports` isn't enabled, every aliased `import from` will be given its
|
||||
own line, in which case, wrapping is not necessary.
|
||||
|
||||
**Default value**: `false`
|
||||
|
||||
**Type**: `bool`
|
||||
@@ -1970,6 +2003,7 @@ from .utils import (
|
||||
```toml
|
||||
[tool.ruff.isort]
|
||||
force-wrap-aliases = true
|
||||
combine-as-imports = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
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.167"
|
||||
version = "0.0.174"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.167"
|
||||
version = "0.0.174"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.167-dev.0"
|
||||
version = "0.0.174-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -512,6 +512,7 @@ mod tests {
|
||||
CheckCodePrefix::D214,
|
||||
CheckCodePrefix::D215,
|
||||
CheckCodePrefix::D300,
|
||||
CheckCodePrefix::D301,
|
||||
CheckCodePrefix::D400,
|
||||
CheckCodePrefix::D403,
|
||||
CheckCodePrefix::D404,
|
||||
|
||||
@@ -135,7 +135,6 @@ fn tokenize_files_to_codes_mapping(value: &str) -> Vec<Token> {
|
||||
}
|
||||
|
||||
/// Parse a 'files-to-codes' mapping, mimicking Flake8's internal logic.
|
||||
///
|
||||
/// See: <https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45>
|
||||
pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair>> {
|
||||
if value.trim().is_empty() {
|
||||
|
||||
@@ -162,6 +162,7 @@ impl DocstringConvention {
|
||||
// CheckCodePrefix::D214,
|
||||
// CheckCodePrefix::D215,
|
||||
CheckCodePrefix::D300,
|
||||
CheckCodePrefix::D301,
|
||||
CheckCodePrefix::D400,
|
||||
CheckCodePrefix::D402,
|
||||
CheckCodePrefix::D403,
|
||||
@@ -209,6 +210,7 @@ impl DocstringConvention {
|
||||
CheckCodePrefix::D214,
|
||||
CheckCodePrefix::D215,
|
||||
CheckCodePrefix::D300,
|
||||
CheckCodePrefix::D301,
|
||||
CheckCodePrefix::D400,
|
||||
// CheckCodePrefix::D402,
|
||||
CheckCodePrefix::D403,
|
||||
@@ -257,6 +259,7 @@ impl DocstringConvention {
|
||||
CheckCodePrefix::D214,
|
||||
// CheckCodePrefix::D215,
|
||||
CheckCodePrefix::D300,
|
||||
CheckCodePrefix::D301,
|
||||
// CheckCodePrefix::D400,
|
||||
CheckCodePrefix::D402,
|
||||
CheckCodePrefix::D403,
|
||||
|
||||
4
resources/test/fixtures/eradicate/ERA001.py
vendored
4
resources/test/fixtures/eradicate/ERA001.py
vendored
@@ -5,9 +5,11 @@ a = 4
|
||||
#foo(1, 2, 3)
|
||||
|
||||
def foo(x, y, z):
|
||||
contentet = 1 # print('hello')
|
||||
content = 1 # print('hello')
|
||||
print(x, y, z)
|
||||
|
||||
# This is a real comment.
|
||||
#return True
|
||||
return False
|
||||
|
||||
#import os # noqa: ERA001
|
||||
|
||||
@@ -130,6 +130,12 @@ def x():
|
||||
return val
|
||||
|
||||
|
||||
def x():
|
||||
a = 1
|
||||
print(f"a={a}")
|
||||
return a
|
||||
|
||||
|
||||
# Test cases for using value for assignment then returning it
|
||||
# See:https://github.com/afonasev/flake8-return/issues/47
|
||||
def resolve_from_url(self, url: str) -> dict:
|
||||
|
||||
137
resources/test/fixtures/flake8_unused_arguments/ARG.py
vendored
Normal file
137
resources/test/fixtures/flake8_unused_arguments/ARG.py
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
from abc import abstractmethod
|
||||
from typing_extensions import override
|
||||
|
||||
|
||||
###
|
||||
# Unused arguments on functions.
|
||||
###
|
||||
def f(self, x):
|
||||
print("Hello, world!")
|
||||
|
||||
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
|
||||
def f(self, x):
|
||||
...
|
||||
|
||||
|
||||
def f(cls, x):
|
||||
...
|
||||
|
||||
|
||||
###
|
||||
# Unused arguments on lambdas.
|
||||
###
|
||||
lambda x: print("Hello, world!")
|
||||
|
||||
|
||||
class X:
|
||||
###
|
||||
# Unused arguments.
|
||||
###
|
||||
def f(self, x):
|
||||
print("Hello, world!")
|
||||
|
||||
def f(self, /, x):
|
||||
print("Hello, world!")
|
||||
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@classmethod
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@staticmethod
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@staticmethod
|
||||
def f(x):
|
||||
print("Hello, world!")
|
||||
|
||||
###
|
||||
# Unused arguments attached to empty functions (OK).
|
||||
###
|
||||
def f(self, x):
|
||||
...
|
||||
|
||||
def f(self, /, x):
|
||||
...
|
||||
|
||||
def f(cls, x):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def f(cls, x):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def f(cls, x):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def f(x):
|
||||
...
|
||||
|
||||
###
|
||||
# Unused functions attached to abstract methods (OK).
|
||||
###
|
||||
@abstractmethod
|
||||
def f(self, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@abstractmethod
|
||||
def f(self, /, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@abstractmethod
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def f(x):
|
||||
print("Hello, world!")
|
||||
|
||||
###
|
||||
# Unused functions attached to overrides (OK).
|
||||
###
|
||||
@override
|
||||
def f(self, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@override
|
||||
def f(self, /, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@override
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@classmethod
|
||||
@override
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def f(cls, x):
|
||||
print("Hello, world!")
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def f(x):
|
||||
print("Hello, world!")
|
||||
41
resources/test/fixtures/isort/insert_empty_lines.pyi
vendored
Normal file
41
resources/test/fixtures/isort/insert_empty_lines.pyi
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import a
|
||||
import b
|
||||
x = 1
|
||||
import os
|
||||
import sys
|
||||
def f():
|
||||
pass
|
||||
if True:
|
||||
x = 1
|
||||
import collections
|
||||
import typing
|
||||
class X: pass
|
||||
y = 1
|
||||
import os
|
||||
import sys
|
||||
"""Docstring"""
|
||||
|
||||
if True:
|
||||
import os
|
||||
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
|
||||
if True:
|
||||
import os
|
||||
def f():
|
||||
pass
|
||||
|
||||
if True:
|
||||
x = 1
|
||||
import collections
|
||||
import typing
|
||||
class X: pass
|
||||
|
||||
if True:
|
||||
x = 1
|
||||
import collections
|
||||
import typing
|
||||
def f(): pass
|
||||
11
resources/test/fixtures/pyflakes/F811_0.py
vendored
Normal file
11
resources/test/fixtures/pyflakes/F811_0.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
def foo(x):
|
||||
return x
|
||||
|
||||
|
||||
@foo
|
||||
def bar():
|
||||
pass
|
||||
|
||||
|
||||
def bar():
|
||||
pass
|
||||
1
resources/test/fixtures/pyflakes/F811_1.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_1.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import fu as FU, bar as FU
|
||||
8
resources/test/fixtures/pyflakes/F811_10.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_10.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Test that importing a module twice using a nested does not issue a warning."""
|
||||
try:
|
||||
if True:
|
||||
if True:
|
||||
import os
|
||||
except:
|
||||
import os
|
||||
os.path
|
||||
9
resources/test/fixtures/pyflakes/F811_11.py
vendored
Normal file
9
resources/test/fixtures/pyflakes/F811_11.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
try:
|
||||
from aa import mixer
|
||||
except AttributeError:
|
||||
from bb import mixer
|
||||
except RuntimeError:
|
||||
from cc import mixer
|
||||
except:
|
||||
from dd import mixer
|
||||
mixer(123)
|
||||
7
resources/test/fixtures/pyflakes/F811_12.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_12.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
try:
|
||||
from aa import mixer
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
from bb import mixer
|
||||
mixer(123)
|
||||
8
resources/test/fixtures/pyflakes/F811_13.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_13.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
try:
|
||||
import funca
|
||||
except ImportError:
|
||||
from bb import funca
|
||||
from bb import funcb
|
||||
else:
|
||||
from bbb import funcb
|
||||
print(funca, funcb)
|
||||
10
resources/test/fixtures/pyflakes/F811_14.py
vendored
Normal file
10
resources/test/fixtures/pyflakes/F811_14.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
try:
|
||||
import b
|
||||
except ImportError:
|
||||
b = Ellipsis
|
||||
from bb import a
|
||||
else:
|
||||
from aa import a
|
||||
finally:
|
||||
a = 42
|
||||
print(a, b)
|
||||
5
resources/test/fixtures/pyflakes/F811_15.py
vendored
Normal file
5
resources/test/fixtures/pyflakes/F811_15.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import fu
|
||||
|
||||
|
||||
def fu():
|
||||
pass
|
||||
9
resources/test/fixtures/pyflakes/F811_16.py
vendored
Normal file
9
resources/test/fixtures/pyflakes/F811_16.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
"""Test that shadowing a global with a nested function generates a warning."""
|
||||
|
||||
import fu
|
||||
|
||||
|
||||
def bar():
|
||||
def baz():
|
||||
def fu():
|
||||
pass
|
||||
10
resources/test/fixtures/pyflakes/F811_17.py
vendored
Normal file
10
resources/test/fixtures/pyflakes/F811_17.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
"""Test that shadowing a global name with a nested function generates a warning."""
|
||||
import fu
|
||||
|
||||
|
||||
def bar():
|
||||
import fu
|
||||
|
||||
def baz():
|
||||
def fu():
|
||||
pass
|
||||
14
resources/test/fixtures/pyflakes/F811_18.py
vendored
Normal file
14
resources/test/fixtures/pyflakes/F811_18.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Test that a global import which is redefined locally, but used later in another scope does not generate a warning."""
|
||||
|
||||
import unittest, transport
|
||||
|
||||
|
||||
class GetTransportTestCase(unittest.TestCase):
|
||||
def test_get_transport(self):
|
||||
transport = 'transport'
|
||||
self.assertIsNotNone(transport)
|
||||
|
||||
|
||||
class TestTransportMethodArgs(unittest.TestCase):
|
||||
def test_send_defaults(self):
|
||||
transport.Transport()
|
||||
7
resources/test/fixtures/pyflakes/F811_19.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_19.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
"""If an imported name is redefined by a class statement which also uses that name in the bases list, no warning is emitted."""
|
||||
|
||||
from fu import bar
|
||||
|
||||
|
||||
class bar(bar):
|
||||
pass
|
||||
1
resources/test/fixtures/pyflakes/F811_2.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_2.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
from moo import fu as FU, bar as FU
|
||||
14
resources/test/fixtures/pyflakes/F811_20.py
vendored
Normal file
14
resources/test/fixtures/pyflakes/F811_20.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
Test that shadowing a global with a class attribute does not produce a
|
||||
warning.
|
||||
"""
|
||||
|
||||
import fu
|
||||
|
||||
|
||||
class bar:
|
||||
# STOPSHIP: This errors.
|
||||
fu = 1
|
||||
|
||||
|
||||
print(fu)
|
||||
1
resources/test/fixtures/pyflakes/F811_3.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_3.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import fu; fu = 3
|
||||
1
resources/test/fixtures/pyflakes/F811_4.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_4.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import fu; fu, bar = 3
|
||||
1
resources/test/fixtures/pyflakes/F811_5.py
vendored
Normal file
1
resources/test/fixtures/pyflakes/F811_5.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
import fu; [fu, bar] = 3
|
||||
7
resources/test/fixtures/pyflakes/F811_6.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_6.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
"""Test that importing a module twice within an if block does raise a warning."""
|
||||
|
||||
i = 2
|
||||
if i == 1:
|
||||
import os
|
||||
import os
|
||||
os.path
|
||||
8
resources/test/fixtures/pyflakes/F811_7.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_7.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Test that importing a module twice in if-else blocks does not raise a warning."""
|
||||
|
||||
i = 2
|
||||
if i == 1:
|
||||
import os
|
||||
else:
|
||||
import os
|
||||
os.path
|
||||
8
resources/test/fixtures/pyflakes/F811_8.py
vendored
Normal file
8
resources/test/fixtures/pyflakes/F811_8.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Test that importing a module twice in a try block does raise a warning."""
|
||||
|
||||
try:
|
||||
import os
|
||||
import os
|
||||
except:
|
||||
pass
|
||||
os.path
|
||||
7
resources/test/fixtures/pyflakes/F811_9.py
vendored
Normal file
7
resources/test/fixtures/pyflakes/F811_9.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
"""Test that importing a module twice in a try block does not raise a warning."""
|
||||
|
||||
try:
|
||||
import os
|
||||
except:
|
||||
import os
|
||||
os.path
|
||||
13
resources/test/fixtures/pyflakes/F842.py
vendored
Normal file
13
resources/test/fixtures/pyflakes/F842.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
def f():
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
class A:
|
||||
name: str
|
||||
age: int
|
||||
|
||||
|
||||
class B:
|
||||
name: str = "Bob"
|
||||
age: int = 18
|
||||
32
resources/test/fixtures/pylint/global_variable_not_assigned.py
vendored
Normal file
32
resources/test/fixtures/pylint/global_variable_not_assigned.py
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
###
|
||||
# Errors.
|
||||
###
|
||||
def f():
|
||||
global x
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
print(x)
|
||||
|
||||
|
||||
###
|
||||
# Non-errors.
|
||||
###
|
||||
def f():
|
||||
global x
|
||||
|
||||
x = 1
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
(x, y) = (1, 2)
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
del x
|
||||
19
resources/test/fixtures/pylint/nonlocal_without_binding.py
vendored
Normal file
19
resources/test/fixtures/pylint/nonlocal_without_binding.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
nonlocal x
|
||||
|
||||
|
||||
def f():
|
||||
nonlocal x
|
||||
|
||||
|
||||
def f():
|
||||
nonlocal y
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
def f():
|
||||
nonlocal x
|
||||
|
||||
def f():
|
||||
nonlocal y
|
||||
148
resources/test/fixtures/pylint/used_prior_global_declaration.py
vendored
Normal file
148
resources/test/fixtures/pylint/used_prior_global_declaration.py
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
###
|
||||
# Errors.
|
||||
###
|
||||
def f():
|
||||
print(x)
|
||||
|
||||
global x
|
||||
|
||||
print(x)
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
print(x)
|
||||
|
||||
global x
|
||||
|
||||
print(x)
|
||||
|
||||
|
||||
def f():
|
||||
print(x)
|
||||
|
||||
global x, y
|
||||
|
||||
print(x)
|
||||
|
||||
|
||||
def f():
|
||||
global x, y
|
||||
|
||||
print(x)
|
||||
|
||||
global x, y
|
||||
|
||||
print(x)
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
global x
|
||||
|
||||
x = 1
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
x = 1
|
||||
|
||||
global x
|
||||
|
||||
x = 1
|
||||
|
||||
|
||||
def f():
|
||||
del x
|
||||
|
||||
global x, y
|
||||
|
||||
del x
|
||||
|
||||
|
||||
def f():
|
||||
global x, y
|
||||
|
||||
del x
|
||||
|
||||
global x, y
|
||||
|
||||
del x
|
||||
|
||||
|
||||
def f():
|
||||
del x
|
||||
|
||||
global x
|
||||
|
||||
del x
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
del x
|
||||
|
||||
global x
|
||||
|
||||
del x
|
||||
|
||||
|
||||
def f():
|
||||
del x
|
||||
|
||||
global x, y
|
||||
|
||||
del x
|
||||
|
||||
|
||||
def f():
|
||||
global x, y
|
||||
|
||||
del x
|
||||
|
||||
global x, y
|
||||
|
||||
del x
|
||||
|
||||
|
||||
###
|
||||
# Non-errors.
|
||||
###
|
||||
def f():
|
||||
global x
|
||||
|
||||
print(x)
|
||||
|
||||
|
||||
def f():
|
||||
global x, y
|
||||
|
||||
print(x)
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
x = 1
|
||||
|
||||
|
||||
def f():
|
||||
global x, y
|
||||
|
||||
x = 1
|
||||
|
||||
|
||||
def f():
|
||||
global x
|
||||
|
||||
del x
|
||||
|
||||
|
||||
def f():
|
||||
global x, y
|
||||
|
||||
del x
|
||||
@@ -75,7 +75,7 @@ def test_break_in_orelse_deep():
|
||||
|
||||
|
||||
def test_break_in_orelse_deep2():
|
||||
"""should rise a useless-else-on-loop message, as the break statement is only
|
||||
"""should raise a useless-else-on-loop message, as the break statement is only
|
||||
for the inner for loop
|
||||
"""
|
||||
for _ in range(10):
|
||||
@@ -101,3 +101,15 @@ def test_break_in_orelse_deep3():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def test_break_in_if_orelse():
|
||||
"""should raise a useless-else-on-loop message due to break in else"""
|
||||
for _ in range(10):
|
||||
if 1 < 2: # pylint: disable=comparison-of-constants
|
||||
pass
|
||||
else:
|
||||
break
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.167"
|
||||
version = "0.0.174"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -8,7 +8,7 @@ use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use codegen::{Scope, Type, Variant};
|
||||
use itertools::Itertools;
|
||||
use ruff::checks::{CheckCode, REDIRECTS};
|
||||
use ruff::checks::{CheckCode, CODE_REDIRECTS, PREFIX_REDIRECTS};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
const FILE: &str = "src/checks_gen.rs";
|
||||
@@ -39,34 +39,26 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Add any aliases (e.g., "U001" to "UP001").
|
||||
for (alias, check_code) in REDIRECTS.iter() {
|
||||
// Compute the length of the prefix and suffix for both codes.
|
||||
let code_str: String = check_code.as_ref().to_string();
|
||||
let code_prefix_len = code_str
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
.count();
|
||||
let code_suffix_len = code_str.len() - code_prefix_len;
|
||||
let alias_prefix_len = alias
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
.count();
|
||||
let alias_suffix_len = alias.len() - alias_prefix_len;
|
||||
assert_eq!(code_suffix_len, alias_suffix_len);
|
||||
for i in 0..=code_suffix_len {
|
||||
let source = code_str[..code_prefix_len + i].to_string();
|
||||
let destination = alias[..alias_prefix_len + i].to_string();
|
||||
if source != destination {
|
||||
prefix_to_codes.insert(
|
||||
destination,
|
||||
prefix_to_codes
|
||||
.get(&source)
|
||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {source:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Add any prefix aliases (e.g., "U" to "UP").
|
||||
for (alias, source) in PREFIX_REDIRECTS.iter() {
|
||||
prefix_to_codes.insert(
|
||||
(*alias).to_string(),
|
||||
prefix_to_codes
|
||||
.get(&(*source).to_string())
|
||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {source:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
// Add any check code aliases (e.g., "U001" to "UP001").
|
||||
for (alias, check_code) in CODE_REDIRECTS.iter() {
|
||||
prefix_to_codes.insert(
|
||||
(*alias).to_string(),
|
||||
prefix_to_codes
|
||||
.get(&check_code.as_ref().to_string())
|
||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {alias:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut scope = Scope::new();
|
||||
@@ -113,10 +105,10 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.line("#[allow(clippy::match_same_arms)]")
|
||||
.line("match self {");
|
||||
for (prefix, codes) in &prefix_to_codes {
|
||||
if let Some(target) = REDIRECTS.get(&prefix.as_str()) {
|
||||
if let Some(target) = CODE_REDIRECTS.get(&prefix.as_str()) {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
|
||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been renamed to \
|
||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
|
||||
`{}`\".bold()); \n vec![{}] }}",
|
||||
prefix,
|
||||
target.as_ref(),
|
||||
@@ -125,6 +117,18 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
||||
.join(", ")
|
||||
));
|
||||
} else if let Some(target) = PREFIX_REDIRECTS.get(&prefix.as_str()) {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
|
||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
|
||||
`{}`\".bold()); \n vec![{}] }}",
|
||||
prefix,
|
||||
target,
|
||||
codes
|
||||
.iter()
|
||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
||||
.join(", ")
|
||||
));
|
||||
} else {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => vec![{}],",
|
||||
@@ -187,7 +191,9 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
output.push_str("pub const CATEGORIES: &[CheckCodePrefix] = &[");
|
||||
output.push('\n');
|
||||
for prefix in prefix_to_codes.keys() {
|
||||
if prefix.chars().all(char::is_alphabetic) {
|
||||
if prefix.chars().all(char::is_alphabetic)
|
||||
&& !PREFIX_REDIRECTS.contains_key(&prefix.as_str())
|
||||
{
|
||||
output.push_str(&format!("CheckCodePrefix::{prefix},"));
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.161"
|
||||
version = "0.0.174"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
105
src/ast/branch_detection.rs
Normal file
105
src/ast/branch_detection.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::ExcepthandlerKind::ExceptHandler;
|
||||
use rustpython_ast::Stmt;
|
||||
use rustpython_parser::ast::StmtKind;
|
||||
|
||||
use crate::ast::types::RefEquality;
|
||||
|
||||
/// Return the common ancestor of `left` and `right` below `stop`, or `None`.
|
||||
fn common_ancestor<'a>(
|
||||
left: &'a RefEquality<'a, Stmt>,
|
||||
right: &'a RefEquality<'a, Stmt>,
|
||||
stop: Option<&'a RefEquality<'a, Stmt>>,
|
||||
depths: &'a FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
child_to_parent: &'a FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
) -> Option<&'a RefEquality<'a, Stmt>> {
|
||||
if let Some(stop) = stop {
|
||||
if left == stop || right == stop {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if left == right {
|
||||
return Some(left);
|
||||
}
|
||||
|
||||
let left_depth = depths.get(left)?;
|
||||
let right_depth = depths.get(right)?;
|
||||
match left_depth.cmp(right_depth) {
|
||||
Ordering::Less => common_ancestor(
|
||||
left,
|
||||
child_to_parent.get(right)?,
|
||||
stop,
|
||||
depths,
|
||||
child_to_parent,
|
||||
),
|
||||
Ordering::Equal => common_ancestor(
|
||||
child_to_parent.get(left)?,
|
||||
child_to_parent.get(right)?,
|
||||
stop,
|
||||
depths,
|
||||
child_to_parent,
|
||||
),
|
||||
Ordering::Greater => common_ancestor(
|
||||
child_to_parent.get(left)?,
|
||||
right,
|
||||
stop,
|
||||
depths,
|
||||
child_to_parent,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the alternative branches for a given node.
|
||||
fn alternatives<'a>(node: &'a RefEquality<'a, Stmt>) -> Vec<Vec<RefEquality<'a, Stmt>>> {
|
||||
match &node.0.node {
|
||||
StmtKind::If { body, .. } => vec![body.iter().map(RefEquality).collect()],
|
||||
StmtKind::Try {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
..
|
||||
} => vec![body.iter().chain(orelse.iter()).map(RefEquality).collect()]
|
||||
.into_iter()
|
||||
.chain(handlers.iter().map(|handler| {
|
||||
let ExceptHandler { body, .. } = &handler.node;
|
||||
body.iter().map(RefEquality).collect()
|
||||
}))
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if `node` is a descendent of any of the nodes in `ancestors`.
|
||||
fn descendant_of<'a>(
|
||||
node: &RefEquality<'a, Stmt>,
|
||||
ancestors: &[RefEquality<'a, Stmt>],
|
||||
stop: &RefEquality<'a, Stmt>,
|
||||
depths: &FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
child_to_parent: &FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
) -> bool {
|
||||
ancestors.iter().any(|ancestor| {
|
||||
common_ancestor(node, ancestor, Some(stop), depths, child_to_parent).is_some()
|
||||
})
|
||||
}
|
||||
|
||||
/// Return `true` if `left` and `right` are on different branches of an `if` or
|
||||
/// `try` statement.
|
||||
pub fn different_forks<'a>(
|
||||
left: &RefEquality<'a, Stmt>,
|
||||
right: &RefEquality<'a, Stmt>,
|
||||
depths: &FxHashMap<RefEquality<'a, Stmt>, usize>,
|
||||
child_to_parent: &FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
|
||||
) -> bool {
|
||||
if let Some(ancestor) = common_ancestor(left, right, None, depths, child_to_parent) {
|
||||
for items in alternatives(ancestor) {
|
||||
let l = descendant_of(left, &items, ancestor, depths, child_to_parent);
|
||||
let r = descendant_of(right, &items, ancestor, depths, child_to_parent);
|
||||
if l ^ r {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
9
src/ast/cast.rs
Normal file
9
src/ast/cast.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||
|
||||
pub fn decorator_list(stmt: &Stmt) -> &Vec<Expr> {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { decorator_list, .. }
|
||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list,
|
||||
_ => panic!("Expected StmtKind::FunctionDef | StmtKind::AsyncFunctionDef"),
|
||||
}
|
||||
}
|
||||
65
src/ast/function_type.rs
Normal file
65
src/ast/function_type.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::helpers::{
|
||||
collect_call_paths, dealias_call_path, match_call_path, to_module_and_member,
|
||||
};
|
||||
use crate::ast::types::{Scope, ScopeKind};
|
||||
|
||||
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
|
||||
|
||||
pub enum FunctionType {
|
||||
Function,
|
||||
Method,
|
||||
ClassMethod,
|
||||
StaticMethod,
|
||||
}
|
||||
|
||||
/// Classify a function based on its scope, name, and decorators.
|
||||
pub fn classify(
|
||||
scope: &Scope,
|
||||
name: &str,
|
||||
decorator_list: &[Expr],
|
||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||
import_aliases: &FxHashMap<&str, &str>,
|
||||
classmethod_decorators: &[String],
|
||||
staticmethod_decorators: &[String],
|
||||
) -> FunctionType {
|
||||
let ScopeKind::Class(scope) = &scope.kind else {
|
||||
return FunctionType::Function;
|
||||
};
|
||||
// Special-case class method, like `__new__`.
|
||||
if CLASS_METHODS.contains(&name)
|
||||
|| scope.bases.iter().any(|expr| {
|
||||
// The class itself extends a known metaclass, so all methods are class methods.
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
METACLASS_BASES
|
||||
.iter()
|
||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||
})
|
||||
|| decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a class method decorator (like `@classmethod`).
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
classmethod_decorators.iter().any(|decorator| {
|
||||
let (module, member) = to_module_and_member(decorator);
|
||||
match_call_path(&call_path, module, member, from_imports)
|
||||
})
|
||||
})
|
||||
{
|
||||
FunctionType::ClassMethod
|
||||
} else if decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a static method decorator (like
|
||||
// `@staticmethod`).
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
staticmethod_decorators.iter().any(|decorator| {
|
||||
let (module, member) = to_module_and_member(decorator);
|
||||
match_call_path(&call_path, module, member, from_imports)
|
||||
})
|
||||
}) {
|
||||
FunctionType::StaticMethod
|
||||
} else {
|
||||
// It's an instance method.
|
||||
FunctionType::Method
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
pub mod branch_detection;
|
||||
pub mod cast;
|
||||
pub mod function_type;
|
||||
pub mod helpers;
|
||||
pub mod operations;
|
||||
pub mod relocate;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::{Cmpop, Located};
|
||||
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
|
||||
use crate::ast::types::{BindingKind, Scope};
|
||||
use crate::ast::types::{Binding, BindingKind, Scope};
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
|
||||
/// Extract the names bound to a given __all__ assignment.
|
||||
pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
pub fn extract_all_names(stmt: &Stmt, scope: &Scope, bindings: &[Binding]) -> Vec<String> {
|
||||
fn add_to_names(names: &mut Vec<String>, elts: &[Expr]) {
|
||||
for elt in elts {
|
||||
if let ExprKind::Constant {
|
||||
@@ -23,8 +26,8 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
|
||||
// Grab the existing bound __all__ values.
|
||||
if let StmtKind::AugAssign { .. } = &stmt.node {
|
||||
if let Some(binding) = scope.values.get("__all__") {
|
||||
if let BindingKind::Export(existing) = &binding.kind {
|
||||
if let Some(index) = scope.values.get("__all__") {
|
||||
if let BindingKind::Export(existing) = &bindings[*index].kind {
|
||||
names.extend_from_slice(existing);
|
||||
}
|
||||
}
|
||||
@@ -69,6 +72,38 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
names
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct GlobalVisitor<'a> {
|
||||
globals: FxHashMap<&'a str, &'a Stmt>,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for GlobalVisitor<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||
match &stmt.node {
|
||||
StmtKind::Global { names } => {
|
||||
for name in names {
|
||||
self.globals.insert(name, stmt);
|
||||
}
|
||||
}
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
// Don't recurse.
|
||||
}
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a map from global name to its last-defining `Stmt`.
|
||||
pub fn extract_globals(body: &[Stmt]) -> FxHashMap<&str, &Stmt> {
|
||||
let mut visitor = GlobalVisitor::default();
|
||||
for stmt in body {
|
||||
visitor.visit_stmt(stmt);
|
||||
}
|
||||
visitor.globals
|
||||
}
|
||||
|
||||
/// Check if a node is parent of a conditional branch.
|
||||
pub fn on_conditional_branch<'a>(parents: &mut impl Iterator<Item = &'a Stmt>) -> bool {
|
||||
parents.any(|parent| {
|
||||
|
||||
151
src/ast/types.rs
151
src/ast/types.rs
@@ -1,7 +1,7 @@
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::{Expr, Keyword, Stmt};
|
||||
use rustpython_ast::{Arguments, Expr, Keyword, Stmt};
|
||||
use rustpython_parser::ast::{Located, Location};
|
||||
|
||||
fn id() -> usize {
|
||||
@@ -30,36 +30,60 @@ impl Range {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionScope {
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionDef<'a> {
|
||||
// Properties derived from StmtKind::FunctionDef.
|
||||
pub name: &'a str,
|
||||
pub args: &'a Arguments,
|
||||
pub body: &'a [Stmt],
|
||||
pub decorator_list: &'a [Expr],
|
||||
// pub returns: Option<&'a Expr>,
|
||||
// pub type_comment: Option<&'a str>,
|
||||
// Scope-specific properties.
|
||||
// TODO(charlie): Create AsyncFunctionDef to mirror the AST.
|
||||
pub async_: bool,
|
||||
pub uses_locals: bool,
|
||||
pub globals: FxHashMap<&'a str, &'a Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClassScope<'a> {
|
||||
#[derive(Debug)]
|
||||
pub struct ClassDef<'a> {
|
||||
// Properties derived from StmtKind::ClassDef.
|
||||
pub name: &'a str,
|
||||
pub bases: &'a [Expr],
|
||||
pub keywords: &'a [Keyword],
|
||||
// pub body: &'a [Stmt],
|
||||
pub decorator_list: &'a [Expr],
|
||||
// Scope-specific properties.
|
||||
pub globals: FxHashMap<&'a str, &'a Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Lambda<'a> {
|
||||
pub args: &'a Arguments,
|
||||
pub body: &'a Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScopeKind<'a> {
|
||||
Class(ClassScope<'a>),
|
||||
Function(FunctionScope),
|
||||
Class(ClassDef<'a>),
|
||||
Function(FunctionDef<'a>),
|
||||
Generator,
|
||||
Module,
|
||||
Arg,
|
||||
Lambda,
|
||||
Lambda(Lambda<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Scope<'a> {
|
||||
pub id: usize,
|
||||
pub kind: ScopeKind<'a>,
|
||||
pub import_starred: bool,
|
||||
pub values: FxHashMap<&'a str, Binding>,
|
||||
pub uses_locals: bool,
|
||||
/// A map from bound name to binding index.
|
||||
pub values: FxHashMap<&'a str, usize>,
|
||||
/// A list of (name, index) pairs for bindings that were overridden in the
|
||||
/// scope.
|
||||
pub overridden: Vec<(&'a str, usize)>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
@@ -68,43 +92,122 @@ impl<'a> Scope<'a> {
|
||||
id: id(),
|
||||
kind,
|
||||
import_starred: false,
|
||||
uses_locals: false,
|
||||
values: FxHashMap::default(),
|
||||
overridden: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BindingContext {
|
||||
pub defined_by: usize,
|
||||
pub defined_in: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BindingKind {
|
||||
Annotation,
|
||||
Argument,
|
||||
Assignment,
|
||||
// TODO(charlie): This seems to be a catch-all.
|
||||
Binding,
|
||||
LoopVar,
|
||||
Global,
|
||||
Nonlocal,
|
||||
Builtin,
|
||||
ClassDefinition,
|
||||
Definition,
|
||||
FunctionDefinition,
|
||||
Export(Vec<String>),
|
||||
FutureImportation,
|
||||
StarImportation(Option<usize>, Option<String>),
|
||||
Importation(String, String, BindingContext),
|
||||
FromImportation(String, String, BindingContext),
|
||||
SubmoduleImportation(String, String, BindingContext),
|
||||
Importation(String, String),
|
||||
FromImportation(String, String),
|
||||
SubmoduleImportation(String, String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Binding {
|
||||
pub struct Binding<'a> {
|
||||
pub kind: BindingKind,
|
||||
pub range: Range,
|
||||
/// The statement in which the `Binding` was defined.
|
||||
pub source: Option<RefEquality<'a, Stmt>>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used.
|
||||
pub used: Option<(usize, Range)>,
|
||||
}
|
||||
|
||||
// Pyflakes defines the following binding hierarchy (via inheritance):
|
||||
// Binding
|
||||
// ExportBinding
|
||||
// Annotation
|
||||
// Argument
|
||||
// Assignment
|
||||
// NamedExprAssignment
|
||||
// Definition
|
||||
// FunctionDefinition
|
||||
// ClassDefinition
|
||||
// Builtin
|
||||
// Importation
|
||||
// SubmoduleImportation
|
||||
// ImportationFrom
|
||||
// StarImportation
|
||||
// FutureImportation
|
||||
|
||||
impl<'a> Binding<'a> {
|
||||
pub fn is_definition(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
BindingKind::ClassDefinition
|
||||
| BindingKind::FunctionDefinition
|
||||
| BindingKind::Builtin
|
||||
| BindingKind::FutureImportation
|
||||
| BindingKind::StarImportation(..)
|
||||
| BindingKind::Importation(..)
|
||||
| BindingKind::FromImportation(..)
|
||||
| BindingKind::SubmoduleImportation(..)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn redefines(&self, existing: &'a Binding) -> bool {
|
||||
match &self.kind {
|
||||
BindingKind::Importation(_, full_name) | BindingKind::FromImportation(_, full_name) => {
|
||||
if let BindingKind::SubmoduleImportation(_, existing_full_name) = &existing.kind {
|
||||
return full_name == existing_full_name;
|
||||
}
|
||||
}
|
||||
BindingKind::SubmoduleImportation(_, full_name) => {
|
||||
if let BindingKind::Importation(_, existing_full_name)
|
||||
| BindingKind::FromImportation(_, existing_full_name)
|
||||
| BindingKind::SubmoduleImportation(_, existing_full_name) = &existing.kind
|
||||
{
|
||||
return full_name == existing_full_name;
|
||||
}
|
||||
}
|
||||
BindingKind::Annotation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::FutureImportation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::StarImportation(..) => {
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
existing.is_definition()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RefEquality<'a, T>(pub &'a T);
|
||||
|
||||
impl<'a, T> std::hash::Hash for RefEquality<'a, T> {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: std::hash::Hasher,
|
||||
{
|
||||
(self.0 as *const T).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
|
||||
fn eq(&self, other: &RefEquality<'b, T>) -> bool {
|
||||
std::ptr::eq(self.0, other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Eq for RefEquality<'a, T> {}
|
||||
|
||||
@@ -159,8 +159,8 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
orelse,
|
||||
..
|
||||
} => {
|
||||
visitor.visit_expr(target);
|
||||
visitor.visit_expr(iter);
|
||||
visitor.visit_expr(target);
|
||||
for stmt in body {
|
||||
visitor.visit_stmt(stmt);
|
||||
}
|
||||
@@ -175,8 +175,8 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
orelse,
|
||||
..
|
||||
} => {
|
||||
visitor.visit_expr(target);
|
||||
visitor.visit_expr(iter);
|
||||
visitor.visit_expr(target);
|
||||
for stmt in body {
|
||||
visitor.visit_stmt(stmt);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::borrow::Cow;
|
||||
use std::str::Lines;
|
||||
|
||||
use rustpython_ast::{Located, Location};
|
||||
@@ -5,31 +6,26 @@ use rustpython_ast::{Located, Location};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
|
||||
/// Extract the leading indentation from a line.
|
||||
pub fn indentation<'a, T>(checker: &'a Checker, located: &'a Located<T>) -> Cow<'a, str> {
|
||||
let range = Range::from_located(located);
|
||||
checker.locator.slice_source_code_range(&Range {
|
||||
location: Location::new(range.location.row(), 0),
|
||||
end_location: Location::new(range.location.row(), range.location.column()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract the leading words from a line of text.
|
||||
pub fn leading_words(line: &str) -> String {
|
||||
line.trim()
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphanumeric() || char.is_whitespace())
|
||||
.collect()
|
||||
pub fn leading_words(line: &str) -> &str {
|
||||
let line = line.trim();
|
||||
line.find(|char: char| !char.is_alphanumeric() && !char.is_whitespace())
|
||||
.map_or(line, |index| &line[..index])
|
||||
}
|
||||
|
||||
/// Extract the leading whitespace from a line of text.
|
||||
pub fn leading_space(line: &str) -> String {
|
||||
line.chars()
|
||||
.take_while(|char| char.is_whitespace())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Extract the leading indentation from a line.
|
||||
pub fn indentation<T>(checker: &Checker, located: &Located<T>) -> String {
|
||||
let range = Range::from_located(located);
|
||||
checker
|
||||
.locator
|
||||
.slice_source_code_range(&Range {
|
||||
location: Location::new(range.location.row(), 0),
|
||||
end_location: Location::new(range.location.row(), range.location.column()),
|
||||
})
|
||||
.to_string()
|
||||
pub fn leading_space(line: &str) -> &str {
|
||||
line.find(|char: char| !char.is_whitespace())
|
||||
.map_or(line, |index| &line[..index])
|
||||
}
|
||||
|
||||
/// Replace any non-whitespace characters from an indentation string.
|
||||
|
||||
1097
src/check_ast.rs
1097
src/check_ast.rs
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
||||
//! Lint rules based on import analysis.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_parser::ast::Suite;
|
||||
|
||||
use crate::ast::visitor::Visitor;
|
||||
@@ -33,8 +35,9 @@ pub fn check_imports(
|
||||
directives: &IsortDirectives,
|
||||
settings: &Settings,
|
||||
autofix: bool,
|
||||
path: &Path,
|
||||
) -> Vec<Check> {
|
||||
let mut tracker = ImportTracker::new(directives);
|
||||
let mut tracker = ImportTracker::new(directives, path);
|
||||
for stmt in python_ast {
|
||||
tracker.visit_stmt(stmt);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::{Check, CheckCode, CheckKind, REDIRECTS};
|
||||
use crate::checks::{Check, CheckCode, CheckKind, CODE_REDIRECTS};
|
||||
use crate::noqa;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::settings::Settings;
|
||||
@@ -209,7 +209,7 @@ pub fn check_lines(
|
||||
let mut invalid_codes = vec![];
|
||||
let mut valid_codes = vec![];
|
||||
for code in codes {
|
||||
let code = REDIRECTS.get(code).map_or(code, AsRef::as_ref);
|
||||
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
valid_codes.push(code.to_string());
|
||||
} else {
|
||||
|
||||
167
src/checks.rs
167
src/checks.rs
@@ -87,22 +87,27 @@ pub enum CheckCode {
|
||||
F706,
|
||||
F707,
|
||||
F722,
|
||||
F811,
|
||||
F821,
|
||||
F822,
|
||||
F823,
|
||||
F831,
|
||||
F841,
|
||||
F842,
|
||||
F901,
|
||||
// pylint
|
||||
PLC0414,
|
||||
PLC2201,
|
||||
PLC3002,
|
||||
PLE0117,
|
||||
PLE0118,
|
||||
PLE1142,
|
||||
PLR0206,
|
||||
PLR0402,
|
||||
PLR1701,
|
||||
PLR1722,
|
||||
PLW0120,
|
||||
PLW0602,
|
||||
// flake8-builtins
|
||||
A001,
|
||||
A002,
|
||||
@@ -160,7 +165,7 @@ pub enum CheckCode {
|
||||
// mccabe
|
||||
C901,
|
||||
// flake8-tidy-imports
|
||||
I252,
|
||||
TID252,
|
||||
// flake8-return
|
||||
RET501,
|
||||
RET502,
|
||||
@@ -242,6 +247,7 @@ pub enum CheckCode {
|
||||
D214,
|
||||
D215,
|
||||
D300,
|
||||
D301,
|
||||
D400,
|
||||
D402,
|
||||
D403,
|
||||
@@ -292,6 +298,12 @@ pub enum CheckCode {
|
||||
FBT001,
|
||||
FBT002,
|
||||
FBT003,
|
||||
// flake8-unused-arguments
|
||||
ARG001,
|
||||
ARG002,
|
||||
ARG003,
|
||||
ARG004,
|
||||
ARG005,
|
||||
// flake8-import-conventions
|
||||
ICN001,
|
||||
// Ruff
|
||||
@@ -326,6 +338,7 @@ pub enum CheckCategory {
|
||||
Flake8Quotes,
|
||||
Flake8Return,
|
||||
Flake8TidyImports,
|
||||
Flake8UnusedArguments,
|
||||
Eradicate,
|
||||
PygrepHooks,
|
||||
Pylint,
|
||||
@@ -364,6 +377,7 @@ impl CheckCategory {
|
||||
CheckCategory::Flake8Quotes => "flake8-quotes",
|
||||
CheckCategory::Flake8Return => "flake8-return",
|
||||
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
|
||||
CheckCategory::Flake8UnusedArguments => "flake8-unused-arguments",
|
||||
CheckCategory::Isort => "isort",
|
||||
CheckCategory::McCabe => "mccabe",
|
||||
CheckCategory::PEP8Naming => "pep8-naming",
|
||||
@@ -392,8 +406,9 @@ impl CheckCategory {
|
||||
CheckCategory::Flake8Print => vec![CheckCodePrefix::T20],
|
||||
CheckCategory::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||
CheckCategory::Flake8Return => vec![CheckCodePrefix::RET],
|
||||
CheckCategory::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||
CheckCategory::Isort => vec![CheckCodePrefix::I00],
|
||||
CheckCategory::Flake8TidyImports => vec![CheckCodePrefix::TID],
|
||||
CheckCategory::Flake8UnusedArguments => vec![CheckCodePrefix::ARG],
|
||||
CheckCategory::Isort => vec![CheckCodePrefix::I],
|
||||
CheckCategory::McCabe => vec![CheckCodePrefix::C90],
|
||||
CheckCategory::PEP8Naming => vec![CheckCodePrefix::N],
|
||||
CheckCategory::Pycodestyle => vec![CheckCodePrefix::E, CheckCodePrefix::W],
|
||||
@@ -470,6 +485,10 @@ impl CheckCategory {
|
||||
"https://pypi.org/project/flake8-tidy-imports/4.8.0/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
CheckCategory::Flake8UnusedArguments => Some((
|
||||
"https://pypi.org/project/flake8-unused-arguments/0.0.12/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
CheckCategory::Isort => {
|
||||
Some(("https://pypi.org/project/isort/5.10.1/", &Platform::PyPI))
|
||||
}
|
||||
@@ -603,6 +622,7 @@ pub enum CheckKind {
|
||||
PercentFormatStarRequiresSequence,
|
||||
PercentFormatUnsupportedFormatCharacter(char),
|
||||
RaiseNotImplemented,
|
||||
RedefinedWhileUnused(String, usize),
|
||||
ReturnOutsideFunction,
|
||||
StringDotFormatExtraNamedArguments(Vec<String>),
|
||||
StringDotFormatExtraPositionalArguments(Vec<String>),
|
||||
@@ -612,20 +632,24 @@ pub enum CheckKind {
|
||||
TwoStarredExpressions,
|
||||
UndefinedExport(String),
|
||||
UndefinedLocal(String),
|
||||
UnusedAnnotation(String),
|
||||
UndefinedName(String),
|
||||
UnusedImport(String, bool),
|
||||
UnusedVariable(String),
|
||||
YieldOutsideFunction(DeferralKeyword),
|
||||
// pylint
|
||||
ConsiderMergingIsinstance(String, Vec<String>),
|
||||
UselessImportAlias,
|
||||
MisplacedComparisonConstant(String),
|
||||
UnnecessaryDirectLambdaCall,
|
||||
PropertyWithParameters,
|
||||
ConsiderUsingFromImport(String, String),
|
||||
AwaitOutsideAsync,
|
||||
ConsiderMergingIsinstance(String, Vec<String>),
|
||||
ConsiderUsingFromImport(String, String),
|
||||
GlobalVariableNotAssigned(String),
|
||||
MisplacedComparisonConstant(String),
|
||||
NonlocalWithoutBinding(String),
|
||||
PropertyWithParameters,
|
||||
UnnecessaryDirectLambdaCall,
|
||||
UseSysExit(String),
|
||||
UsedPriorGlobalDeclaration(String, usize),
|
||||
UselessElseOnLoop,
|
||||
ConsiderUsingSysExit,
|
||||
UselessImportAlias,
|
||||
// flake8-builtins
|
||||
BuiltinVariableShadowing(String),
|
||||
BuiltinArgumentShadowing(String),
|
||||
@@ -781,6 +805,7 @@ pub enum CheckKind {
|
||||
SectionUnderlineMatchesSectionLength(String),
|
||||
SectionUnderlineNotOverIndented(String),
|
||||
SkipDocstring,
|
||||
UsesRPrefixForBackslashedContent,
|
||||
UsesTripleQuotes,
|
||||
// pep8-naming
|
||||
InvalidClassName(String),
|
||||
@@ -817,6 +842,12 @@ pub enum CheckKind {
|
||||
BooleanPositionalValueInFunctionCall,
|
||||
// pygrep-hooks
|
||||
NoEval,
|
||||
// flake8-unused-arguments
|
||||
UnusedFunctionArgument(String),
|
||||
UnusedMethodArgument(String),
|
||||
UnusedClassMethodArgument(String),
|
||||
UnusedStaticMethodArgument(String),
|
||||
UnusedLambdaArgument(String),
|
||||
// flake8-import-conventions
|
||||
ImportAliasIsNotConventional(String, String),
|
||||
// Ruff
|
||||
@@ -913,16 +944,20 @@ impl CheckCode {
|
||||
CheckCode::F706 => CheckKind::ReturnOutsideFunction,
|
||||
CheckCode::F707 => CheckKind::DefaultExceptNotLast,
|
||||
CheckCode::F722 => CheckKind::ForwardAnnotationSyntaxError("...".to_string()),
|
||||
CheckCode::F811 => CheckKind::RedefinedWhileUnused("...".to_string(), 1),
|
||||
CheckCode::F821 => CheckKind::UndefinedName("...".to_string()),
|
||||
CheckCode::F822 => CheckKind::UndefinedExport("...".to_string()),
|
||||
CheckCode::F823 => CheckKind::UndefinedLocal("...".to_string()),
|
||||
CheckCode::F831 => CheckKind::DuplicateArgumentName,
|
||||
CheckCode::F841 => CheckKind::UnusedVariable("...".to_string()),
|
||||
CheckCode::F842 => CheckKind::UnusedAnnotation("...".to_string()),
|
||||
CheckCode::F901 => CheckKind::RaiseNotImplemented,
|
||||
// pylint
|
||||
CheckCode::PLC0414 => CheckKind::UselessImportAlias,
|
||||
CheckCode::PLC2201 => CheckKind::MisplacedComparisonConstant("...".to_string()),
|
||||
CheckCode::PLC3002 => CheckKind::UnnecessaryDirectLambdaCall,
|
||||
CheckCode::PLE0117 => CheckKind::NonlocalWithoutBinding("...".to_string()),
|
||||
CheckCode::PLE0118 => CheckKind::UsedPriorGlobalDeclaration("...".to_string(), 1),
|
||||
CheckCode::PLE1142 => CheckKind::AwaitOutsideAsync,
|
||||
CheckCode::PLR0402 => {
|
||||
CheckKind::ConsiderUsingFromImport("...".to_string(), "...".to_string())
|
||||
@@ -931,8 +966,9 @@ impl CheckCode {
|
||||
CheckCode::PLR1701 => {
|
||||
CheckKind::ConsiderMergingIsinstance("...".to_string(), vec!["...".to_string()])
|
||||
}
|
||||
CheckCode::PLR1722 => CheckKind::ConsiderUsingSysExit,
|
||||
CheckCode::PLR1722 => CheckKind::UseSysExit("exit".to_string()),
|
||||
CheckCode::PLW0120 => CheckKind::UselessElseOnLoop,
|
||||
CheckCode::PLW0602 => CheckKind::GlobalVariableNotAssigned("...".to_string()),
|
||||
// flake8-builtins
|
||||
CheckCode::A001 => CheckKind::BuiltinVariableShadowing("...".to_string()),
|
||||
CheckCode::A002 => CheckKind::BuiltinArgumentShadowing("...".to_string()),
|
||||
@@ -1003,7 +1039,7 @@ impl CheckCode {
|
||||
// flake8-debugger
|
||||
CheckCode::T100 => CheckKind::Debugger(DebuggerUsingType::Import("...".to_string())),
|
||||
// flake8-tidy-imports
|
||||
CheckCode::I252 => CheckKind::BannedRelativeImport(Strictness::All),
|
||||
CheckCode::TID252 => CheckKind::BannedRelativeImport(Strictness::All),
|
||||
// flake8-return
|
||||
CheckCode::RET501 => CheckKind::UnnecessaryReturnNone,
|
||||
CheckCode::RET502 => CheckKind::ImplicitReturnValue,
|
||||
@@ -1090,6 +1126,7 @@ impl CheckCode {
|
||||
CheckCode::D214 => CheckKind::SectionNotOverIndented("Returns".to_string()),
|
||||
CheckCode::D215 => CheckKind::SectionUnderlineNotOverIndented("Returns".to_string()),
|
||||
CheckCode::D300 => CheckKind::UsesTripleQuotes,
|
||||
CheckCode::D301 => CheckKind::UsesRPrefixForBackslashedContent,
|
||||
CheckCode::D400 => CheckKind::EndsInPeriod,
|
||||
CheckCode::D402 => CheckKind::NoSignature,
|
||||
CheckCode::D403 => CheckKind::FirstLineCapitalized,
|
||||
@@ -1159,6 +1196,12 @@ impl CheckCode {
|
||||
CheckCode::FBT003 => CheckKind::BooleanPositionalValueInFunctionCall,
|
||||
// pygrep-hooks
|
||||
CheckCode::PGH001 => CheckKind::NoEval,
|
||||
// flake8-unused-arguments
|
||||
CheckCode::ARG001 => CheckKind::UnusedFunctionArgument("...".to_string()),
|
||||
CheckCode::ARG002 => CheckKind::UnusedMethodArgument("...".to_string()),
|
||||
CheckCode::ARG003 => CheckKind::UnusedClassMethodArgument("...".to_string()),
|
||||
CheckCode::ARG004 => CheckKind::UnusedStaticMethodArgument("...".to_string()),
|
||||
CheckCode::ARG005 => CheckKind::UnusedLambdaArgument("...".to_string()),
|
||||
// flake8-import-conventions
|
||||
CheckCode::ICN001 => {
|
||||
CheckKind::ImportAliasIsNotConventional("...".to_string(), "...".to_string())
|
||||
@@ -1188,6 +1231,11 @@ impl CheckCode {
|
||||
CheckCode::ANN205 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN206 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ANN401 => CheckCategory::Flake8Annotations,
|
||||
CheckCode::ARG001 => CheckCategory::Flake8UnusedArguments,
|
||||
CheckCode::ARG002 => CheckCategory::Flake8UnusedArguments,
|
||||
CheckCode::ARG003 => CheckCategory::Flake8UnusedArguments,
|
||||
CheckCode::ARG004 => CheckCategory::Flake8UnusedArguments,
|
||||
CheckCode::ARG005 => CheckCategory::Flake8UnusedArguments,
|
||||
CheckCode::B002 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B003 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B004 => CheckCategory::Flake8Bugbear,
|
||||
@@ -1259,6 +1307,7 @@ impl CheckCode {
|
||||
CheckCode::D214 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D215 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D300 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D301 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D400 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D402 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D403 => CheckCategory::Pydocstyle,
|
||||
@@ -1329,18 +1378,20 @@ impl CheckCode {
|
||||
CheckCode::F706 => CheckCategory::Pyflakes,
|
||||
CheckCode::F707 => CheckCategory::Pyflakes,
|
||||
CheckCode::F722 => CheckCategory::Pyflakes,
|
||||
CheckCode::F811 => CheckCategory::Pyflakes,
|
||||
CheckCode::F821 => CheckCategory::Pyflakes,
|
||||
CheckCode::F822 => CheckCategory::Pyflakes,
|
||||
CheckCode::F823 => CheckCategory::Pyflakes,
|
||||
CheckCode::F831 => CheckCategory::Pyflakes,
|
||||
CheckCode::F841 => CheckCategory::Pyflakes,
|
||||
CheckCode::F842 => CheckCategory::Pyflakes,
|
||||
CheckCode::F901 => CheckCategory::Pyflakes,
|
||||
CheckCode::FBT001 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::I001 => CheckCategory::Isort,
|
||||
CheckCode::TID252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::ICN001 => CheckCategory::Flake8ImportConventions,
|
||||
CheckCode::I252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::N801 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N802 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N803 => CheckCategory::PEP8Naming,
|
||||
@@ -1360,12 +1411,15 @@ impl CheckCode {
|
||||
CheckCode::PLC0414 => CheckCategory::Pylint,
|
||||
CheckCode::PLC2201 => CheckCategory::Pylint,
|
||||
CheckCode::PLC3002 => CheckCategory::Pylint,
|
||||
CheckCode::PLE0117 => CheckCategory::Pylint,
|
||||
CheckCode::PLE0118 => CheckCategory::Pylint,
|
||||
CheckCode::PLE1142 => CheckCategory::Pylint,
|
||||
CheckCode::PLR0206 => CheckCategory::Pylint,
|
||||
CheckCode::PLR0402 => CheckCategory::Pylint,
|
||||
CheckCode::PLR1701 => CheckCategory::Pylint,
|
||||
CheckCode::PLR1722 => CheckCategory::Pylint,
|
||||
CheckCode::PLW0120 => CheckCategory::Pylint,
|
||||
CheckCode::PLW0602 => CheckCategory::Pylint,
|
||||
CheckCode::Q000 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q001 => CheckCategory::Flake8Quotes,
|
||||
CheckCode::Q002 => CheckCategory::Flake8Quotes,
|
||||
@@ -1478,23 +1532,28 @@ impl CheckKind {
|
||||
CheckKind::TypeComparison => &CheckCode::E721,
|
||||
CheckKind::UndefinedExport(_) => &CheckCode::F822,
|
||||
CheckKind::UndefinedLocal(_) => &CheckCode::F823,
|
||||
CheckKind::RedefinedWhileUnused(..) => &CheckCode::F811,
|
||||
CheckKind::UndefinedName(_) => &CheckCode::F821,
|
||||
CheckKind::UnusedImport(..) => &CheckCode::F401,
|
||||
CheckKind::UnusedVariable(_) => &CheckCode::F841,
|
||||
CheckKind::UnusedAnnotation(_) => &CheckCode::F842,
|
||||
CheckKind::YieldOutsideFunction(_) => &CheckCode::F704,
|
||||
// pycodestyle warnings
|
||||
CheckKind::NoNewLineAtEndOfFile => &CheckCode::W292,
|
||||
CheckKind::InvalidEscapeSequence(_) => &CheckCode::W605,
|
||||
// pylint
|
||||
CheckKind::UselessImportAlias => &CheckCode::PLC0414,
|
||||
CheckKind::MisplacedComparisonConstant(..) => &CheckCode::PLC2201,
|
||||
CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002,
|
||||
CheckKind::AwaitOutsideAsync => &CheckCode::PLE1142,
|
||||
CheckKind::ConsiderMergingIsinstance(..) => &CheckCode::PLR1701,
|
||||
CheckKind::PropertyWithParameters => &CheckCode::PLR0206,
|
||||
CheckKind::ConsiderUsingFromImport(..) => &CheckCode::PLR0402,
|
||||
CheckKind::ConsiderUsingSysExit => &CheckCode::PLR1722,
|
||||
CheckKind::GlobalVariableNotAssigned(..) => &CheckCode::PLW0602,
|
||||
CheckKind::MisplacedComparisonConstant(..) => &CheckCode::PLC2201,
|
||||
CheckKind::PropertyWithParameters => &CheckCode::PLR0206,
|
||||
CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002,
|
||||
CheckKind::UseSysExit(_) => &CheckCode::PLR1722,
|
||||
CheckKind::NonlocalWithoutBinding(..) => &CheckCode::PLE0117,
|
||||
CheckKind::UsedPriorGlobalDeclaration(..) => &CheckCode::PLE0118,
|
||||
CheckKind::UselessElseOnLoop => &CheckCode::PLW0120,
|
||||
CheckKind::UselessImportAlias => &CheckCode::PLC0414,
|
||||
// flake8-builtins
|
||||
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
|
||||
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
|
||||
@@ -1550,7 +1609,7 @@ impl CheckKind {
|
||||
// flake8-debugger
|
||||
CheckKind::Debugger(_) => &CheckCode::T100,
|
||||
// flake8-tidy-imports
|
||||
CheckKind::BannedRelativeImport(_) => &CheckCode::I252,
|
||||
CheckKind::BannedRelativeImport(_) => &CheckCode::TID252,
|
||||
// flake8-return
|
||||
CheckKind::UnnecessaryReturnNone => &CheckCode::RET501,
|
||||
CheckKind::ImplicitReturnValue => &CheckCode::RET502,
|
||||
@@ -1650,6 +1709,7 @@ impl CheckKind {
|
||||
CheckKind::SectionUnderlineMatchesSectionLength(_) => &CheckCode::D409,
|
||||
CheckKind::SectionUnderlineNotOverIndented(_) => &CheckCode::D215,
|
||||
CheckKind::SkipDocstring => &CheckCode::D418,
|
||||
CheckKind::UsesRPrefixForBackslashedContent => &CheckCode::D301,
|
||||
CheckKind::UsesTripleQuotes => &CheckCode::D300,
|
||||
// pep8-naming
|
||||
CheckKind::InvalidClassName(_) => &CheckCode::N801,
|
||||
@@ -1686,6 +1746,12 @@ impl CheckKind {
|
||||
CheckKind::BooleanPositionalValueInFunctionCall => &CheckCode::FBT003,
|
||||
// pygrep-hooks
|
||||
CheckKind::NoEval => &CheckCode::PGH001,
|
||||
// flake8-unused-arguments
|
||||
CheckKind::UnusedFunctionArgument(..) => &CheckCode::ARG001,
|
||||
CheckKind::UnusedMethodArgument(..) => &CheckCode::ARG002,
|
||||
CheckKind::UnusedClassMethodArgument(..) => &CheckCode::ARG003,
|
||||
CheckKind::UnusedStaticMethodArgument(..) => &CheckCode::ARG004,
|
||||
CheckKind::UnusedLambdaArgument(..) => &CheckCode::ARG005,
|
||||
// flake8-import-conventions
|
||||
CheckKind::ImportAliasIsNotConventional(..) => &CheckCode::ICN001,
|
||||
// Ruff
|
||||
@@ -1810,6 +1876,9 @@ impl CheckKind {
|
||||
CheckKind::RaiseNotImplemented => {
|
||||
"`raise NotImplemented` should be `raise NotImplementedError`".to_string()
|
||||
}
|
||||
CheckKind::RedefinedWhileUnused(name, line) => {
|
||||
format!("Redefinition of unused `{name}` from line {line}")
|
||||
}
|
||||
CheckKind::ReturnOutsideFunction => {
|
||||
"`return` statement outside of a function/method".to_string()
|
||||
}
|
||||
@@ -1858,6 +1927,9 @@ impl CheckKind {
|
||||
CheckKind::UndefinedName(name) => {
|
||||
format!("Undefined name `{name}`")
|
||||
}
|
||||
CheckKind::UnusedAnnotation(name) => {
|
||||
format!("Local variable `{name}` is annotated but never used")
|
||||
}
|
||||
CheckKind::UnusedImport(name, ignore_init) => {
|
||||
if *ignore_init {
|
||||
format!(
|
||||
@@ -1885,10 +1957,13 @@ impl CheckKind {
|
||||
}
|
||||
CheckKind::ConsiderMergingIsinstance(obj, types) => {
|
||||
let types = types.join(", ");
|
||||
format!("Consider merging these isinstance calls: `isinstance({obj}, ({types}))`")
|
||||
format!("Merge these isinstance calls: `isinstance({obj}, ({types}))`")
|
||||
}
|
||||
CheckKind::MisplacedComparisonConstant(comprison) => {
|
||||
format!("Comparison should be {comprison}")
|
||||
CheckKind::MisplacedComparisonConstant(comparison) => {
|
||||
format!("Comparison should be {comparison}")
|
||||
}
|
||||
CheckKind::NonlocalWithoutBinding(name) => {
|
||||
format!("Nonlocal name `{name}` found without binding")
|
||||
}
|
||||
CheckKind::UnnecessaryDirectLambdaCall => "Lambda expression called directly. Execute \
|
||||
the expression inline instead."
|
||||
@@ -1897,7 +1972,13 @@ impl CheckKind {
|
||||
"Cannot have defined parameters for properties".to_string()
|
||||
}
|
||||
CheckKind::ConsiderUsingFromImport(module, name) => {
|
||||
format!("Consider using `from {module} import {name}`")
|
||||
format!("Use `from {module} import {name}` in lieu of alias")
|
||||
}
|
||||
CheckKind::UsedPriorGlobalDeclaration(name, line) => {
|
||||
format!("Name `{name}` is used prior to global declaration on line {line}")
|
||||
}
|
||||
CheckKind::GlobalVariableNotAssigned(name) => {
|
||||
format!("Using global for `{name}` but no assignment is done")
|
||||
}
|
||||
CheckKind::AwaitOutsideAsync => {
|
||||
"`await` should be used within an async function".to_string()
|
||||
@@ -1905,7 +1986,7 @@ impl CheckKind {
|
||||
CheckKind::UselessElseOnLoop => "Else clause on loop without a break statement, \
|
||||
remove the else and de-indent all the code inside it"
|
||||
.to_string(),
|
||||
CheckKind::ConsiderUsingSysExit => "Consider using `sys.exit()`".to_string(),
|
||||
CheckKind::UseSysExit(name) => format!("Use `sys.exit()` instead of `{name}`"),
|
||||
// flake8-builtins
|
||||
CheckKind::BuiltinVariableShadowing(name) => {
|
||||
format!("Variable `{name}` is shadowing a python builtin")
|
||||
@@ -2296,6 +2377,9 @@ impl CheckKind {
|
||||
CheckKind::FirstLineCapitalized => {
|
||||
"First word of the first line should be properly capitalized".to_string()
|
||||
}
|
||||
CheckKind::UsesRPrefixForBackslashedContent => {
|
||||
r#"Use r""" if any backslashes in a docstring"#.to_string()
|
||||
}
|
||||
CheckKind::UsesTripleQuotes => r#"Use """triple double quotes""""#.to_string(),
|
||||
CheckKind::MultiLineSummaryFirstLine => {
|
||||
"Multi-line docstring summary should start at the first line".to_string()
|
||||
@@ -2477,6 +2561,18 @@ impl CheckKind {
|
||||
}
|
||||
// pygrep-hooks
|
||||
CheckKind::NoEval => "No builtin `eval()` allowed".to_string(),
|
||||
// flake8-unused-arguments
|
||||
CheckKind::UnusedFunctionArgument(name) => {
|
||||
format!("Unused function argument: `{name}`")
|
||||
}
|
||||
CheckKind::UnusedMethodArgument(name) => format!("Unused method argument: `{name}`"),
|
||||
CheckKind::UnusedClassMethodArgument(name) => {
|
||||
format!("Unused class method argument: `{name}`")
|
||||
}
|
||||
CheckKind::UnusedStaticMethodArgument(name) => {
|
||||
format!("Unused static method argument: `{name}`")
|
||||
}
|
||||
CheckKind::UnusedLambdaArgument(name) => format!("Unused lambda argument: `{name}`"),
|
||||
// flake8-import-conventions
|
||||
CheckKind::ImportAliasIsNotConventional(name, asname) => {
|
||||
format!("`{name}` should be imported as `{asname}`")
|
||||
@@ -2551,7 +2647,6 @@ impl CheckKind {
|
||||
| CheckKind::BlankLineBeforeSection(..)
|
||||
| CheckKind::CapitalizeSectionName(..)
|
||||
| CheckKind::CommentedOutCode
|
||||
| CheckKind::ConsiderUsingSysExit
|
||||
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass(..)
|
||||
| CheckKind::DashedUnderlineAfterSection(..)
|
||||
@@ -2617,6 +2712,7 @@ impl CheckKind {
|
||||
| CheckKind::UnusedNOQA(..)
|
||||
| CheckKind::UsePEP585Annotation(..)
|
||||
| CheckKind::UsePEP604Annotation
|
||||
| CheckKind::UseSysExit(..)
|
||||
| CheckKind::UselessImportAlias
|
||||
| CheckKind::UselessMetaclassType
|
||||
| CheckKind::UselessObjectInheritance(..)
|
||||
@@ -2648,7 +2744,7 @@ impl Check {
|
||||
}
|
||||
|
||||
/// A hash map from deprecated to latest `CheckCode`.
|
||||
pub static REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||
FxHashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U001", CheckCode::UP001),
|
||||
@@ -2665,6 +2761,25 @@ pub static REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||
("U013", CheckCode::UP013),
|
||||
("U014", CheckCode::UP014),
|
||||
("U015", CheckCode::UP015),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I252", CheckCode::TID252),
|
||||
("M001", CheckCode::RUF100),
|
||||
])
|
||||
});
|
||||
|
||||
/// A hash map from deprecated `CheckCodePrefix` to latest `CheckCodePrefix`.
|
||||
pub static PREFIX_REDIRECTS: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
FxHashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U", "UP"),
|
||||
("U0", "UP0"),
|
||||
("U00", "UP00"),
|
||||
("U01", "UP01"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I2", "TID2"),
|
||||
("I25", "TID25"),
|
||||
("M", "RUF100"),
|
||||
("M0", "RUF100"),
|
||||
])
|
||||
});
|
||||
|
||||
|
||||
@@ -36,6 +36,14 @@ pub enum CheckCodePrefix {
|
||||
ANN4,
|
||||
ANN40,
|
||||
ANN401,
|
||||
ARG,
|
||||
ARG0,
|
||||
ARG00,
|
||||
ARG001,
|
||||
ARG002,
|
||||
ARG003,
|
||||
ARG004,
|
||||
ARG005,
|
||||
B,
|
||||
B0,
|
||||
B00,
|
||||
@@ -131,6 +139,7 @@ pub enum CheckCodePrefix {
|
||||
D3,
|
||||
D30,
|
||||
D300,
|
||||
D301,
|
||||
D4,
|
||||
D40,
|
||||
D400,
|
||||
@@ -235,6 +244,8 @@ pub enum CheckCodePrefix {
|
||||
F72,
|
||||
F722,
|
||||
F8,
|
||||
F81,
|
||||
F811,
|
||||
F82,
|
||||
F821,
|
||||
F822,
|
||||
@@ -243,6 +254,7 @@ pub enum CheckCodePrefix {
|
||||
F831,
|
||||
F84,
|
||||
F841,
|
||||
F842,
|
||||
F9,
|
||||
F90,
|
||||
F901,
|
||||
@@ -263,6 +275,9 @@ pub enum CheckCodePrefix {
|
||||
ICN0,
|
||||
ICN00,
|
||||
ICN001,
|
||||
M,
|
||||
M0,
|
||||
M001,
|
||||
N,
|
||||
N8,
|
||||
N80,
|
||||
@@ -300,6 +315,11 @@ pub enum CheckCodePrefix {
|
||||
PLC300,
|
||||
PLC3002,
|
||||
PLE,
|
||||
PLE0,
|
||||
PLE01,
|
||||
PLE011,
|
||||
PLE0117,
|
||||
PLE0118,
|
||||
PLE1,
|
||||
PLE11,
|
||||
PLE114,
|
||||
@@ -323,6 +343,9 @@ pub enum CheckCodePrefix {
|
||||
PLW01,
|
||||
PLW012,
|
||||
PLW0120,
|
||||
PLW06,
|
||||
PLW060,
|
||||
PLW0602,
|
||||
Q,
|
||||
Q0,
|
||||
Q00,
|
||||
@@ -367,6 +390,10 @@ pub enum CheckCodePrefix {
|
||||
T20,
|
||||
T201,
|
||||
T203,
|
||||
TID,
|
||||
TID2,
|
||||
TID25,
|
||||
TID252,
|
||||
U,
|
||||
U0,
|
||||
U00,
|
||||
@@ -492,6 +519,32 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::ANN4 => vec![CheckCode::ANN401],
|
||||
CheckCodePrefix::ANN40 => vec![CheckCode::ANN401],
|
||||
CheckCodePrefix::ANN401 => vec![CheckCode::ANN401],
|
||||
CheckCodePrefix::ARG => vec![
|
||||
CheckCode::ARG001,
|
||||
CheckCode::ARG002,
|
||||
CheckCode::ARG003,
|
||||
CheckCode::ARG004,
|
||||
CheckCode::ARG005,
|
||||
],
|
||||
CheckCodePrefix::ARG0 => vec![
|
||||
CheckCode::ARG001,
|
||||
CheckCode::ARG002,
|
||||
CheckCode::ARG003,
|
||||
CheckCode::ARG004,
|
||||
CheckCode::ARG005,
|
||||
],
|
||||
CheckCodePrefix::ARG00 => vec![
|
||||
CheckCode::ARG001,
|
||||
CheckCode::ARG002,
|
||||
CheckCode::ARG003,
|
||||
CheckCode::ARG004,
|
||||
CheckCode::ARG005,
|
||||
],
|
||||
CheckCodePrefix::ARG001 => vec![CheckCode::ARG001],
|
||||
CheckCodePrefix::ARG002 => vec![CheckCode::ARG002],
|
||||
CheckCodePrefix::ARG003 => vec![CheckCode::ARG003],
|
||||
CheckCodePrefix::ARG004 => vec![CheckCode::ARG004],
|
||||
CheckCodePrefix::ARG005 => vec![CheckCode::ARG005],
|
||||
CheckCodePrefix::B => vec![
|
||||
CheckCode::B002,
|
||||
CheckCode::B003,
|
||||
@@ -718,6 +771,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::D214,
|
||||
CheckCode::D215,
|
||||
CheckCode::D300,
|
||||
CheckCode::D301,
|
||||
CheckCode::D400,
|
||||
CheckCode::D402,
|
||||
CheckCode::D403,
|
||||
@@ -820,9 +874,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::D213 => vec![CheckCode::D213],
|
||||
CheckCodePrefix::D214 => vec![CheckCode::D214],
|
||||
CheckCodePrefix::D215 => vec![CheckCode::D215],
|
||||
CheckCodePrefix::D3 => vec![CheckCode::D300],
|
||||
CheckCodePrefix::D30 => vec![CheckCode::D300],
|
||||
CheckCodePrefix::D3 => vec![CheckCode::D300, CheckCode::D301],
|
||||
CheckCodePrefix::D30 => vec![CheckCode::D300, CheckCode::D301],
|
||||
CheckCodePrefix::D300 => vec![CheckCode::D300],
|
||||
CheckCodePrefix::D301 => vec![CheckCode::D301],
|
||||
CheckCodePrefix::D4 => vec![
|
||||
CheckCode::D400,
|
||||
CheckCode::D402,
|
||||
@@ -985,11 +1040,13 @@ impl CheckCodePrefix {
|
||||
CheckCode::F706,
|
||||
CheckCode::F707,
|
||||
CheckCode::F722,
|
||||
CheckCode::F811,
|
||||
CheckCode::F821,
|
||||
CheckCode::F822,
|
||||
CheckCode::F823,
|
||||
CheckCode::F831,
|
||||
CheckCode::F841,
|
||||
CheckCode::F842,
|
||||
CheckCode::F901,
|
||||
],
|
||||
CheckCodePrefix::F4 => vec![
|
||||
@@ -1117,20 +1174,25 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F72 => vec![CheckCode::F722],
|
||||
CheckCodePrefix::F722 => vec![CheckCode::F722],
|
||||
CheckCodePrefix::F8 => vec![
|
||||
CheckCode::F811,
|
||||
CheckCode::F821,
|
||||
CheckCode::F822,
|
||||
CheckCode::F823,
|
||||
CheckCode::F831,
|
||||
CheckCode::F841,
|
||||
CheckCode::F842,
|
||||
],
|
||||
CheckCodePrefix::F81 => vec![CheckCode::F811],
|
||||
CheckCodePrefix::F811 => vec![CheckCode::F811],
|
||||
CheckCodePrefix::F82 => vec![CheckCode::F821, CheckCode::F822, CheckCode::F823],
|
||||
CheckCodePrefix::F821 => vec![CheckCode::F821],
|
||||
CheckCodePrefix::F822 => vec![CheckCode::F822],
|
||||
CheckCodePrefix::F823 => vec![CheckCode::F823],
|
||||
CheckCodePrefix::F83 => vec![CheckCode::F831],
|
||||
CheckCodePrefix::F831 => vec![CheckCode::F831],
|
||||
CheckCodePrefix::F84 => vec![CheckCode::F841],
|
||||
CheckCodePrefix::F84 => vec![CheckCode::F841, CheckCode::F842],
|
||||
CheckCodePrefix::F841 => vec![CheckCode::F841],
|
||||
CheckCodePrefix::F842 => vec![CheckCode::F842],
|
||||
CheckCodePrefix::F9 => vec![CheckCode::F901],
|
||||
CheckCodePrefix::F90 => vec![CheckCode::F901],
|
||||
CheckCodePrefix::F901 => vec![CheckCode::F901],
|
||||
@@ -1140,17 +1202,68 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::FBT001 => vec![CheckCode::FBT001],
|
||||
CheckCodePrefix::FBT002 => vec![CheckCode::FBT002],
|
||||
CheckCodePrefix::FBT003 => vec![CheckCode::FBT003],
|
||||
CheckCodePrefix::I => vec![CheckCode::I252, CheckCode::I001],
|
||||
CheckCodePrefix::I => vec![CheckCode::I001],
|
||||
CheckCodePrefix::I0 => vec![CheckCode::I001],
|
||||
CheckCodePrefix::I00 => vec![CheckCode::I001],
|
||||
CheckCodePrefix::I001 => vec![CheckCode::I001],
|
||||
CheckCodePrefix::I2 => vec![CheckCode::I252],
|
||||
CheckCodePrefix::I25 => vec![CheckCode::I252],
|
||||
CheckCodePrefix::I252 => vec![CheckCode::I252],
|
||||
CheckCodePrefix::I2 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`I2` has been remapped to `TID2`".bold()
|
||||
);
|
||||
vec![CheckCode::TID252]
|
||||
}
|
||||
CheckCodePrefix::I25 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`I25` has been remapped to `TID25`".bold()
|
||||
);
|
||||
vec![CheckCode::TID252]
|
||||
}
|
||||
CheckCodePrefix::I252 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`I252` has been remapped to `TID252`".bold()
|
||||
);
|
||||
vec![CheckCode::TID252]
|
||||
}
|
||||
CheckCodePrefix::ICN => vec![CheckCode::ICN001],
|
||||
CheckCodePrefix::ICN0 => vec![CheckCode::ICN001],
|
||||
CheckCodePrefix::ICN00 => vec![CheckCode::ICN001],
|
||||
CheckCodePrefix::ICN001 => vec![CheckCode::ICN001],
|
||||
CheckCodePrefix::M => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`M` has been remapped to `RUF100`".bold()
|
||||
);
|
||||
vec![CheckCode::RUF100]
|
||||
}
|
||||
CheckCodePrefix::M0 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`M0` has been remapped to `RUF100`".bold()
|
||||
);
|
||||
vec![CheckCode::RUF100]
|
||||
}
|
||||
CheckCodePrefix::M001 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`M001` has been remapped to `RUF100`".bold()
|
||||
);
|
||||
vec![CheckCode::RUF100]
|
||||
}
|
||||
CheckCodePrefix::N => vec![
|
||||
CheckCode::N801,
|
||||
CheckCode::N802,
|
||||
@@ -1238,7 +1351,14 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::PLC30 => vec![CheckCode::PLC3002],
|
||||
CheckCodePrefix::PLC300 => vec![CheckCode::PLC3002],
|
||||
CheckCodePrefix::PLC3002 => vec![CheckCode::PLC3002],
|
||||
CheckCodePrefix::PLE => vec![CheckCode::PLE1142],
|
||||
CheckCodePrefix::PLE => {
|
||||
vec![CheckCode::PLE0117, CheckCode::PLE0118, CheckCode::PLE1142]
|
||||
}
|
||||
CheckCodePrefix::PLE0 => vec![CheckCode::PLE0117, CheckCode::PLE0118],
|
||||
CheckCodePrefix::PLE01 => vec![CheckCode::PLE0117, CheckCode::PLE0118],
|
||||
CheckCodePrefix::PLE011 => vec![CheckCode::PLE0117, CheckCode::PLE0118],
|
||||
CheckCodePrefix::PLE0117 => vec![CheckCode::PLE0117],
|
||||
CheckCodePrefix::PLE0118 => vec![CheckCode::PLE0118],
|
||||
CheckCodePrefix::PLE1 => vec![CheckCode::PLE1142],
|
||||
CheckCodePrefix::PLE11 => vec![CheckCode::PLE1142],
|
||||
CheckCodePrefix::PLE114 => vec![CheckCode::PLE1142],
|
||||
@@ -1262,11 +1382,14 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::PLR1701 => vec![CheckCode::PLR1701],
|
||||
CheckCodePrefix::PLR172 => vec![CheckCode::PLR1722],
|
||||
CheckCodePrefix::PLR1722 => vec![CheckCode::PLR1722],
|
||||
CheckCodePrefix::PLW => vec![CheckCode::PLW0120],
|
||||
CheckCodePrefix::PLW0 => vec![CheckCode::PLW0120],
|
||||
CheckCodePrefix::PLW => vec![CheckCode::PLW0120, CheckCode::PLW0602],
|
||||
CheckCodePrefix::PLW0 => vec![CheckCode::PLW0120, CheckCode::PLW0602],
|
||||
CheckCodePrefix::PLW01 => vec![CheckCode::PLW0120],
|
||||
CheckCodePrefix::PLW012 => vec![CheckCode::PLW0120],
|
||||
CheckCodePrefix::PLW0120 => vec![CheckCode::PLW0120],
|
||||
CheckCodePrefix::PLW06 => vec![CheckCode::PLW0602],
|
||||
CheckCodePrefix::PLW060 => vec![CheckCode::PLW0602],
|
||||
CheckCodePrefix::PLW0602 => vec![CheckCode::PLW0602],
|
||||
CheckCodePrefix::Q => vec![
|
||||
CheckCode::Q000,
|
||||
CheckCode::Q001,
|
||||
@@ -1379,54 +1502,82 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T201 => vec![CheckCode::T201],
|
||||
CheckCodePrefix::T203 => vec![CheckCode::T203],
|
||||
CheckCodePrefix::U => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U0 => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U00 => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
],
|
||||
CheckCodePrefix::TID => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::TID2 => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::TID25 => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::TID252 => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::U => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U` has been remapped to `UP`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U0 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U0` has been remapped to `UP0`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U00 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U00` has been remapped to `UP00`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U001 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U001` has been renamed to `UP001`".bold()
|
||||
"`U001` has been remapped to `UP001`".bold()
|
||||
);
|
||||
vec![CheckCode::UP001]
|
||||
}
|
||||
@@ -1435,7 +1586,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U003` has been renamed to `UP003`".bold()
|
||||
"`U003` has been remapped to `UP003`".bold()
|
||||
);
|
||||
vec![CheckCode::UP003]
|
||||
}
|
||||
@@ -1444,7 +1595,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U004` has been renamed to `UP004`".bold()
|
||||
"`U004` has been remapped to `UP004`".bold()
|
||||
);
|
||||
vec![CheckCode::UP004]
|
||||
}
|
||||
@@ -1453,7 +1604,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U005` has been renamed to `UP005`".bold()
|
||||
"`U005` has been remapped to `UP005`".bold()
|
||||
);
|
||||
vec![CheckCode::UP005]
|
||||
}
|
||||
@@ -1462,7 +1613,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U006` has been renamed to `UP006`".bold()
|
||||
"`U006` has been remapped to `UP006`".bold()
|
||||
);
|
||||
vec![CheckCode::UP006]
|
||||
}
|
||||
@@ -1471,7 +1622,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U007` has been renamed to `UP007`".bold()
|
||||
"`U007` has been remapped to `UP007`".bold()
|
||||
);
|
||||
vec![CheckCode::UP007]
|
||||
}
|
||||
@@ -1480,7 +1631,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U008` has been renamed to `UP008`".bold()
|
||||
"`U008` has been remapped to `UP008`".bold()
|
||||
);
|
||||
vec![CheckCode::UP008]
|
||||
}
|
||||
@@ -1489,24 +1640,32 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U009` has been renamed to `UP009`".bold()
|
||||
"`U009` has been remapped to `UP009`".bold()
|
||||
);
|
||||
vec![CheckCode::UP009]
|
||||
}
|
||||
CheckCodePrefix::U01 => vec![
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U01 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U01` has been remapped to `UP01`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U010 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U010` has been renamed to `UP010`".bold()
|
||||
"`U010` has been remapped to `UP010`".bold()
|
||||
);
|
||||
vec![CheckCode::UP010]
|
||||
}
|
||||
@@ -1515,7 +1674,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U011` has been renamed to `UP011`".bold()
|
||||
"`U011` has been remapped to `UP011`".bold()
|
||||
);
|
||||
vec![CheckCode::UP011]
|
||||
}
|
||||
@@ -1524,7 +1683,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U012` has been renamed to `UP012`".bold()
|
||||
"`U012` has been remapped to `UP012`".bold()
|
||||
);
|
||||
vec![CheckCode::UP012]
|
||||
}
|
||||
@@ -1533,7 +1692,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U013` has been renamed to `UP013`".bold()
|
||||
"`U013` has been remapped to `UP013`".bold()
|
||||
);
|
||||
vec![CheckCode::UP013]
|
||||
}
|
||||
@@ -1542,7 +1701,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U014` has been renamed to `UP014`".bold()
|
||||
"`U014` has been remapped to `UP014`".bold()
|
||||
);
|
||||
vec![CheckCode::UP014]
|
||||
}
|
||||
@@ -1551,7 +1710,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U015` has been renamed to `UP015`".bold()
|
||||
"`U015` has been remapped to `UP015`".bold()
|
||||
);
|
||||
vec![CheckCode::UP015]
|
||||
}
|
||||
@@ -1698,6 +1857,14 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::ANN4 => SuffixLength::One,
|
||||
CheckCodePrefix::ANN40 => SuffixLength::Two,
|
||||
CheckCodePrefix::ANN401 => SuffixLength::Three,
|
||||
CheckCodePrefix::ARG => SuffixLength::Zero,
|
||||
CheckCodePrefix::ARG0 => SuffixLength::One,
|
||||
CheckCodePrefix::ARG00 => SuffixLength::Two,
|
||||
CheckCodePrefix::ARG001 => SuffixLength::Three,
|
||||
CheckCodePrefix::ARG002 => SuffixLength::Three,
|
||||
CheckCodePrefix::ARG003 => SuffixLength::Three,
|
||||
CheckCodePrefix::ARG004 => SuffixLength::Three,
|
||||
CheckCodePrefix::ARG005 => SuffixLength::Three,
|
||||
CheckCodePrefix::B => SuffixLength::Zero,
|
||||
CheckCodePrefix::B0 => SuffixLength::One,
|
||||
CheckCodePrefix::B00 => SuffixLength::Two,
|
||||
@@ -1793,6 +1960,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::D3 => SuffixLength::One,
|
||||
CheckCodePrefix::D30 => SuffixLength::Two,
|
||||
CheckCodePrefix::D300 => SuffixLength::Three,
|
||||
CheckCodePrefix::D301 => SuffixLength::Three,
|
||||
CheckCodePrefix::D4 => SuffixLength::One,
|
||||
CheckCodePrefix::D40 => SuffixLength::Two,
|
||||
CheckCodePrefix::D400 => SuffixLength::Three,
|
||||
@@ -1897,6 +2065,8 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F72 => SuffixLength::Two,
|
||||
CheckCodePrefix::F722 => SuffixLength::Three,
|
||||
CheckCodePrefix::F8 => SuffixLength::One,
|
||||
CheckCodePrefix::F81 => SuffixLength::Two,
|
||||
CheckCodePrefix::F811 => SuffixLength::Three,
|
||||
CheckCodePrefix::F82 => SuffixLength::Two,
|
||||
CheckCodePrefix::F821 => SuffixLength::Three,
|
||||
CheckCodePrefix::F822 => SuffixLength::Three,
|
||||
@@ -1905,6 +2075,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::F831 => SuffixLength::Three,
|
||||
CheckCodePrefix::F84 => SuffixLength::Two,
|
||||
CheckCodePrefix::F841 => SuffixLength::Three,
|
||||
CheckCodePrefix::F842 => SuffixLength::Three,
|
||||
CheckCodePrefix::F9 => SuffixLength::One,
|
||||
CheckCodePrefix::F90 => SuffixLength::Two,
|
||||
CheckCodePrefix::F901 => SuffixLength::Three,
|
||||
@@ -1925,6 +2096,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::ICN0 => SuffixLength::One,
|
||||
CheckCodePrefix::ICN00 => SuffixLength::Two,
|
||||
CheckCodePrefix::ICN001 => SuffixLength::Three,
|
||||
CheckCodePrefix::M => SuffixLength::Zero,
|
||||
CheckCodePrefix::M0 => SuffixLength::One,
|
||||
CheckCodePrefix::M001 => SuffixLength::Three,
|
||||
CheckCodePrefix::N => SuffixLength::Zero,
|
||||
CheckCodePrefix::N8 => SuffixLength::One,
|
||||
CheckCodePrefix::N80 => SuffixLength::Two,
|
||||
@@ -1962,6 +2136,11 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::PLC300 => SuffixLength::Three,
|
||||
CheckCodePrefix::PLC3002 => SuffixLength::Four,
|
||||
CheckCodePrefix::PLE => SuffixLength::Zero,
|
||||
CheckCodePrefix::PLE0 => SuffixLength::One,
|
||||
CheckCodePrefix::PLE01 => SuffixLength::Two,
|
||||
CheckCodePrefix::PLE011 => SuffixLength::Three,
|
||||
CheckCodePrefix::PLE0117 => SuffixLength::Four,
|
||||
CheckCodePrefix::PLE0118 => SuffixLength::Four,
|
||||
CheckCodePrefix::PLE1 => SuffixLength::One,
|
||||
CheckCodePrefix::PLE11 => SuffixLength::Two,
|
||||
CheckCodePrefix::PLE114 => SuffixLength::Three,
|
||||
@@ -1985,6 +2164,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::PLW01 => SuffixLength::Two,
|
||||
CheckCodePrefix::PLW012 => SuffixLength::Three,
|
||||
CheckCodePrefix::PLW0120 => SuffixLength::Four,
|
||||
CheckCodePrefix::PLW06 => SuffixLength::Two,
|
||||
CheckCodePrefix::PLW060 => SuffixLength::Three,
|
||||
CheckCodePrefix::PLW0602 => SuffixLength::Four,
|
||||
CheckCodePrefix::Q => SuffixLength::Zero,
|
||||
CheckCodePrefix::Q0 => SuffixLength::One,
|
||||
CheckCodePrefix::Q00 => SuffixLength::Two,
|
||||
@@ -2029,6 +2211,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::T20 => SuffixLength::Two,
|
||||
CheckCodePrefix::T201 => SuffixLength::Three,
|
||||
CheckCodePrefix::T203 => SuffixLength::Three,
|
||||
CheckCodePrefix::TID => SuffixLength::Zero,
|
||||
CheckCodePrefix::TID2 => SuffixLength::One,
|
||||
CheckCodePrefix::TID25 => SuffixLength::Two,
|
||||
CheckCodePrefix::TID252 => SuffixLength::Three,
|
||||
CheckCodePrefix::U => SuffixLength::Zero,
|
||||
CheckCodePrefix::U0 => SuffixLength::One,
|
||||
CheckCodePrefix::U00 => SuffixLength::Two,
|
||||
@@ -2096,6 +2282,7 @@ impl CheckCodePrefix {
|
||||
pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::ANN,
|
||||
CheckCodePrefix::ARG,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
@@ -2117,7 +2304,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::RUF,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::TID,
|
||||
CheckCodePrefix::UP,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
|
||||
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "UR\"\"\"", "UR'''", "Ur\"\"\"",
|
||||
"Ur'''", "U\"\"\"", "U'''", "uR\"\"\"", "uR'''", "R\"\"\"", "R'''", "\"\"\"", "'''",
|
||||
];
|
||||
|
||||
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];
|
||||
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &[
|
||||
"ur\"", "ur'", "u\"", "u'", "r\"", "r'", "ur\"", "ur'", "u\"", "u'", "r\"", "r'", "UR\"",
|
||||
"UR'", "Ur\"", "Ur'", "U\"", "U'", "uR\"", "uR'", "R\"", "R'", "\"", "'",
|
||||
];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use rustpython_ast::{Expr, Stmt};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DefinitionKind<'a> {
|
||||
Module,
|
||||
Package,
|
||||
@@ -17,6 +19,15 @@ pub struct Definition<'a> {
|
||||
pub docstring: Option<&'a Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Docstring<'a> {
|
||||
pub kind: DefinitionKind<'a>,
|
||||
pub expr: &'a Expr,
|
||||
pub contents: &'a Cow<'a, str>,
|
||||
pub body: &'a str,
|
||||
pub indentation: &'a Cow<'a, str>,
|
||||
}
|
||||
|
||||
pub enum Documentable {
|
||||
Class,
|
||||
Function,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::docstrings::styles::SectionStyle;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SectionContext<'a> {
|
||||
pub(crate) section_name: String,
|
||||
pub(crate) section_name: &'a str,
|
||||
pub(crate) previous_line: &'a str,
|
||||
pub(crate) line: &'a str,
|
||||
pub(crate) following_lines: &'a [&'a str],
|
||||
@@ -22,7 +22,7 @@ fn is_docstring_section(context: &SectionContext) -> bool {
|
||||
let section_name_suffix = context
|
||||
.line
|
||||
.trim()
|
||||
.strip_prefix(&context.section_name)
|
||||
.strip_prefix(context.section_name)
|
||||
.unwrap()
|
||||
.trim();
|
||||
let this_looks_like_a_section_name =
|
||||
|
||||
@@ -4,7 +4,7 @@ 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"
|
||||
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());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use rustpython_ast::{Arguments, Expr, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::cast;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
use crate::visibility;
|
||||
@@ -32,7 +33,7 @@ pub fn overloaded_name(checker: &Checker, definition: &Definition) -> Option<Str
|
||||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, stmt) {
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
@@ -50,7 +51,7 @@ pub fn is_overload_impl(checker: &Checker, definition: &Definition, overloaded_n
|
||||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, stmt) {
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
false
|
||||
} else {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::ast::{cast, visitor};
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{CheckCode, CheckKind};
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
@@ -192,7 +192,10 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
.chain(args.kwonlyargs.iter())
|
||||
.skip(
|
||||
// If this is a non-static method, skip `cls` or `self`.
|
||||
usize::from(!visibility::is_staticmethod(checker, stmt)),
|
||||
usize::from(!visibility::is_staticmethod(
|
||||
checker,
|
||||
cast::decorator_list(stmt),
|
||||
)),
|
||||
)
|
||||
{
|
||||
// ANN401 for dynamically typed arguments
|
||||
@@ -264,10 +267,10 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
}
|
||||
|
||||
// ANN101, ANN102
|
||||
if !visibility::is_staticmethod(checker, stmt) {
|
||||
if !visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||
if let Some(arg) = args.args.first() {
|
||||
if arg.node.annotation.is_none() {
|
||||
if visibility::is_classmethod(checker, stmt) {
|
||||
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.enabled.contains(&CheckCode::ANN102) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MissingTypeCls(arg.node.arg.to_string()),
|
||||
@@ -300,14 +303,14 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
return;
|
||||
}
|
||||
|
||||
if visibility::is_classmethod(checker, stmt) {
|
||||
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.enabled.contains(&CheckCode::ANN206) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MissingReturnTypeClassMethod(name.to_string()),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
} else if visibility::is_staticmethod(checker, stmt) {
|
||||
} else if visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.enabled.contains(&CheckCode::ANN205) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MissingReturnTypeStaticMethod(name.to_string()),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{
|
||||
Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location, Stmt,
|
||||
};
|
||||
@@ -27,9 +26,9 @@ fn duplicate_handler_exceptions<'a>(
|
||||
checker: &mut Checker,
|
||||
expr: &'a Expr,
|
||||
elts: &'a [Expr],
|
||||
) -> BTreeSet<Vec<&'a str>> {
|
||||
let mut seen: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
let mut duplicates: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
) -> FxHashSet<Vec<&'a str>> {
|
||||
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut unique_elts: Vec<&Expr> = Vec::default();
|
||||
for type_ in elts {
|
||||
let call_path = helpers::collect_call_paths(type_);
|
||||
@@ -76,8 +75,8 @@ fn duplicate_handler_exceptions<'a>(
|
||||
}
|
||||
|
||||
pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Excepthandler]) {
|
||||
let mut seen: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
let mut duplicates: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
for handler in handlers {
|
||||
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
|
||||
continue;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::Stmt;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
@@ -10,7 +9,7 @@ pub fn check_conventional_import(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: Option<&str>,
|
||||
conventions: &BTreeMap<String, String>,
|
||||
conventions: &FxHashMap<String, String>,
|
||||
) -> Option<Check> {
|
||||
let mut is_valid_import = true;
|
||||
if let Some(expected_alias) = conventions.get(name) {
|
||||
|
||||
@@ -3,10 +3,11 @@ pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
@@ -33,7 +34,7 @@ mod tests {
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: None,
|
||||
extend_aliases: Some(BTreeMap::from([
|
||||
extend_aliases: Some(FxHashMap::from_iter([
|
||||
("dask.array".to_string(), "da".to_string()),
|
||||
("dask.dataframe".to_string(), "dd".to_string()),
|
||||
])),
|
||||
@@ -56,7 +57,7 @@ mod tests {
|
||||
flake8_import_conventions:
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: Some(BTreeMap::from([
|
||||
aliases: Some(FxHashMap::from_iter([
|
||||
("altair".to_string(), "alt".to_string()),
|
||||
("matplotlib.pyplot".to_string(), "plt".to_string()),
|
||||
("pandas".to_string(), "pd".to_string()),
|
||||
@@ -83,7 +84,7 @@ mod tests {
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: None,
|
||||
extend_aliases: Some(BTreeMap::from([(
|
||||
extend_aliases: Some(FxHashMap::from_iter([(
|
||||
"numpy".to_string(),
|
||||
"nmp".to_string(),
|
||||
)])),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
//! Settings for import conventions.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
|
||||
@@ -20,7 +22,7 @@ pub struct Options {
|
||||
doc = "The conventional aliases for imports. These aliases can be extended by the \
|
||||
`extend_aliases` option.",
|
||||
default = r#"{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}"#,
|
||||
value_type = "BTreeMap<String, String>",
|
||||
value_type = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
# Declare the default aliases.
|
||||
altair = "alt"
|
||||
@@ -30,33 +32,41 @@ pub struct Options {
|
||||
seaborn = "sns"
|
||||
"#
|
||||
)]
|
||||
pub aliases: Option<BTreeMap<String, String>>,
|
||||
pub aliases: Option<FxHashMap<String, String>>,
|
||||
#[option(
|
||||
doc = "A mapping of modules to their conventional import aliases. These aliases will be \
|
||||
added to the `aliases` mapping.",
|
||||
default = r#"{}"#,
|
||||
value_type = "BTreeMap<String, String>",
|
||||
value_type = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
# Declare a custom alias for the `matplotlib` module.
|
||||
"dask.dataframe" = "dd"
|
||||
"#
|
||||
)]
|
||||
pub extend_aliases: Option<BTreeMap<String, String>>,
|
||||
pub extend_aliases: Option<FxHashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub aliases: BTreeMap<String, String>,
|
||||
pub aliases: FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
fn default_aliases() -> BTreeMap<String, String> {
|
||||
impl Hash for Settings {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for value in self.aliases.iter().sorted() {
|
||||
value.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_aliases() -> FxHashMap<String, String> {
|
||||
CONVENTIONAL_ALIASES
|
||||
.iter()
|
||||
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
.collect::<FxHashMap<_, _>>()
|
||||
}
|
||||
|
||||
fn resolve_aliases(options: Options) -> BTreeMap<String, String> {
|
||||
fn resolve_aliases(options: Options) -> FxHashMap<String, String> {
|
||||
let mut aliases = match options.aliases {
|
||||
Some(options_aliases) => options_aliases,
|
||||
None => default_aliases(),
|
||||
|
||||
@@ -19,24 +19,14 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
||||
};
|
||||
|
||||
if checker.patch(check.kind.code()) {
|
||||
let context = checker.binding_context();
|
||||
if matches!(
|
||||
checker.parents[context.defined_by].node,
|
||||
StmtKind::Expr { .. }
|
||||
) {
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
.iter()
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
match helpers::remove_stmt(
|
||||
checker.parents[context.defined_by],
|
||||
context.defined_in.map(|index| checker.parents[index]),
|
||||
&deleted,
|
||||
) {
|
||||
let defined_by = checker.current_parent();
|
||||
let defined_in = checker.current_grandparent();
|
||||
if matches!(defined_by.0.node, StmtKind::Expr { .. }) {
|
||||
let deleted: Vec<&Stmt> = checker.deletions.iter().map(|node| node.0).collect();
|
||||
match helpers::remove_stmt(defined_by.0, defined_in.map(|node| node.0), &deleted) {
|
||||
Ok(fix) => {
|
||||
if fix.content.is_empty() || fix.content == "pass" {
|
||||
checker.deletions.insert(context.defined_by);
|
||||
checker.deletions.insert(defined_by.clone());
|
||||
}
|
||||
check.amend(fix);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ pub struct Stack<'a> {
|
||||
#[derive(Default)]
|
||||
pub struct ReturnVisitor<'a> {
|
||||
pub stack: Stack<'a>,
|
||||
// If we're in an f-string, the location of the defining expression.
|
||||
in_f_string: Option<Location>,
|
||||
}
|
||||
|
||||
impl<'a> ReturnVisitor<'a> {
|
||||
@@ -34,7 +36,7 @@ impl<'a> ReturnVisitor<'a> {
|
||||
.assigns
|
||||
.entry(id)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(expr.location);
|
||||
.push(self.in_f_string.unwrap_or(expr.location));
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
@@ -70,7 +72,7 @@ impl<'a> Visitor<'a> for ReturnVisitor<'a> {
|
||||
.refs
|
||||
.entry(id)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(value.location);
|
||||
.push(self.in_f_string.unwrap_or(value.location));
|
||||
}
|
||||
|
||||
visitor::walk_expr(self, value);
|
||||
@@ -111,7 +113,13 @@ impl<'a> Visitor<'a> for ReturnVisitor<'a> {
|
||||
.refs
|
||||
.entry(id)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(expr.location);
|
||||
.push(self.in_f_string.unwrap_or(expr.location));
|
||||
}
|
||||
ExprKind::JoinedStr { .. } => {
|
||||
let prev_in_f_string = self.in_f_string;
|
||||
self.in_f_string = Some(expr.location);
|
||||
visitor::walk_expr(self, expr);
|
||||
self.in_f_string = prev_in_f_string;
|
||||
}
|
||||
_ => visitor::walk_expr(self, expr),
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ mod tests {
|
||||
#[test]
|
||||
fn ban_parent_imports() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_tidy_imports/I252.py"),
|
||||
Path::new("./resources/test/fixtures/flake8_tidy_imports/TID252.py"),
|
||||
&Settings {
|
||||
flake8_tidy_imports: flake8_tidy_imports::settings::Settings {
|
||||
ban_relative_imports: Strictness::Parents,
|
||||
},
|
||||
..Settings::for_rules(vec![CheckCode::I252])
|
||||
..Settings::for_rules(vec![CheckCode::TID252])
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
@@ -32,12 +32,12 @@ mod tests {
|
||||
#[test]
|
||||
fn ban_all_imports() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_tidy_imports/I252.py"),
|
||||
Path::new("./resources/test/fixtures/flake8_tidy_imports/TID252.py"),
|
||||
&Settings {
|
||||
flake8_tidy_imports: flake8_tidy_imports::settings::Settings {
|
||||
ban_relative_imports: Strictness::All,
|
||||
},
|
||||
..Settings::for_rules(vec![CheckCode::I252])
|
||||
..Settings::for_rules(vec![CheckCode::TID252])
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
|
||||
19
src/flake8_unused_arguments/helpers.rs
Normal file
19
src/flake8_unused_arguments/helpers.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use rustpython_ast::{Constant, ExprKind, Stmt, StmtKind};
|
||||
|
||||
pub fn is_empty(body: &[Stmt]) -> bool {
|
||||
match &body {
|
||||
[] => true,
|
||||
// Also allow: raise NotImplementedError, raise NotImplemented
|
||||
[stmt] => match &stmt.node {
|
||||
StmtKind::Pass => true,
|
||||
StmtKind::Expr { value } => match &value.node {
|
||||
ExprKind::Constant { value, .. } => {
|
||||
matches!(value, Constant::Str(_) | Constant::Ellipsis)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
35
src/flake8_unused_arguments/mod.rs
Normal file
35
src/flake8_unused_arguments/mod.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
mod helpers;
|
||||
pub mod plugins;
|
||||
mod types;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(CheckCode::ARG001, Path::new("ARG.py"); "ARG001")]
|
||||
#[test_case(CheckCode::ARG002, Path::new("ARG.py"); "ARG002")]
|
||||
#[test_case(CheckCode::ARG003, Path::new("ARG.py"); "ARG003")]
|
||||
#[test_case(CheckCode::ARG004, Path::new("ARG.py"); "ARG004")]
|
||||
#[test_case(CheckCode::ARG005, Path::new("ARG.py"); "ARG005")]
|
||||
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_unused_arguments")
|
||||
.join(path)
|
||||
.as_path(),
|
||||
&settings::Settings::for_rule(check_code),
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
199
src/flake8_unused_arguments/plugins.rs
Normal file
199
src/flake8_unused_arguments/plugins.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
use std::iter;
|
||||
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::{Arg, Arguments};
|
||||
|
||||
use crate::ast::function_type;
|
||||
use crate::ast::function_type::FunctionType;
|
||||
use crate::ast::helpers::collect_arg_names;
|
||||
use crate::ast::types::{Binding, BindingKind, FunctionDef, Lambda, Scope, ScopeKind};
|
||||
use crate::check_ast::Checker;
|
||||
use crate::flake8_unused_arguments::helpers;
|
||||
use crate::flake8_unused_arguments::types::Argumentable;
|
||||
use crate::{visibility, Check};
|
||||
|
||||
/// Check a plain function for unused arguments.
|
||||
fn function(
|
||||
argumentable: &Argumentable,
|
||||
args: &Arguments,
|
||||
values: &FxHashMap<&str, usize>,
|
||||
bindings: &[Binding],
|
||||
dummy_variable_rgx: &Regex,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
for arg_name in collect_arg_names(args) {
|
||||
if let Some(binding) = values.get(arg_name).map(|index| &bindings[*index]) {
|
||||
if binding.used.is_none()
|
||||
&& matches!(binding.kind, BindingKind::Argument)
|
||||
&& !dummy_variable_rgx.is_match(arg_name)
|
||||
{
|
||||
checks.push(Check::new(
|
||||
argumentable.check_for(arg_name.to_string()),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
checks
|
||||
}
|
||||
|
||||
/// Check a method for unused arguments.
|
||||
fn method(
|
||||
argumentable: &Argumentable,
|
||||
args: &Arguments,
|
||||
values: &FxHashMap<&str, usize>,
|
||||
bindings: &[Binding],
|
||||
dummy_variable_rgx: &Regex,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
for arg in args
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(args.args.iter())
|
||||
.skip(1)
|
||||
.chain(args.kwonlyargs.iter())
|
||||
.chain(iter::once::<Option<&Arg>>(args.vararg.as_deref()).flatten())
|
||||
.chain(iter::once::<Option<&Arg>>(args.kwarg.as_deref()).flatten())
|
||||
{
|
||||
if let Some(binding) = values
|
||||
.get(&arg.node.arg.as_str())
|
||||
.map(|index| &bindings[*index])
|
||||
{
|
||||
if binding.used.is_none()
|
||||
&& matches!(binding.kind, BindingKind::Argument)
|
||||
&& !dummy_variable_rgx.is_match(arg.node.arg.as_str())
|
||||
{
|
||||
checks.push(Check::new(
|
||||
argumentable.check_for(arg.node.arg.to_string()),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
checks
|
||||
}
|
||||
|
||||
/// ARG001, ARG002, ARG003, ARG004, ARG005
|
||||
pub fn unused_arguments(
|
||||
checker: &Checker,
|
||||
parent: &Scope,
|
||||
scope: &Scope,
|
||||
bindings: &[Binding],
|
||||
) -> Vec<Check> {
|
||||
match &scope.kind {
|
||||
ScopeKind::Function(FunctionDef {
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
decorator_list,
|
||||
..
|
||||
}) => {
|
||||
match function_type::classify(
|
||||
parent,
|
||||
name,
|
||||
decorator_list,
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
&checker.settings.pep8_naming.classmethod_decorators,
|
||||
&checker.settings.pep8_naming.staticmethod_decorators,
|
||||
) {
|
||||
FunctionType::Function => {
|
||||
if checker
|
||||
.settings
|
||||
.enabled
|
||||
.contains(Argumentable::Function.check_code())
|
||||
{
|
||||
function(
|
||||
&Argumentable::Function,
|
||||
args,
|
||||
&scope.values,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
FunctionType::Method => {
|
||||
if checker
|
||||
.settings
|
||||
.enabled
|
||||
.contains(Argumentable::Method.check_code())
|
||||
&& !helpers::is_empty(body)
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
{
|
||||
method(
|
||||
&Argumentable::Method,
|
||||
args,
|
||||
&scope.values,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
FunctionType::ClassMethod => {
|
||||
if checker
|
||||
.settings
|
||||
.enabled
|
||||
.contains(Argumentable::ClassMethod.check_code())
|
||||
&& !helpers::is_empty(body)
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
{
|
||||
method(
|
||||
&Argumentable::ClassMethod,
|
||||
args,
|
||||
&scope.values,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
FunctionType::StaticMethod => {
|
||||
if checker
|
||||
.settings
|
||||
.enabled
|
||||
.contains(Argumentable::StaticMethod.check_code())
|
||||
&& !helpers::is_empty(body)
|
||||
&& !visibility::is_abstract(checker, decorator_list)
|
||||
&& !visibility::is_override(checker, decorator_list)
|
||||
{
|
||||
function(
|
||||
&Argumentable::StaticMethod,
|
||||
args,
|
||||
&scope.values,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ScopeKind::Lambda(Lambda { args, .. }) => {
|
||||
if checker
|
||||
.settings
|
||||
.enabled
|
||||
.contains(Argumentable::Lambda.check_code())
|
||||
{
|
||||
function(
|
||||
&Argumentable::Lambda,
|
||||
args,
|
||||
&scope.values,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
_ => unreachable!("Expected ScopeKind::Function | ScopeKind::Lambda"),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
source: src/flake8_unused_arguments/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedFunctionArgument: self
|
||||
location:
|
||||
row: 8
|
||||
column: 6
|
||||
end_location:
|
||||
row: 8
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 8
|
||||
column: 12
|
||||
end_location:
|
||||
row: 8
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: cls
|
||||
location:
|
||||
row: 12
|
||||
column: 6
|
||||
end_location:
|
||||
row: 12
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 12
|
||||
column: 11
|
||||
end_location:
|
||||
row: 12
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: self
|
||||
location:
|
||||
row: 16
|
||||
column: 6
|
||||
end_location:
|
||||
row: 16
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 16
|
||||
column: 12
|
||||
end_location:
|
||||
row: 16
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: cls
|
||||
location:
|
||||
row: 20
|
||||
column: 6
|
||||
end_location:
|
||||
row: 20
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedFunctionArgument: x
|
||||
location:
|
||||
row: 20
|
||||
column: 11
|
||||
end_location:
|
||||
row: 20
|
||||
column: 12
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: src/flake8_unused_arguments/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedMethodArgument: x
|
||||
location:
|
||||
row: 34
|
||||
column: 16
|
||||
end_location:
|
||||
row: 34
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedMethodArgument: x
|
||||
location:
|
||||
row: 37
|
||||
column: 19
|
||||
end_location:
|
||||
row: 37
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedMethodArgument: x
|
||||
location:
|
||||
row: 40
|
||||
column: 15
|
||||
end_location:
|
||||
row: 40
|
||||
column: 16
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/flake8_unused_arguments/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedClassMethodArgument: x
|
||||
location:
|
||||
row: 44
|
||||
column: 15
|
||||
end_location:
|
||||
row: 44
|
||||
column: 16
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: src/flake8_unused_arguments/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedStaticMethodArgument: cls
|
||||
location:
|
||||
row: 48
|
||||
column: 10
|
||||
end_location:
|
||||
row: 48
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedStaticMethodArgument: x
|
||||
location:
|
||||
row: 48
|
||||
column: 15
|
||||
end_location:
|
||||
row: 48
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
UnusedStaticMethodArgument: x
|
||||
location:
|
||||
row: 52
|
||||
column: 10
|
||||
end_location:
|
||||
row: 52
|
||||
column: 11
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/flake8_unused_arguments/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnusedLambdaArgument: x
|
||||
location:
|
||||
row: 27
|
||||
column: 7
|
||||
end_location:
|
||||
row: 27
|
||||
column: 8
|
||||
fix: ~
|
||||
|
||||
32
src/flake8_unused_arguments/types.rs
Normal file
32
src/flake8_unused_arguments/types.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use crate::checks::{CheckCode, CheckKind};
|
||||
|
||||
/// An AST node that can contain arguments.
|
||||
pub enum Argumentable {
|
||||
Function,
|
||||
Method,
|
||||
ClassMethod,
|
||||
StaticMethod,
|
||||
Lambda,
|
||||
}
|
||||
|
||||
impl Argumentable {
|
||||
pub fn check_for(&self, name: String) -> CheckKind {
|
||||
match self {
|
||||
Argumentable::Function => CheckKind::UnusedFunctionArgument(name),
|
||||
Argumentable::Method => CheckKind::UnusedMethodArgument(name),
|
||||
Argumentable::ClassMethod => CheckKind::UnusedClassMethodArgument(name),
|
||||
Argumentable::StaticMethod => CheckKind::UnusedStaticMethodArgument(name),
|
||||
Argumentable::Lambda => CheckKind::UnusedLambdaArgument(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_code(&self) -> &CheckCode {
|
||||
match self {
|
||||
Argumentable::Function => &CheckCode::ARG001,
|
||||
Argumentable::Method => &CheckCode::ARG002,
|
||||
Argumentable::ClassMethod => &CheckCode::ARG003,
|
||||
Argumentable::StaticMethod => &CheckCode::ARG004,
|
||||
Argumentable::Lambda => &CheckCode::ARG005,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -8,6 +7,7 @@ use anyhow::{anyhow, Result};
|
||||
use globset::GlobMatcher;
|
||||
use log::debug;
|
||||
use path_absolutize::{path_dedot, Absolutize};
|
||||
use rustc_hash::FxHashSet;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
@@ -83,8 +83,8 @@ pub fn iter_python_files<'a>(
|
||||
/// Create tree set with codes matching the pattern/code pairs.
|
||||
pub(crate) fn ignores_from_path<'a>(
|
||||
path: &Path,
|
||||
pattern_code_pairs: &'a [(GlobMatcher, GlobMatcher, BTreeSet<CheckCode>)],
|
||||
) -> Result<BTreeSet<&'a CheckCode>> {
|
||||
pattern_code_pairs: &'a [(GlobMatcher, GlobMatcher, FxHashSet<CheckCode>)],
|
||||
) -> Result<FxHashSet<&'a CheckCode>> {
|
||||
let (file_path, file_basename) = extract_path_names(path)?;
|
||||
Ok(pattern_code_pairs
|
||||
.iter()
|
||||
|
||||
@@ -559,6 +559,7 @@ mod tests {
|
||||
#[test_case(Path::new("force_wrap_aliases.py"))]
|
||||
#[test_case(Path::new("import_from_after_import.py"))]
|
||||
#[test_case(Path::new("insert_empty_lines.py"))]
|
||||
#[test_case(Path::new("insert_empty_lines.pyi"))]
|
||||
#[test_case(Path::new("leading_prefix.py"))]
|
||||
#[test_case(Path::new("no_reorder_within_section.py"))]
|
||||
#[test_case(Path::new("no_wrap_star.py"))]
|
||||
|
||||
@@ -19,14 +19,12 @@ fn extract_range(body: &[&Stmt]) -> Range {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_indentation(body: &[&Stmt], locator: &SourceCodeLocator) -> String {
|
||||
fn extract_indentation_range(body: &[&Stmt]) -> Range {
|
||||
let location = body.first().unwrap().location;
|
||||
let range = Range {
|
||||
Range {
|
||||
location: Location::new(location.row(), 0),
|
||||
end_location: location,
|
||||
};
|
||||
let existing = locator.slice_source_code_range(&range);
|
||||
leading_space(&existing)
|
||||
}
|
||||
}
|
||||
|
||||
/// I001
|
||||
@@ -36,8 +34,10 @@ pub fn check_imports(
|
||||
settings: &Settings,
|
||||
autofix: bool,
|
||||
) -> Option<Check> {
|
||||
let indentation = locator.slice_source_code_range(&extract_indentation_range(&block.imports));
|
||||
let indentation = leading_space(&indentation);
|
||||
|
||||
let range = extract_range(&block.imports);
|
||||
let indentation = extract_indentation(&block.imports, locator);
|
||||
|
||||
// Extract comments. Take care to grab any inline comments from the last line.
|
||||
let comments = comments::collect_comments(
|
||||
@@ -77,7 +77,7 @@ pub fn check_imports(
|
||||
if has_leading_content {
|
||||
content.push('\n');
|
||||
}
|
||||
content.push_str(&indent(&expected, &indentation));
|
||||
content.push_str(&indent(&expected, indentation));
|
||||
check.amend(Fix::replacement(
|
||||
content,
|
||||
// Preserve leading prefix (but put the imports on a new line).
|
||||
@@ -104,7 +104,7 @@ pub fn check_imports(
|
||||
let mut check = Check::new(CheckKind::UnsortedImports, range);
|
||||
if autofix && settings.fixable.contains(check.kind.code()) {
|
||||
check.amend(Fix::replacement(
|
||||
indent(&expected, &indentation),
|
||||
indent(&expected, indentation),
|
||||
range.location,
|
||||
range.end_location,
|
||||
));
|
||||
|
||||
@@ -32,11 +32,16 @@ pub struct Options {
|
||||
test_id as test_id
|
||||
)
|
||||
```
|
||||
|
||||
Note that this setting is only effective when combined with `combine-as-imports = true`.
|
||||
When `combine-as-imports` isn't enabled, every aliased `import from` will be given its
|
||||
own line, in which case, wrapping is not necessary.
|
||||
"#,
|
||||
default = r#"false"#,
|
||||
value_type = "bool",
|
||||
example = r#"
|
||||
force-wrap-aliases = true
|
||||
combine-as-imports = true
|
||||
"#
|
||||
)]
|
||||
pub force_wrap_aliases: Option<bool>,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 0
|
||||
fix:
|
||||
content: "import a\nimport b\n\n"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
fix:
|
||||
content: "import os\nimport sys\n\n"
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 14
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
column: 0
|
||||
fix:
|
||||
content: "import os\nimport sys\n\n"
|
||||
location:
|
||||
row: 14
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
fix:
|
||||
content: " import collections\n import typing\n\n"
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_ast::{
|
||||
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
|
||||
ExcepthandlerKind, Expr, ExprContext, Keyword, MatchCase, Operator, Pattern, Stmt, StmtKind,
|
||||
@@ -20,16 +22,18 @@ pub struct Block<'a> {
|
||||
}
|
||||
|
||||
pub struct ImportTracker<'a> {
|
||||
blocks: Vec<Block<'a>>,
|
||||
directives: &'a IsortDirectives,
|
||||
pyi: bool,
|
||||
blocks: Vec<Block<'a>>,
|
||||
split_index: usize,
|
||||
nested: bool,
|
||||
}
|
||||
|
||||
impl<'a> ImportTracker<'a> {
|
||||
pub fn new(directives: &'a IsortDirectives) -> Self {
|
||||
pub fn new(directives: &'a IsortDirectives, path: &'a Path) -> Self {
|
||||
Self {
|
||||
directives,
|
||||
pyi: path.extension().map_or(false, |ext| ext == "pyi"),
|
||||
blocks: vec![Block::default()],
|
||||
split_index: 0,
|
||||
nested: false,
|
||||
@@ -41,6 +45,34 @@ impl<'a> ImportTracker<'a> {
|
||||
self.blocks[index].imports.push(stmt);
|
||||
}
|
||||
|
||||
fn trailer_for(&self, stmt: &'a Stmt) -> Option<Trailer> {
|
||||
if self.pyi {
|
||||
// Black treats interface files differently, limiting to one newline
|
||||
// (`Trailing::Sibling`), and avoiding inserting any newlines in nested function
|
||||
// blocks.
|
||||
if self.nested
|
||||
&& matches!(
|
||||
stmt.node,
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. }
|
||||
)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(Trailer::Sibling)
|
||||
}
|
||||
} else if self.nested {
|
||||
Some(Trailer::Sibling)
|
||||
} else {
|
||||
Some(match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
StmtKind::ClassDef { .. } => Trailer::ClassDef,
|
||||
_ => Trailer::Sibling,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(&mut self, trailer: Option<Trailer>) {
|
||||
let index = self.blocks.len() - 1;
|
||||
if !self.blocks[index].imports.is_empty() {
|
||||
@@ -62,17 +94,7 @@ where
|
||||
// Track manual splits.
|
||||
while self.split_index < self.directives.splits.len() {
|
||||
if stmt.location.row() >= self.directives.splits[self.split_index] {
|
||||
self.finalize(Some(if self.nested {
|
||||
Trailer::Sibling
|
||||
} else {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
StmtKind::ClassDef { .. } => Trailer::ClassDef,
|
||||
_ => Trailer::Sibling,
|
||||
}
|
||||
}));
|
||||
self.finalize(self.trailer_for(stmt));
|
||||
self.split_index += 1;
|
||||
} else {
|
||||
break;
|
||||
@@ -87,17 +109,7 @@ where
|
||||
{
|
||||
self.track_import(stmt);
|
||||
} else {
|
||||
self.finalize(Some(if self.nested {
|
||||
Trailer::Sibling
|
||||
} else {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
StmtKind::ClassDef { .. } => Trailer::ClassDef,
|
||||
_ => Trailer::Sibling,
|
||||
}
|
||||
}));
|
||||
self.finalize(self.trailer_for(stmt));
|
||||
}
|
||||
|
||||
// Track scope.
|
||||
|
||||
@@ -54,6 +54,7 @@ mod flake8_print;
|
||||
pub mod flake8_quotes;
|
||||
mod flake8_return;
|
||||
pub mod flake8_tidy_imports;
|
||||
mod flake8_unused_arguments;
|
||||
pub mod fs;
|
||||
mod isort;
|
||||
mod lex;
|
||||
|
||||
@@ -92,6 +92,7 @@ pub(crate) fn check_path(
|
||||
&directives.isort,
|
||||
settings,
|
||||
autofix,
|
||||
path,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -133,7 +134,7 @@ pub(crate) fn check_path(
|
||||
Ok(checks)
|
||||
}
|
||||
|
||||
const MAX_ITERATIONS: usize = 1;
|
||||
const MAX_ITERATIONS: usize = 100;
|
||||
|
||||
/// Lint the source code at the given `Path`.
|
||||
pub fn lint_path(
|
||||
|
||||
@@ -392,7 +392,7 @@ fn inner_main() -> Result<ExitCode> {
|
||||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
|
||||
printer.write_once(&diagnostics)?;
|
||||
printer.write_once(&diagnostics, &autofix)?;
|
||||
}
|
||||
|
||||
// Check for updates if we're in a non-silent log level.
|
||||
|
||||
26
src/noqa.rs
26
src/noqa.rs
@@ -1,4 +1,3 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -7,8 +6,9 @@ use itertools::Itertools;
|
||||
use nohash_hasher::IntMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::checks::{Check, CheckCode, REDIRECTS};
|
||||
use crate::checks::{Check, CheckCode, CODE_REDIRECTS};
|
||||
|
||||
static NO_QA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
@@ -70,7 +70,7 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
pub fn includes(needle: &CheckCode, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.as_ref();
|
||||
haystack.iter().any(|candidate| {
|
||||
if let Some(candidate) = REDIRECTS.get(candidate) {
|
||||
if let Some(candidate) = CODE_REDIRECTS.get(candidate) {
|
||||
needle == candidate.as_ref()
|
||||
} else {
|
||||
&needle == candidate
|
||||
@@ -83,7 +83,7 @@ pub fn add_noqa(
|
||||
checks: &[Check],
|
||||
contents: &str,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &BTreeSet<String>,
|
||||
external: &FxHashSet<String>,
|
||||
) -> Result<usize> {
|
||||
let (count, output) = add_noqa_inner(checks, contents, noqa_line_for, external);
|
||||
fs::write(path, output)?;
|
||||
@@ -94,16 +94,16 @@ fn add_noqa_inner(
|
||||
checks: &[Check],
|
||||
contents: &str,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &BTreeSet<String>,
|
||||
external: &FxHashSet<String>,
|
||||
) -> (usize, String) {
|
||||
let mut matches_by_line: BTreeMap<usize, BTreeSet<&CheckCode>> = BTreeMap::new();
|
||||
let mut matches_by_line: FxHashMap<usize, FxHashSet<&CheckCode>> = FxHashMap::default();
|
||||
for (lineno, line) in contents.lines().enumerate() {
|
||||
// If we hit an exemption for the entire file, bail.
|
||||
if is_file_exempt(line) {
|
||||
return (0, contents.to_string());
|
||||
}
|
||||
|
||||
let mut codes: BTreeSet<&CheckCode> = BTreeSet::new();
|
||||
let mut codes: FxHashSet<&CheckCode> = FxHashSet::default();
|
||||
for check in checks {
|
||||
if check.location.row() == lineno + 1 {
|
||||
codes.insert(check.kind.code());
|
||||
@@ -117,7 +117,7 @@ fn add_noqa_inner(
|
||||
|
||||
if !codes.is_empty() {
|
||||
let matches = matches_by_line.entry(noqa_lineno).or_default();
|
||||
matches.append(&mut codes);
|
||||
matches.extend(codes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,9 +199,9 @@ fn add_noqa_inner(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use nohash_hasher::IntMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
@@ -227,7 +227,7 @@ mod tests {
|
||||
let checks = vec![];
|
||||
let contents = "x = 1";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 0);
|
||||
assert_eq!(output.trim(), contents.trim());
|
||||
@@ -241,7 +241,7 @@ mod tests {
|
||||
)];
|
||||
let contents = "x = 1";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(output.trim(), "x = 1 # noqa: F841".trim());
|
||||
@@ -264,7 +264,7 @@ mod tests {
|
||||
];
|
||||
let contents = "x = 1 # noqa: E741";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(output.trim(), "x = 1 # noqa: E741, F841".trim());
|
||||
@@ -287,7 +287,7 @@ mod tests {
|
||||
];
|
||||
let contents = "x = 1 # noqa";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(output.trim(), "x = 1 # noqa: E741, F841".trim());
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::function_type;
|
||||
use crate::ast::types::{Range, Scope, ScopeKind};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::pep8_naming::helpers;
|
||||
use crate::pep8_naming::helpers::FunctionType;
|
||||
use crate::pep8_naming::settings::Settings;
|
||||
use crate::python::string::{self};
|
||||
|
||||
@@ -58,15 +58,16 @@ pub fn invalid_first_argument_name_for_class_method(
|
||||
settings: &Settings,
|
||||
) -> Option<Check> {
|
||||
if !matches!(
|
||||
helpers::function_type(
|
||||
function_type::classify(
|
||||
scope,
|
||||
name,
|
||||
decorator_list,
|
||||
from_imports,
|
||||
import_aliases,
|
||||
settings,
|
||||
&settings.classmethod_decorators,
|
||||
&settings.staticmethod_decorators,
|
||||
),
|
||||
FunctionType::ClassMethod
|
||||
function_type::FunctionType::ClassMethod
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
@@ -99,15 +100,16 @@ pub fn invalid_first_argument_name_for_method(
|
||||
settings: &Settings,
|
||||
) -> Option<Check> {
|
||||
if !matches!(
|
||||
helpers::function_type(
|
||||
function_type::classify(
|
||||
scope,
|
||||
name,
|
||||
decorator_list,
|
||||
from_imports,
|
||||
import_aliases,
|
||||
settings,
|
||||
&settings.classmethod_decorators,
|
||||
&settings.staticmethod_decorators,
|
||||
),
|
||||
FunctionType::Method
|
||||
function_type::FunctionType::Method
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -1,71 +1,10 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||
use rustpython_ast::{Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers::{
|
||||
collect_call_paths, dealias_call_path, match_call_path, to_module_and_member,
|
||||
};
|
||||
use crate::ast::types::{Scope, ScopeKind};
|
||||
use crate::pep8_naming::settings::Settings;
|
||||
use crate::ast::helpers::{collect_call_paths, match_call_path};
|
||||
use crate::python::string::{is_lower, is_upper};
|
||||
|
||||
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
|
||||
|
||||
pub enum FunctionType {
|
||||
Function,
|
||||
Method,
|
||||
ClassMethod,
|
||||
StaticMethod,
|
||||
}
|
||||
|
||||
/// Classify a function based on its scope, name, and decorators.
|
||||
pub fn function_type(
|
||||
scope: &Scope,
|
||||
name: &str,
|
||||
decorator_list: &[Expr],
|
||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||
import_aliases: &FxHashMap<&str, &str>,
|
||||
settings: &Settings,
|
||||
) -> FunctionType {
|
||||
let ScopeKind::Class(scope) = &scope.kind else {
|
||||
return FunctionType::Function;
|
||||
};
|
||||
// Special-case class method, like `__new__`.
|
||||
if CLASS_METHODS.contains(&name)
|
||||
|| scope.bases.iter().any(|expr| {
|
||||
// The class itself extends a known metaclass, so all methods are class methods.
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
METACLASS_BASES
|
||||
.iter()
|
||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||
})
|
||||
|| decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a class method decorator (like `@classmethod`).
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
settings.classmethod_decorators.iter().any(|decorator| {
|
||||
let (module, member) = to_module_and_member(decorator);
|
||||
match_call_path(&call_path, module, member, from_imports)
|
||||
})
|
||||
})
|
||||
{
|
||||
FunctionType::ClassMethod
|
||||
} else if decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a static method decorator (like
|
||||
// `@staticmethod`).
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
settings.staticmethod_decorators.iter().any(|decorator| {
|
||||
let (module, member) = to_module_and_member(decorator);
|
||||
match_call_path(&call_path, module, member, from_imports)
|
||||
})
|
||||
}) {
|
||||
FunctionType::StaticMethod
|
||||
} else {
|
||||
// It's an instance method.
|
||||
FunctionType::Method
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_camelcase(name: &str) -> bool {
|
||||
!is_lower(name) && !is_upper(name) && !name.contains('_')
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use itertools::iterate;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::autofix::Fix;
|
||||
use crate::autofix::{fixer, Fix};
|
||||
use crate::checks::CheckCode;
|
||||
use crate::fs::relativize_path;
|
||||
use crate::linter::Diagnostics;
|
||||
@@ -57,15 +57,15 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn post_text(&self, num_fixable: usize) {
|
||||
fn post_text(&self, num_fixable: usize, autofix: &fixer::Mode) {
|
||||
if self.log_level >= &LogLevel::Default {
|
||||
if num_fixable > 0 {
|
||||
if num_fixable > 0 && !matches!(autofix, fixer::Mode::Apply) {
|
||||
println!("{num_fixable} potentially fixable with the --fix option.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_once(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
pub fn write_once(&self, diagnostics: &Diagnostics, autofix: &fixer::Mode) -> Result<()> {
|
||||
if matches!(self.log_level, LogLevel::Silent) {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -147,7 +147,7 @@ impl<'a> Printer<'a> {
|
||||
print_message(message);
|
||||
}
|
||||
|
||||
self.post_text(num_fixable);
|
||||
self.post_text(num_fixable, autofix);
|
||||
}
|
||||
SerializationFormat::Grouped => {
|
||||
self.pre_text(diagnostics);
|
||||
@@ -190,7 +190,7 @@ impl<'a> Printer<'a> {
|
||||
println!();
|
||||
}
|
||||
|
||||
self.post_text(num_fixable);
|
||||
self.post_text(num_fixable, autofix);
|
||||
}
|
||||
SerializationFormat::Github => {
|
||||
self.pre_text(diagnostics);
|
||||
|
||||
@@ -298,11 +298,11 @@ pub fn do_not_assign_lambda(checker: &mut Checker, target: &Expr, value: &Expr,
|
||||
{
|
||||
match function(id, args, body) {
|
||||
Ok(content) => {
|
||||
let indentation =
|
||||
&leading_space(&checker.locator.slice_source_code_range(&Range {
|
||||
location: Location::new(stmt.location.row(), 0),
|
||||
end_location: Location::new(stmt.location.row() + 1, 0),
|
||||
}));
|
||||
let first_line = checker.locator.slice_source_code_range(&Range {
|
||||
location: Location::new(stmt.location.row(), 0),
|
||||
end_location: Location::new(stmt.location.row() + 1, 0),
|
||||
});
|
||||
let indentation = &leading_space(&first_line);
|
||||
let mut indented = String::new();
|
||||
for (idx, line) in content.lines().enumerate() {
|
||||
if idx == 0 {
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::docstrings::constants;
|
||||
use crate::SourceCodeLocator;
|
||||
|
||||
/// Strip the leading and trailing quotes from a docstring.
|
||||
pub fn raw_contents(contents: &str) -> &str {
|
||||
for pattern in constants::TRIPLE_QUOTE_PREFIXES {
|
||||
if contents.starts_with(pattern) {
|
||||
return &contents[pattern.len()..contents.len() - 3];
|
||||
}
|
||||
}
|
||||
for pattern in constants::SINGLE_QUOTE_PREFIXES {
|
||||
if contents.starts_with(pattern) {
|
||||
return &contents[pattern.len()..contents.len() - 1];
|
||||
}
|
||||
}
|
||||
unreachable!("Expected docstring to start with a valid triple- or single-quote prefix")
|
||||
}
|
||||
|
||||
/// Return the leading quote string for a docstring (e.g., `"""`).
|
||||
pub fn leading_quote<'a>(docstring: &Expr, locator: &'a SourceCodeLocator) -> Option<&'a str> {
|
||||
if let Some(first_line) = locator
|
||||
.slice_source_code_range(&Range::from_located(docstring))
|
||||
.lines()
|
||||
.next()
|
||||
.map(str::to_lowercase)
|
||||
{
|
||||
pub fn leading_quote(content: &str) -> Option<&str> {
|
||||
if let Some(first_line) = content.lines().next() {
|
||||
for pattern in constants::TRIPLE_QUOTE_PREFIXES
|
||||
.iter()
|
||||
.chain(constants::SINGLE_QUOTE_PREFIXES)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mod helpers;
|
||||
pub mod helpers;
|
||||
pub mod plugins;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -37,6 +37,7 @@ mod tests {
|
||||
#[test_case(CheckCode::D214, Path::new("sections.py"); "D214")]
|
||||
#[test_case(CheckCode::D215, Path::new("sections.py"); "D215")]
|
||||
#[test_case(CheckCode::D300, Path::new("D.py"); "D300")]
|
||||
#[test_case(CheckCode::D301, Path::new("D.py"); "D301")]
|
||||
#[test_case(CheckCode::D400, Path::new("D.py"); "D400_0")]
|
||||
#[test_case(CheckCode::D400, Path::new("D400.py"); "D400_1")]
|
||||
#[test_case(CheckCode::D402, Path::new("D.py"); "D402")]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
---
|
||||
source: src/pydocstyle/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UsesRPrefixForBackslashedContent
|
||||
location:
|
||||
row: 328
|
||||
column: 4
|
||||
end_location:
|
||||
row: 328
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: UsesRPrefixForBackslashedContent
|
||||
location:
|
||||
row: 333
|
||||
column: 4
|
||||
end_location:
|
||||
row: 333
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind: UsesRPrefixForBackslashedContent
|
||||
location:
|
||||
row: 338
|
||||
column: 4
|
||||
end_location:
|
||||
row: 338
|
||||
column: 21
|
||||
fix: ~
|
||||
|
||||
@@ -7,7 +7,7 @@ use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind,
|
||||
};
|
||||
|
||||
use crate::ast::types::{BindingKind, FunctionScope, Range, Scope, ScopeKind};
|
||||
use crate::ast::types::{Binding, BindingKind, Range, Scope, ScopeKind};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::pyflakes::cformat::CFormatSummary;
|
||||
use crate::pyflakes::format::FormatSummary;
|
||||
@@ -366,12 +366,12 @@ pub fn if_tuple(test: &Expr, location: Range) -> Option<Check> {
|
||||
}
|
||||
|
||||
/// F821
|
||||
pub fn undefined_local(scopes: &[&Scope], name: &str) -> Option<Check> {
|
||||
pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &[Binding]) -> Option<Check> {
|
||||
let current = &scopes.last().expect("No current scope found");
|
||||
if matches!(current.kind, ScopeKind::Function(_)) && !current.values.contains_key(name) {
|
||||
for scope in scopes.iter().rev().skip(1) {
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if let Some(binding) = scope.values.get(name) {
|
||||
if let Some(binding) = scope.values.get(name).map(|index| &bindings[*index]) {
|
||||
if let Some((scope_id, location)) = binding.used {
|
||||
if scope_id == current.id {
|
||||
return Some(Check::new(
|
||||
@@ -388,29 +388,31 @@ pub fn undefined_local(scopes: &[&Scope], name: &str) -> Option<Check> {
|
||||
}
|
||||
|
||||
/// F841
|
||||
pub fn unused_variables(scope: &Scope, dummy_variable_rgx: &Regex) -> Vec<Check> {
|
||||
pub fn unused_variable(
|
||||
scope: &Scope,
|
||||
bindings: &[Binding],
|
||||
dummy_variable_rgx: &Regex,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(FunctionScope {
|
||||
uses_locals: true,
|
||||
..
|
||||
})
|
||||
) {
|
||||
if scope.uses_locals && matches!(scope.kind, ScopeKind::Function(..)) {
|
||||
return checks;
|
||||
}
|
||||
|
||||
for (&name, binding) in &scope.values {
|
||||
for (name, binding) in scope
|
||||
.values
|
||||
.iter()
|
||||
.map(|(name, index)| (name, &bindings[*index]))
|
||||
{
|
||||
if binding.used.is_none()
|
||||
&& matches!(binding.kind, BindingKind::Assignment)
|
||||
&& !dummy_variable_rgx.is_match(name)
|
||||
&& name != "__tracebackhide__"
|
||||
&& name != "__traceback_info__"
|
||||
&& name != "__traceback_supplement__"
|
||||
&& name != &"__tracebackhide__"
|
||||
&& name != &"__traceback_info__"
|
||||
&& name != &"__traceback_supplement__"
|
||||
{
|
||||
checks.push(Check::new(
|
||||
CheckKind::UnusedVariable(name.to_string()),
|
||||
CheckKind::UnusedVariable((*name).to_string()),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
@@ -419,6 +421,31 @@ pub fn unused_variables(scope: &Scope, dummy_variable_rgx: &Regex) -> Vec<Check>
|
||||
checks
|
||||
}
|
||||
|
||||
/// F842
|
||||
pub fn unused_annotation(
|
||||
scope: &Scope,
|
||||
bindings: &[Binding],
|
||||
dummy_variable_rgx: &Regex,
|
||||
) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
for (name, binding) in scope
|
||||
.values
|
||||
.iter()
|
||||
.map(|(name, index)| (name, &bindings[*index]))
|
||||
{
|
||||
if binding.used.is_none()
|
||||
&& matches!(binding.kind, BindingKind::Annotation)
|
||||
&& !dummy_variable_rgx.is_match(name)
|
||||
{
|
||||
checks.push(Check::new(
|
||||
CheckKind::UnusedAnnotation((*name).to_string()),
|
||||
binding.range,
|
||||
));
|
||||
}
|
||||
}
|
||||
checks
|
||||
}
|
||||
|
||||
/// F707
|
||||
pub fn default_except_not_last(handlers: &[Excepthandler]) -> Option<Check> {
|
||||
for (idx, handler) in handlers.iter().enumerate() {
|
||||
@@ -552,12 +579,13 @@ pub fn starred_expressions(
|
||||
}
|
||||
|
||||
/// F701
|
||||
pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]) -> Option<Check> {
|
||||
pub fn break_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Check> {
|
||||
let mut allowed: bool = false;
|
||||
let mut parent = stmt;
|
||||
for index in parent_stack.iter().rev() {
|
||||
let child = parent;
|
||||
parent = parents[*index];
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
@@ -567,7 +595,6 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
@@ -575,6 +602,7 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
@@ -588,16 +616,13 @@ pub fn break_outside_loop(stmt: &Stmt, parents: &[&Stmt], parent_stack: &[usize]
|
||||
}
|
||||
|
||||
/// F702
|
||||
pub fn continue_outside_loop(
|
||||
stmt: &Stmt,
|
||||
parents: &[&Stmt],
|
||||
parent_stack: &[usize],
|
||||
pub fn continue_outside_loop<'a>(
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) -> Option<Check> {
|
||||
let mut allowed: bool = false;
|
||||
let mut parent = stmt;
|
||||
for index in parent_stack.iter().rev() {
|
||||
let child = parent;
|
||||
parent = parents[*index];
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match &parent.node {
|
||||
StmtKind::For { orelse, .. }
|
||||
| StmtKind::AsyncFor { orelse, .. }
|
||||
@@ -607,7 +632,6 @@ pub fn continue_outside_loop(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
| StmtKind::ClassDef { .. } => {
|
||||
@@ -615,6 +639,7 @@ pub fn continue_outside_loop(
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
if allowed {
|
||||
|
||||
3729
src/pyflakes/mod.rs
3729
src/pyflakes/mod.rs
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user