Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
d698c6123e | ||
|
|
9579faffa8 | ||
|
|
9c6e8c7644 | ||
|
|
7abecd4f0e | ||
|
|
b8ff209af8 | ||
|
|
c5451cd8ad | ||
|
|
92b9ab3010 | ||
|
|
f2ac8c4ec2 | ||
|
|
80e2f0c92e | ||
|
|
ea550abd3c | ||
|
|
5c26777e4c | ||
|
|
6eb6b6eede |
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.165
|
||||
rev: v0.0.172
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
@@ -105,6 +105,8 @@ You may also want to add the new configuration option to the `flake8-to-ruff` to
|
||||
responsible for converting `flake8` configuration files to Ruff's TOML format. This logic
|
||||
lives in `flake8_to_ruff/src/converter.rs`.
|
||||
|
||||
To update the documentation for supported configuration options, run `cargo dev generate-options`.
|
||||
|
||||
## Release process
|
||||
|
||||
As of now, Ruff has an ad hoc release process: releases are cut with high frequency via GitHub
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.165-dev.0"
|
||||
version = "0.0.172-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1821,7 +1821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.165"
|
||||
version = "0.0.172"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1874,7 +1874,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.165"
|
||||
version = "0.0.172"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1892,7 +1892,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.161"
|
||||
version = "0.0.172"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.165"
|
||||
version = "0.0.172"
|
||||
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.172", 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)
|
||||
|
||||
201
README.md
201
README.md
@@ -68,31 +68,33 @@ of [Conda](https://docs.conda.io/en/latest/):
|
||||
|
||||
1. [Installation and Usage](#installation-and-usage)
|
||||
1. [Configuration](#configuration)
|
||||
1. [Supported Rules](#supported-rules)
|
||||
1. [Pyflakes (F)](#pyflakes)
|
||||
1. [pycodestyle (E, W)](#pycodestyle)
|
||||
1. [mccabe (C90)](#mccabe)
|
||||
1. [isort (I)](#isort)
|
||||
1. [pydocstyle (D)](#pydocstyle)
|
||||
1. [pyupgrade (UP)](#pyupgrade)
|
||||
1. [pep8-naming (N)](#pep8-naming)
|
||||
1. [flake8-2020 (YTT)](#flake8-2020)
|
||||
1. [flake8-annotations (ANN)](#flake8-annotations)
|
||||
1. [flake8-bandit (S)](#flake8-bandit)
|
||||
1. [flake8-blind-except (BLE)](#flake8-blind-except)
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
|
||||
1. [flake8-bugbear (B)](#flake8-bugbear)
|
||||
1. [flake8-builtins (A)](#flake8-builtins)
|
||||
1. [flake8-comprehensions (C4)](#flake8-comprehensions)
|
||||
1. [flake8-debugger (T10)](#flake8-debugger)
|
||||
1. [flake8-print (T20)](#flake8-print)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes)
|
||||
1. [flake8-return (RET)](#flake8-return)
|
||||
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports)
|
||||
1. [eradicate (ERA)](#eradicate)
|
||||
1. [pygrep-hooks (PGH)](#pygrep-hooks)
|
||||
1. [Pylint (PL)](#pylint)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules)
|
||||
1. [Supported Rules](#supported-rules) <!-- Begin auto-generated table of contents. -->
|
||||
1. [Pyflakes (F)](#pyflakes-f)
|
||||
1. [pycodestyle (E, W)](#pycodestyle-e-w)
|
||||
1. [mccabe (C90)](#mccabe-c90)
|
||||
1. [isort (I)](#isort-i)
|
||||
1. [pydocstyle (D)](#pydocstyle-d)
|
||||
1. [pyupgrade (UP)](#pyupgrade-up)
|
||||
1. [pep8-naming (N)](#pep8-naming-n)
|
||||
1. [flake8-2020 (YTT)](#flake8-2020-ytt)
|
||||
1. [flake8-annotations (ANN)](#flake8-annotations-ann)
|
||||
1. [flake8-bandit (S)](#flake8-bandit-s)
|
||||
1. [flake8-blind-except (BLE)](#flake8-blind-except-ble)
|
||||
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap-fbt)
|
||||
1. [flake8-bugbear (B)](#flake8-bugbear-b)
|
||||
1. [flake8-builtins (A)](#flake8-builtins-a)
|
||||
1. [flake8-comprehensions (C4)](#flake8-comprehensions-c4)
|
||||
1. [flake8-debugger (T10)](#flake8-debugger-t10)
|
||||
1. [flake8-import-conventions (ICN)](#flake8-import-conventions-icn)
|
||||
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 (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)
|
||||
1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. -->
|
||||
1. [Editor Integrations](#editor-integrations)
|
||||
1. [FAQ](#faq)
|
||||
1. [Development](#development)
|
||||
@@ -145,7 +147,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.165
|
||||
rev: v0.0.172
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -195,6 +197,13 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
# Assume Python 3.10.
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.flake8-import-conventions.aliases]
|
||||
altair = "alt"
|
||||
"matplotlib.pyplot" = "plt"
|
||||
numpy = "np"
|
||||
pandas = "pd"
|
||||
seaborn = "sns"
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
# Unlike Flake8, default to a complexity level of 10.
|
||||
max-complexity = 10
|
||||
@@ -387,8 +396,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||
|
||||
<!-- Sections automatically generated by `cargo dev generate-rules-table`. -->
|
||||
<!-- Begin auto-generated sections. -->
|
||||
|
||||
### Pyflakes
|
||||
### Pyflakes (F)
|
||||
|
||||
For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
|
||||
|
||||
@@ -430,14 +438,16 @@ 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
|
||||
### pycodestyle (E, W)
|
||||
|
||||
For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI.
|
||||
|
||||
@@ -460,7 +470,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
|
||||
| W292 | NoNewLineAtEndOfFile | No newline at end of file | |
|
||||
| W605 | InvalidEscapeSequence | Invalid escape sequence: '\c' | |
|
||||
|
||||
### mccabe
|
||||
### mccabe (C90)
|
||||
|
||||
For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
|
||||
@@ -468,7 +478,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| C901 | FunctionIsTooComplex | `...` is too complex (10) | |
|
||||
|
||||
### isort
|
||||
### isort (I)
|
||||
|
||||
For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
|
||||
|
||||
@@ -476,7 +486,7 @@ For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| I001 | UnsortedImports | Import block is un-sorted or un-formatted | 🛠 |
|
||||
|
||||
### pydocstyle
|
||||
### pydocstyle (D)
|
||||
|
||||
For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
|
||||
|
||||
@@ -507,6 +517,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 | |
|
||||
@@ -527,7 +538,7 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
|
||||
| D418 | SkipDocstring | Function decorated with `@overload` shouldn't contain a docstring | |
|
||||
| D419 | NonEmpty | Docstring is empty | |
|
||||
|
||||
### pyupgrade
|
||||
### pyupgrade (UP)
|
||||
|
||||
For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
|
||||
@@ -548,7 +559,7 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
| UP014 | ConvertNamedTupleFunctionalToClass | Convert `...` from `NamedTuple` functional to class syntax | 🛠 |
|
||||
| UP015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
|
||||
|
||||
### pep8-naming
|
||||
### pep8-naming (N)
|
||||
|
||||
For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyPI.
|
||||
|
||||
@@ -570,7 +581,7 @@ For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyP
|
||||
| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` | |
|
||||
| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix | |
|
||||
|
||||
### flake8-2020
|
||||
### flake8-2020 (YTT)
|
||||
|
||||
For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI.
|
||||
|
||||
@@ -587,7 +598,7 @@ For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI
|
||||
| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` | |
|
||||
| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` | |
|
||||
|
||||
### flake8-annotations
|
||||
### flake8-annotations (ANN)
|
||||
|
||||
For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2.9.1/) on PyPI.
|
||||
|
||||
@@ -605,7 +616,7 @@ For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2
|
||||
| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` | |
|
||||
| ANN401 | DynamicallyTypedExpression | Dynamically typed expressions (typing.Any) are disallowed in `...` | |
|
||||
|
||||
### flake8-bandit
|
||||
### flake8-bandit (S)
|
||||
|
||||
For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on PyPI.
|
||||
|
||||
@@ -618,15 +629,15 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on
|
||||
| S106 | HardcodedPasswordFuncArg | Possible hardcoded password: `"..."` | |
|
||||
| S107 | HardcodedPasswordDefault | Possible hardcoded password: `"..."` | |
|
||||
|
||||
### flake8-blind-except
|
||||
### flake8-blind-except (BLE)
|
||||
|
||||
For more, see [flake8-blind-except](https://pypi.org/project/flake8-blind-except/0.2.1/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| BLE001 | BlindExcept | Blind except Exception: statement | |
|
||||
| BLE001 | BlindExcept | Do not catch blind exception: `Exception` | |
|
||||
|
||||
### flake8-boolean-trap
|
||||
### flake8-boolean-trap (FBT)
|
||||
|
||||
For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap/0.1.0/) on PyPI.
|
||||
|
||||
@@ -636,7 +647,7 @@ For more, see [flake8-boolean-trap](https://pypi.org/project/flake8-boolean-trap
|
||||
| FBT002 | BooleanDefaultValueInFunctionDefinition | Boolean default value in function definition | |
|
||||
| FBT003 | BooleanPositionalValueInFunctionCall | Boolean positional value in function call | |
|
||||
|
||||
### flake8-bugbear
|
||||
### flake8-bugbear (B)
|
||||
|
||||
For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/) on PyPI.
|
||||
|
||||
@@ -669,8 +680,9 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
|
||||
| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged | |
|
||||
| B027 | EmptyMethodWithoutAbstractDecorator | `...` is an empty method in an abstract base class, but has no abstract decorator | |
|
||||
| B904 | RaiseWithoutFromInsideExcept | Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling | |
|
||||
| B905 | ZipWithoutExplicitStrict | `zip()` without an explicit `strict=` parameter | |
|
||||
|
||||
### flake8-builtins
|
||||
### flake8-builtins (A)
|
||||
|
||||
For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/2.0.1/) on PyPI.
|
||||
|
||||
@@ -680,7 +692,7 @@ For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/2.0.1/)
|
||||
| A002 | BuiltinArgumentShadowing | Argument `...` is shadowing a python builtin | |
|
||||
| A003 | BuiltinAttributeShadowing | Class attribute `...` is shadowing a python builtin | |
|
||||
|
||||
### flake8-comprehensions
|
||||
### flake8-comprehensions (C4)
|
||||
|
||||
For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/3.10.1/) on PyPI.
|
||||
|
||||
@@ -703,7 +715,7 @@ For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehens
|
||||
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 |
|
||||
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
|
||||
|
||||
### flake8-debugger
|
||||
### flake8-debugger (T10)
|
||||
|
||||
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/) on PyPI.
|
||||
|
||||
@@ -711,7 +723,13 @@ For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/)
|
||||
| ---- | ---- | ------- | --- |
|
||||
| T100 | Debugger | Import for `...` found | |
|
||||
|
||||
### flake8-print
|
||||
### flake8-import-conventions (ICN)
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ICN001 | ImportAliasIsNotConventional | `...` should be imported as `...` | |
|
||||
|
||||
### flake8-print (T20)
|
||||
|
||||
For more, see [flake8-print](https://pypi.org/project/flake8-print/5.0.0/) on PyPI.
|
||||
|
||||
@@ -720,7 +738,7 @@ For more, see [flake8-print](https://pypi.org/project/flake8-print/5.0.0/) on Py
|
||||
| T201 | PrintFound | `print` found | 🛠 |
|
||||
| T203 | PPrintFound | `pprint` found | 🛠 |
|
||||
|
||||
### flake8-quotes
|
||||
### flake8-quotes (Q)
|
||||
|
||||
For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on PyPI.
|
||||
|
||||
@@ -731,7 +749,7 @@ For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on
|
||||
| Q002 | BadQuotesDocstring | Single quote docstring found but double quotes preferred | |
|
||||
| Q003 | AvoidQuoteEscape | Change outer quotes to avoid escaping inner quotes | |
|
||||
|
||||
### flake8-return
|
||||
### flake8-return (RET)
|
||||
|
||||
For more, see [flake8-return](https://pypi.org/project/flake8-return/1.2.0/) on PyPI.
|
||||
|
||||
@@ -746,15 +764,27 @@ 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
|
||||
### 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 | |
|
||||
|
||||
### eradicate
|
||||
### 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)
|
||||
|
||||
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
||||
|
||||
@@ -762,7 +792,7 @@ For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ERA001 | CommentedOutCode | Found commented-out code | 🛠 |
|
||||
|
||||
### pygrep-hooks
|
||||
### pygrep-hooks (PGH)
|
||||
|
||||
For more, see [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) on GitHub.
|
||||
|
||||
@@ -770,7 +800,7 @@ For more, see [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) on GitH
|
||||
| ---- | ---- | ------- | --- |
|
||||
| PGH001 | NoEval | No builtin `eval()` allowed | |
|
||||
|
||||
### Pylint
|
||||
### Pylint (PLC, PLE, PLR, PLW)
|
||||
|
||||
For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
|
||||
|
||||
@@ -781,12 +811,12 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
|
||||
| PLC3002 | UnnecessaryDirectLambdaCall | Lambda expression called directly. Execute the expression inline instead. | |
|
||||
| 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
|
||||
### Ruff-specific rules (RUF)
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
@@ -921,7 +951,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:
|
||||
@@ -940,6 +970,7 @@ natively, including:
|
||||
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-return`](https://pypi.org/project/flake8-return/)
|
||||
@@ -952,6 +983,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
|
||||
@@ -988,6 +1025,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-debugger`](https://pypi.org/project/flake8-debugger/)
|
||||
- [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/)
|
||||
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
|
||||
- [`flake8-import-conventions`](https://github.com/joaopalmeiro/flake8-import-conventions)
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-return`](https://pypi.org/project/flake8-return/)
|
||||
@@ -1449,7 +1487,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>`
|
||||
|
||||
@@ -1758,6 +1796,48 @@ extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
|
||||
|
||||
---
|
||||
|
||||
### `flake8-import-conventions`
|
||||
|
||||
#### [`aliases`](#aliases)
|
||||
|
||||
The conventional aliases for imports. These aliases can be extended by the `extend_aliases` option.
|
||||
|
||||
**Default value**: `{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}`
|
||||
|
||||
**Type**: `FxHashMap<String, String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-import-conventions]
|
||||
# Declare the default aliases.
|
||||
altair = "alt"
|
||||
matplotlib.pyplot = "plt"
|
||||
numpy = "np"
|
||||
pandas = "pd"
|
||||
seaborn = "sns"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### [`extend-aliases`](#extend-aliases)
|
||||
|
||||
A mapping of modules to their conventional import aliases. These aliases will be added to the `aliases` mapping.
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
**Type**: `FxHashMap<String, String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
```toml
|
||||
[tool.ruff.flake8-import-conventions]
|
||||
# Declare a custom alias for the `matplotlib` module.
|
||||
"dask.dataframe" = "dd"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `flake8-quotes`
|
||||
|
||||
#### [`avoid-escape`](#avoid-escape)
|
||||
@@ -1903,6 +1983,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`
|
||||
@@ -1912,6 +1996,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.165"
|
||||
version = "0.0.172"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.165"
|
||||
version = "0.0.172"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.165-dev.0"
|
||||
version = "0.0.172-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -270,6 +270,7 @@ mod tests {
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
flake8_import_conventions: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
@@ -314,6 +315,7 @@ mod tests {
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
flake8_import_conventions: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
@@ -358,6 +360,7 @@ mod tests {
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
flake8_import_conventions: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
@@ -402,6 +405,7 @@ mod tests {
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
flake8_import_conventions: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
@@ -451,6 +455,7 @@ mod tests {
|
||||
avoid_escape: None,
|
||||
}),
|
||||
flake8_tidy_imports: None,
|
||||
flake8_import_conventions: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
@@ -507,6 +512,7 @@ mod tests {
|
||||
CheckCodePrefix::D214,
|
||||
CheckCodePrefix::D215,
|
||||
CheckCodePrefix::D300,
|
||||
CheckCodePrefix::D301,
|
||||
CheckCodePrefix::D400,
|
||||
CheckCodePrefix::D403,
|
||||
CheckCodePrefix::D404,
|
||||
@@ -533,6 +539,7 @@ mod tests {
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
flake8_tidy_imports: None,
|
||||
flake8_import_conventions: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
@@ -583,6 +590,7 @@ mod tests {
|
||||
avoid_escape: None,
|
||||
}),
|
||||
flake8_tidy_imports: None,
|
||||
flake8_import_conventions: None,
|
||||
isort: None,
|
||||
mccabe: None,
|
||||
pep8_naming: None,
|
||||
|
||||
@@ -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
|
||||
|
||||
49
resources/test/fixtures/flake8_annotations/allow_overload.py
vendored
Normal file
49
resources/test/fixtures/flake8_annotations/allow_overload.py
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
from typing import overload
|
||||
|
||||
|
||||
@overload
|
||||
def foo(i: int) -> "int":
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def foo(i: "str") -> "str":
|
||||
...
|
||||
|
||||
|
||||
def foo(i):
|
||||
return i
|
||||
|
||||
|
||||
@overload
|
||||
def bar(i: int) -> "int":
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def bar(i: "str") -> "str":
|
||||
...
|
||||
|
||||
|
||||
class X:
|
||||
def bar(i):
|
||||
return i
|
||||
|
||||
|
||||
# TODO(charlie): This third case should raise an error (as in Mypy), because we have a
|
||||
# statement between the interfaces and implementation.
|
||||
@overload
|
||||
def baz(i: int) -> "int":
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def baz(i: "str") -> "str":
|
||||
...
|
||||
|
||||
|
||||
x = 1
|
||||
|
||||
|
||||
def baz(i):
|
||||
return i
|
||||
@@ -53,3 +53,11 @@ try:
|
||||
raise e
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise bad
|
||||
except BaseException:
|
||||
pass
|
||||
|
||||
10
resources/test/fixtures/flake8_bugbear/B905.py
vendored
Normal file
10
resources/test/fixtures/flake8_bugbear/B905.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
zip()
|
||||
zip(range(3))
|
||||
zip("a", "b")
|
||||
zip("a", "b", *zip("c"))
|
||||
zip(zip("a"), strict=False)
|
||||
zip(zip("a", strict=True))
|
||||
|
||||
zip(range(3), strict=True)
|
||||
zip("a", "b", strict=False)
|
||||
zip("a", "b", "c", strict=True)
|
||||
25
resources/test/fixtures/flake8_import_conventions/custom.py
vendored
Normal file
25
resources/test/fixtures/flake8_import_conventions/custom.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import math # not checked
|
||||
|
||||
import altair # unconventional
|
||||
import dask.array # unconventional
|
||||
import dask.dataframe # unconventional
|
||||
import matplotlib.pyplot # unconventional
|
||||
import numpy # unconventional
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
import dask.array as darray # unconventional
|
||||
import dask.dataframe as ddf # unconventional
|
||||
import numpy as nmp # unconventional
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
|
||||
import altair as alt # conventional
|
||||
import dask.array as da # conventional
|
||||
import dask.dataframe as dd # conventional
|
||||
import matplotlib.pyplot as plt # conventional
|
||||
import numpy as np # conventional
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
19
resources/test/fixtures/flake8_import_conventions/defaults.py
vendored
Normal file
19
resources/test/fixtures/flake8_import_conventions/defaults.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import math # not checked
|
||||
|
||||
import altair # unconventional
|
||||
import matplotlib.pyplot # unconventional
|
||||
import numpy # unconventional
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
import numpy as nmp # unconventional
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
|
||||
import altair as alt # conventional
|
||||
import matplotlib.pyplot as plt # conventional
|
||||
import numpy as np # conventional
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
19
resources/test/fixtures/flake8_import_conventions/override_default.py
vendored
Normal file
19
resources/test/fixtures/flake8_import_conventions/override_default.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import math # not checked
|
||||
|
||||
import altair # unconventional
|
||||
import matplotlib.pyplot # unconventional
|
||||
import numpy # unconventional
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
import numpy as np # unconventional
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
|
||||
import altair as alt # conventional
|
||||
import matplotlib.pyplot as plt # conventional
|
||||
import numpy as nmp # conventional
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
19
resources/test/fixtures/flake8_import_conventions/remove_default.py
vendored
Normal file
19
resources/test/fixtures/flake8_import_conventions/remove_default.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import math # not checked
|
||||
|
||||
import altair # unconventional
|
||||
import matplotlib.pyplot # unconventional
|
||||
import numpy # not checked
|
||||
import pandas # unconventional
|
||||
import seaborn # unconventional
|
||||
|
||||
import altair as altr # unconventional
|
||||
import matplotlib.pyplot as plot # unconventional
|
||||
import numpy as nmp # not checked
|
||||
import pandas as pdas # unconventional
|
||||
import seaborn as sbrn # unconventional
|
||||
|
||||
import altair as alt # conventional
|
||||
import matplotlib.pyplot as plt # conventional
|
||||
import numpy as np # not checked
|
||||
import pandas as pd # conventional
|
||||
import seaborn as sns # conventional
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
6
resources/test/fixtures/pyproject.toml
vendored
6
resources/test/fixtures/pyproject.toml
vendored
@@ -41,3 +41,9 @@ staticmethod-decorators = ["staticmethod"]
|
||||
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
ban-relative-imports = "parents"
|
||||
|
||||
[tool.ruff.flake8-import-conventions.aliases]
|
||||
pandas = "pd"
|
||||
|
||||
[tool.ruff.flake8-import-conventions.extend-aliases]
|
||||
"dask.dataframe" = "dd"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.165"
|
||||
version = "0.0.172"
|
||||
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();
|
||||
@@ -76,6 +68,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.new_enum("CheckCodePrefix")
|
||||
.vis("pub")
|
||||
.derive("EnumString")
|
||||
.derive("AsRefStr")
|
||||
.derive("Debug")
|
||||
.derive("PartialEq")
|
||||
.derive("Eq")
|
||||
@@ -112,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(),
|
||||
@@ -124,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![{}],",
|
||||
@@ -170,9 +175,9 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
output.push('\n');
|
||||
output.push_str("use colored::Colorize;");
|
||||
output.push('\n');
|
||||
output.push_str("use serde::{{Serialize, Deserialize}};");
|
||||
output.push_str("use serde::{Deserialize, Serialize};");
|
||||
output.push('\n');
|
||||
output.push_str("use strum_macros::EnumString;");
|
||||
output.push_str("use strum_macros::{AsRefStr, EnumString};");
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
output.push_str("use crate::checks::CheckCode;");
|
||||
@@ -186,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');
|
||||
}
|
||||
|
||||
@@ -7,11 +7,15 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use itertools::Itertools;
|
||||
use ruff::checks::{CheckCategory, CheckCode};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
const BEGIN_PRAGMA: &str = "<!-- Begin auto-generated sections. -->";
|
||||
const END_PRAGMA: &str = "<!-- End auto-generated sections. -->";
|
||||
const TABLE_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated sections. -->";
|
||||
const TABLE_END_PRAGMA: &str = "<!-- End auto-generated sections. -->";
|
||||
|
||||
const TOC_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated table of contents. -->";
|
||||
const TOC_END_PRAGMA: &str = "<!-- End auto-generated table of contents. -->";
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Cli {
|
||||
@@ -22,73 +26,91 @@ pub struct Cli {
|
||||
|
||||
pub fn main(cli: &Cli) -> Result<()> {
|
||||
// Generate the table string.
|
||||
let mut output = String::new();
|
||||
let mut table_out = String::new();
|
||||
let mut toc_out = String::new();
|
||||
for check_category in CheckCategory::iter() {
|
||||
output.push_str(&format!("### {}", check_category.title()));
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
let codes_csv: String = check_category.codes().iter().map(AsRef::as_ref).join(", ");
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", check_category.title()));
|
||||
table_out.push('\n');
|
||||
table_out.push('\n');
|
||||
|
||||
toc_out.push_str(&format!(
|
||||
" 1. [{} ({})](#{}-{})\n",
|
||||
check_category.title(),
|
||||
codes_csv,
|
||||
check_category.title().to_lowercase().replace(' ', "-"),
|
||||
codes_csv.to_lowercase().replace(',', "-").replace(' ', "")
|
||||
));
|
||||
|
||||
if let Some((url, platform)) = check_category.url() {
|
||||
output.push_str(&format!(
|
||||
table_out.push_str(&format!(
|
||||
"For more, see [{}]({}) on {}.",
|
||||
check_category.title(),
|
||||
url,
|
||||
platform
|
||||
));
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
table_out.push('\n');
|
||||
table_out.push('\n');
|
||||
}
|
||||
|
||||
output.push_str("| Code | Name | Message | Fix |");
|
||||
output.push('\n');
|
||||
output.push_str("| ---- | ---- | ------- | --- |");
|
||||
output.push('\n');
|
||||
table_out.push_str("| Code | Name | Message | Fix |");
|
||||
table_out.push('\n');
|
||||
table_out.push_str("| ---- | ---- | ------- | --- |");
|
||||
table_out.push('\n');
|
||||
|
||||
for check_code in CheckCode::iter() {
|
||||
if check_code.category() == check_category {
|
||||
let check_kind = check_code.kind();
|
||||
let fix_token = if check_kind.fixable() { "🛠" } else { "" };
|
||||
output.push_str(&format!(
|
||||
table_out.push_str(&format!(
|
||||
"| {} | {} | {} | {} |",
|
||||
check_kind.code().as_ref(),
|
||||
check_kind.as_ref(),
|
||||
check_kind.summary().replace('|', r"\|"),
|
||||
fix_token
|
||||
));
|
||||
output.push('\n');
|
||||
table_out.push('\n');
|
||||
}
|
||||
}
|
||||
output.push('\n');
|
||||
table_out.push('\n');
|
||||
}
|
||||
|
||||
if cli.dry_run {
|
||||
print!("{output}");
|
||||
print!("Table of Contents: {toc_out}\n Rules Tables: {table_out}");
|
||||
} else {
|
||||
// Read the existing file.
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("README.md");
|
||||
let existing = fs::read_to_string(&file)?;
|
||||
|
||||
// Extract the prefix.
|
||||
let index = existing
|
||||
.find(BEGIN_PRAGMA)
|
||||
.expect("Unable to find begin pragma");
|
||||
let prefix = &existing[..index + BEGIN_PRAGMA.len()];
|
||||
|
||||
// Extract the suffix.
|
||||
let index = existing
|
||||
.find(END_PRAGMA)
|
||||
.expect("Unable to find end pragma");
|
||||
let suffix = &existing[index..];
|
||||
|
||||
// Write the prefix, new contents, and suffix.
|
||||
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
|
||||
write!(f, "{prefix}\n\n")?;
|
||||
write!(f, "{output}")?;
|
||||
write!(f, "{suffix}")?;
|
||||
// Extra newline in the markdown numbered list looks weird
|
||||
replace_readme_section(toc_out.trim_end(), TOC_BEGIN_PRAGMA, TOC_END_PRAGMA)?;
|
||||
replace_readme_section(&table_out, TABLE_BEGIN_PRAGMA, TABLE_END_PRAGMA)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> {
|
||||
// Read the existing file.
|
||||
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("Failed to find root directory")
|
||||
.join("README.md");
|
||||
let existing = fs::read_to_string(&file)?;
|
||||
|
||||
// Extract the prefix.
|
||||
let index = existing
|
||||
.find(begin_pragma)
|
||||
.expect("Unable to find begin pragma");
|
||||
let prefix = &existing[..index + begin_pragma.len()];
|
||||
|
||||
// Extract the suffix.
|
||||
let index = existing
|
||||
.find(end_pragma)
|
||||
.expect("Unable to find end pragma");
|
||||
let suffix = &existing[index..];
|
||||
|
||||
// Write the prefix, new contents, and suffix.
|
||||
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
|
||||
writeln!(f, "{prefix}")?;
|
||||
write!(f, "{content}")?;
|
||||
write!(f, "{suffix}")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.161"
|
||||
version = "0.0.172"
|
||||
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;
|
||||
|
||||
@@ -3,10 +3,10 @@ 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};
|
||||
|
||||
/// 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 +23,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);
|
||||
}
|
||||
}
|
||||
|
||||
142
src/ast/types.rs
142
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,50 @@ impl Range {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionScope {
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionDef<'a> {
|
||||
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>,
|
||||
// TODO(charlie): Create AsyncFunctionDef to mirror the AST.
|
||||
pub async_: bool,
|
||||
pub uses_locals: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClassScope<'a> {
|
||||
#[derive(Debug)]
|
||||
pub struct ClassDef<'a> {
|
||||
pub name: &'a str,
|
||||
pub bases: &'a [Expr],
|
||||
pub keywords: &'a [Keyword],
|
||||
// pub body: &'a [Stmt],
|
||||
pub decorator_list: &'a [Expr],
|
||||
}
|
||||
|
||||
#[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,
|
||||
pub values: FxHashMap<&'a str, usize>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
@@ -68,43 +82,123 @@ impl<'a> Scope<'a> {
|
||||
id: id(),
|
||||
kind,
|
||||
import_starred: false,
|
||||
uses_locals: false,
|
||||
values: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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)>,
|
||||
/// A list of pointers to `Binding` instances that redefined this binding.
|
||||
pub redefined: Vec<usize>,
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
25
src/cache.rs
25
src/cache.rs
@@ -139,20 +139,21 @@ pub fn get(
|
||||
return None;
|
||||
};
|
||||
|
||||
if let Ok(encoded) = read_sync(cache_key(path, settings, autofix)) {
|
||||
match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
Ok(CheckResult {
|
||||
metadata: CacheMetadata { mtime },
|
||||
messages,
|
||||
}) => {
|
||||
if FileTime::from_last_modification_time(metadata).unix_seconds() == mtime {
|
||||
return Some(messages);
|
||||
}
|
||||
}
|
||||
Err(e) => error!("Failed to deserialize encoded cache entry: {e:?}"),
|
||||
let encoded = read_sync(cache_key(path, settings, autofix)).ok()?;
|
||||
let (mtime, messages) = match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
||||
Ok(CheckResult {
|
||||
metadata: CacheMetadata { mtime },
|
||||
messages,
|
||||
}) => (mtime, messages),
|
||||
Err(e) => {
|
||||
error!("Failed to deserialize encoded cache entry: {e:?}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
if FileTime::from_last_modification_time(metadata).unix_seconds() != mtime {
|
||||
return None;
|
||||
}
|
||||
None
|
||||
Some(messages)
|
||||
}
|
||||
|
||||
/// Set a value in the cache.
|
||||
|
||||
825
src/check_ast.rs
825
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;
|
||||
@@ -20,19 +20,18 @@ static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwra
|
||||
|
||||
/// Whether the given line is too long and should be reported.
|
||||
fn should_enforce_line_length(line: &str, length: usize, limit: usize) -> bool {
|
||||
if length > limit {
|
||||
let mut chunks = line.split_whitespace();
|
||||
if let (Some(first), Some(_)) = (chunks.next(), chunks.next()) {
|
||||
// Do not enforce the line length for commented lines that end with a URL
|
||||
// or contain only a single word.
|
||||
!(first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)))
|
||||
} else {
|
||||
// Single word / no printable chars - no way to make the line shorter
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
if length <= limit {
|
||||
return false;
|
||||
}
|
||||
let mut chunks = line.split_whitespace();
|
||||
let (Some(first), Some(_)) = (chunks.next(), chunks.next()) else {
|
||||
// Single word / no printable chars - no way to make the line shorter
|
||||
return false;
|
||||
};
|
||||
|
||||
// Do not enforce the line length for commented lines that end with a URL
|
||||
// or contain only a single word.
|
||||
!(first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)))
|
||||
}
|
||||
|
||||
pub fn check_lines(
|
||||
@@ -210,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 {
|
||||
|
||||
189
src/checks.rs
189
src/checks.rs
@@ -10,6 +10,7 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::flake8_debugger::types::DebuggerUsingType;
|
||||
use crate::flake8_quotes::settings::Quote;
|
||||
use crate::flake8_tidy_imports::settings::Strictness;
|
||||
@@ -86,11 +87,13 @@ pub enum CheckCode {
|
||||
F706,
|
||||
F707,
|
||||
F722,
|
||||
F811,
|
||||
F821,
|
||||
F822,
|
||||
F823,
|
||||
F831,
|
||||
F841,
|
||||
F842,
|
||||
F901,
|
||||
// pylint
|
||||
PLC0414,
|
||||
@@ -134,6 +137,7 @@ pub enum CheckCode {
|
||||
B026,
|
||||
B027,
|
||||
B904,
|
||||
B905,
|
||||
// flake8-blind-except
|
||||
BLE001,
|
||||
// flake8-comprehensions
|
||||
@@ -158,7 +162,7 @@ pub enum CheckCode {
|
||||
// mccabe
|
||||
C901,
|
||||
// flake8-tidy-imports
|
||||
I252,
|
||||
TID252,
|
||||
// flake8-return
|
||||
RET501,
|
||||
RET502,
|
||||
@@ -240,6 +244,7 @@ pub enum CheckCode {
|
||||
D214,
|
||||
D215,
|
||||
D300,
|
||||
D301,
|
||||
D400,
|
||||
D402,
|
||||
D403,
|
||||
@@ -290,6 +295,14 @@ pub enum CheckCode {
|
||||
FBT001,
|
||||
FBT002,
|
||||
FBT003,
|
||||
// flake8-unused-arguments
|
||||
ARG001,
|
||||
ARG002,
|
||||
ARG003,
|
||||
ARG004,
|
||||
ARG005,
|
||||
// flake8-import-conventions
|
||||
ICN001,
|
||||
// Ruff
|
||||
RUF001,
|
||||
RUF002,
|
||||
@@ -317,10 +330,12 @@ pub enum CheckCategory {
|
||||
Flake8Builtins,
|
||||
Flake8Comprehensions,
|
||||
Flake8Debugger,
|
||||
Flake8ImportConventions,
|
||||
Flake8Print,
|
||||
Flake8Quotes,
|
||||
Flake8Return,
|
||||
Flake8TidyImports,
|
||||
Flake8UnusedArguments,
|
||||
Eradicate,
|
||||
PygrepHooks,
|
||||
Pylint,
|
||||
@@ -354,10 +369,12 @@ impl CheckCategory {
|
||||
CheckCategory::Flake8Builtins => "flake8-builtins",
|
||||
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
||||
CheckCategory::Flake8Debugger => "flake8-debugger",
|
||||
CheckCategory::Flake8ImportConventions => "flake8-import-conventions",
|
||||
CheckCategory::Flake8Print => "flake8-print",
|
||||
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",
|
||||
@@ -371,6 +388,42 @@ impl CheckCategory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn codes(&self) -> Vec<CheckCodePrefix> {
|
||||
match self {
|
||||
CheckCategory::Eradicate => vec![CheckCodePrefix::ERA],
|
||||
CheckCategory::Flake82020 => vec![CheckCodePrefix::YTT],
|
||||
CheckCategory::Flake8Annotations => vec![CheckCodePrefix::ANN],
|
||||
CheckCategory::Flake8Bandit => vec![CheckCodePrefix::S],
|
||||
CheckCategory::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
|
||||
CheckCategory::Flake8BooleanTrap => vec![CheckCodePrefix::FBT],
|
||||
CheckCategory::Flake8Bugbear => vec![CheckCodePrefix::B],
|
||||
CheckCategory::Flake8Builtins => vec![CheckCodePrefix::A],
|
||||
CheckCategory::Flake8Comprehensions => vec![CheckCodePrefix::C4],
|
||||
CheckCategory::Flake8Debugger => vec![CheckCodePrefix::T10],
|
||||
CheckCategory::Flake8Print => vec![CheckCodePrefix::T20],
|
||||
CheckCategory::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||
CheckCategory::Flake8Return => vec![CheckCodePrefix::RET],
|
||||
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],
|
||||
CheckCategory::Pydocstyle => vec![CheckCodePrefix::D],
|
||||
CheckCategory::Pyflakes => vec![CheckCodePrefix::F],
|
||||
CheckCategory::PygrepHooks => vec![CheckCodePrefix::PGH],
|
||||
CheckCategory::Pylint => vec![
|
||||
CheckCodePrefix::PLC,
|
||||
CheckCodePrefix::PLE,
|
||||
CheckCodePrefix::PLR,
|
||||
CheckCodePrefix::PLW,
|
||||
],
|
||||
CheckCategory::Pyupgrade => vec![CheckCodePrefix::UP],
|
||||
CheckCategory::Flake8ImportConventions => vec![CheckCodePrefix::ICN],
|
||||
CheckCategory::Ruff => vec![CheckCodePrefix::RUF],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn url(&self) -> Option<(&'static str, &'static Platform)> {
|
||||
match self {
|
||||
CheckCategory::Eradicate => {
|
||||
@@ -412,6 +465,7 @@ impl CheckCategory {
|
||||
"https://pypi.org/project/flake8-debugger/4.1.2/",
|
||||
&Platform::PyPI,
|
||||
)),
|
||||
CheckCategory::Flake8ImportConventions => None,
|
||||
CheckCategory::Flake8Print => Some((
|
||||
"https://pypi.org/project/flake8-print/5.0.0/",
|
||||
&Platform::PyPI,
|
||||
@@ -428,6 +482,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))
|
||||
}
|
||||
@@ -561,6 +619,7 @@ pub enum CheckKind {
|
||||
PercentFormatStarRequiresSequence,
|
||||
PercentFormatUnsupportedFormatCharacter(char),
|
||||
RaiseNotImplemented,
|
||||
RedefinedWhileUnused(String, usize),
|
||||
ReturnOutsideFunction,
|
||||
StringDotFormatExtraNamedArguments(Vec<String>),
|
||||
StringDotFormatExtraPositionalArguments(Vec<String>),
|
||||
@@ -570,6 +629,7 @@ pub enum CheckKind {
|
||||
TwoStarredExpressions,
|
||||
UndefinedExport(String),
|
||||
UndefinedLocal(String),
|
||||
UnusedAnnotation(String),
|
||||
UndefinedName(String),
|
||||
UnusedImport(String, bool),
|
||||
UnusedVariable(String),
|
||||
@@ -583,13 +643,13 @@ pub enum CheckKind {
|
||||
ConsiderUsingFromImport(String, String),
|
||||
AwaitOutsideAsync,
|
||||
UselessElseOnLoop,
|
||||
ConsiderUsingSysExit,
|
||||
UseSysExit(String),
|
||||
// flake8-builtins
|
||||
BuiltinVariableShadowing(String),
|
||||
BuiltinArgumentShadowing(String),
|
||||
BuiltinAttributeShadowing(String),
|
||||
// flake8-blind-except
|
||||
BlindExcept,
|
||||
BlindExcept(String),
|
||||
// flake8-bugbear
|
||||
AbstractBaseClassWithoutAbstractMethod(String),
|
||||
AssignmentToOsEnviron,
|
||||
@@ -618,6 +678,7 @@ pub enum CheckKind {
|
||||
UselessComparison,
|
||||
UselessContextlibSuppress,
|
||||
UselessExpression,
|
||||
ZipWithoutExplicitStrict,
|
||||
// flake8-comprehensions
|
||||
UnnecessaryGeneratorList,
|
||||
UnnecessaryGeneratorSet,
|
||||
@@ -738,6 +799,7 @@ pub enum CheckKind {
|
||||
SectionUnderlineMatchesSectionLength(String),
|
||||
SectionUnderlineNotOverIndented(String),
|
||||
SkipDocstring,
|
||||
UsesRPrefixForBackslashedContent,
|
||||
UsesTripleQuotes,
|
||||
// pep8-naming
|
||||
InvalidClassName(String),
|
||||
@@ -774,6 +836,14 @@ 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
|
||||
AmbiguousUnicodeCharacterString(char, char),
|
||||
AmbiguousUnicodeCharacterDocstring(char, char),
|
||||
@@ -868,11 +938,13 @@ 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,
|
||||
@@ -886,7 +958,7 @@ 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,
|
||||
// flake8-builtins
|
||||
CheckCode::A001 => CheckKind::BuiltinVariableShadowing("...".to_string()),
|
||||
@@ -924,6 +996,7 @@ impl CheckCode {
|
||||
CheckCode::B026 => CheckKind::StarArgUnpackingAfterKeywordArg,
|
||||
CheckCode::B027 => CheckKind::EmptyMethodWithoutAbstractDecorator("...".to_string()),
|
||||
CheckCode::B904 => CheckKind::RaiseWithoutFromInsideExcept,
|
||||
CheckCode::B905 => CheckKind::ZipWithoutExplicitStrict,
|
||||
// flake8-comprehensions
|
||||
CheckCode::C400 => CheckKind::UnnecessaryGeneratorList,
|
||||
CheckCode::C401 => CheckKind::UnnecessaryGeneratorSet,
|
||||
@@ -957,7 +1030,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,
|
||||
@@ -999,7 +1072,7 @@ impl CheckCode {
|
||||
CheckCode::YTT302 => CheckKind::SysVersionCmpStr10,
|
||||
CheckCode::YTT303 => CheckKind::SysVersionSlice1Referenced,
|
||||
// flake8-blind-except
|
||||
CheckCode::BLE001 => CheckKind::BlindExcept,
|
||||
CheckCode::BLE001 => CheckKind::BlindExcept("Exception".to_string()),
|
||||
// pyupgrade
|
||||
CheckCode::UP001 => CheckKind::UselessMetaclassType,
|
||||
CheckCode::UP003 => CheckKind::TypeOfPrimitive(Primitive::Str),
|
||||
@@ -1044,6 +1117,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,
|
||||
@@ -1113,6 +1187,16 @@ 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())
|
||||
}
|
||||
// Ruff
|
||||
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
|
||||
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
|
||||
@@ -1138,6 +1222,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,
|
||||
@@ -1165,6 +1254,7 @@ impl CheckCode {
|
||||
CheckCode::B026 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B027 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B904 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B905 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::BLE001 => CheckCategory::Flake8BlindExcept,
|
||||
CheckCode::C400 => CheckCategory::Flake8Comprehensions,
|
||||
CheckCode::C401 => CheckCategory::Flake8Comprehensions,
|
||||
@@ -1208,6 +1298,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,
|
||||
@@ -1278,17 +1369,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::I252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::TID252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::ICN001 => CheckCategory::Flake8ImportConventions,
|
||||
CheckCode::N801 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N802 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N803 => CheckCategory::PEP8Naming,
|
||||
@@ -1426,9 +1520,11 @@ 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,
|
||||
@@ -1441,7 +1537,7 @@ impl CheckKind {
|
||||
CheckKind::ConsiderMergingIsinstance(..) => &CheckCode::PLR1701,
|
||||
CheckKind::PropertyWithParameters => &CheckCode::PLR0206,
|
||||
CheckKind::ConsiderUsingFromImport(..) => &CheckCode::PLR0402,
|
||||
CheckKind::ConsiderUsingSysExit => &CheckCode::PLR1722,
|
||||
CheckKind::UseSysExit(_) => &CheckCode::PLR1722,
|
||||
CheckKind::UselessElseOnLoop => &CheckCode::PLW0120,
|
||||
// flake8-builtins
|
||||
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
|
||||
@@ -1465,6 +1561,7 @@ impl CheckKind {
|
||||
CheckKind::MutableArgumentDefault => &CheckCode::B006,
|
||||
CheckKind::NoAssertRaisesException => &CheckCode::B017,
|
||||
CheckKind::RaiseWithoutFromInsideExcept => &CheckCode::B904,
|
||||
CheckKind::ZipWithoutExplicitStrict => &CheckCode::B905,
|
||||
CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013,
|
||||
CheckKind::SetAttrWithConstant => &CheckCode::B010,
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
|
||||
@@ -1476,7 +1573,7 @@ impl CheckKind {
|
||||
CheckKind::UselessContextlibSuppress => &CheckCode::B022,
|
||||
CheckKind::UselessExpression => &CheckCode::B018,
|
||||
// flake8-blind-except
|
||||
CheckKind::BlindExcept => &CheckCode::BLE001,
|
||||
CheckKind::BlindExcept(_) => &CheckCode::BLE001,
|
||||
// flake8-comprehensions
|
||||
CheckKind::UnnecessaryGeneratorList => &CheckCode::C400,
|
||||
CheckKind::UnnecessaryGeneratorSet => &CheckCode::C401,
|
||||
@@ -1497,7 +1594,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,
|
||||
@@ -1597,6 +1694,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,
|
||||
@@ -1633,6 +1731,14 @@ 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
|
||||
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
|
||||
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
|
||||
@@ -1755,6 +1861,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()
|
||||
}
|
||||
@@ -1803,6 +1912,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!(
|
||||
@@ -1830,10 +1942,10 @@ 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::UnnecessaryDirectLambdaCall => "Lambda expression called directly. Execute \
|
||||
the expression inline instead."
|
||||
@@ -1842,7 +1954,7 @@ 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::AwaitOutsideAsync => {
|
||||
"`await` should be used within an async function".to_string()
|
||||
@@ -1850,7 +1962,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")
|
||||
@@ -1976,6 +2088,9 @@ impl CheckKind {
|
||||
from None to distinguish them from errors in exception handling"
|
||||
.to_string()
|
||||
}
|
||||
CheckKind::ZipWithoutExplicitStrict => {
|
||||
"`zip()` without an explicit `strict=` parameter".to_string()
|
||||
}
|
||||
// flake8-comprehensions
|
||||
CheckKind::UnnecessaryGeneratorList => {
|
||||
"Unnecessary generator (rewrite as a `list` comprehension)".to_string()
|
||||
@@ -2238,6 +2353,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()
|
||||
@@ -2402,7 +2520,7 @@ impl CheckKind {
|
||||
format!("Possible hardcoded password: `\"{string}\"`")
|
||||
}
|
||||
// flake8-blind-except
|
||||
CheckKind::BlindExcept => "Blind except Exception: statement".to_string(),
|
||||
CheckKind::BlindExcept(name) => format!("Do not catch blind exception: `{name}`"),
|
||||
// mccabe
|
||||
CheckKind::FunctionIsTooComplex(name, complexity) => {
|
||||
format!("`{name}` is too complex ({complexity})")
|
||||
@@ -2419,6 +2537,22 @@ 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}`")
|
||||
}
|
||||
// Ruff
|
||||
CheckKind::AmbiguousUnicodeCharacterString(confusable, representant) => {
|
||||
format!(
|
||||
@@ -2489,7 +2623,6 @@ impl CheckKind {
|
||||
| CheckKind::BlankLineBeforeSection(..)
|
||||
| CheckKind::CapitalizeSectionName(..)
|
||||
| CheckKind::CommentedOutCode
|
||||
| CheckKind::ConsiderUsingSysExit
|
||||
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass(..)
|
||||
| CheckKind::DashedUnderlineAfterSection(..)
|
||||
@@ -2555,6 +2688,7 @@ impl CheckKind {
|
||||
| CheckKind::UnusedNOQA(..)
|
||||
| CheckKind::UsePEP585Annotation(..)
|
||||
| CheckKind::UsePEP604Annotation
|
||||
| CheckKind::UseSysExit(..)
|
||||
| CheckKind::UselessImportAlias
|
||||
| CheckKind::UselessMetaclassType
|
||||
| CheckKind::UselessObjectInheritance(..)
|
||||
@@ -2586,7 +2720,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),
|
||||
@@ -2603,6 +2737,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"),
|
||||
])
|
||||
});
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
use colored::Colorize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::EnumString;
|
||||
use strum_macros::{AsRefStr, EnumString};
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
|
||||
#[derive(EnumString, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
|
||||
#[derive(
|
||||
EnumString, AsRefStr, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize,
|
||||
)]
|
||||
pub enum CheckCodePrefix {
|
||||
A,
|
||||
A0,
|
||||
@@ -34,6 +36,14 @@ pub enum CheckCodePrefix {
|
||||
ANN4,
|
||||
ANN40,
|
||||
ANN401,
|
||||
ARG,
|
||||
ARG0,
|
||||
ARG00,
|
||||
ARG001,
|
||||
ARG002,
|
||||
ARG003,
|
||||
ARG004,
|
||||
ARG005,
|
||||
B,
|
||||
B0,
|
||||
B00,
|
||||
@@ -68,6 +78,7 @@ pub enum CheckCodePrefix {
|
||||
B9,
|
||||
B90,
|
||||
B904,
|
||||
B905,
|
||||
BLE,
|
||||
BLE0,
|
||||
BLE00,
|
||||
@@ -128,6 +139,7 @@ pub enum CheckCodePrefix {
|
||||
D3,
|
||||
D30,
|
||||
D300,
|
||||
D301,
|
||||
D4,
|
||||
D40,
|
||||
D400,
|
||||
@@ -232,6 +244,8 @@ pub enum CheckCodePrefix {
|
||||
F72,
|
||||
F722,
|
||||
F8,
|
||||
F81,
|
||||
F811,
|
||||
F82,
|
||||
F821,
|
||||
F822,
|
||||
@@ -240,6 +254,7 @@ pub enum CheckCodePrefix {
|
||||
F831,
|
||||
F84,
|
||||
F841,
|
||||
F842,
|
||||
F9,
|
||||
F90,
|
||||
F901,
|
||||
@@ -256,6 +271,13 @@ pub enum CheckCodePrefix {
|
||||
I2,
|
||||
I25,
|
||||
I252,
|
||||
ICN,
|
||||
ICN0,
|
||||
ICN00,
|
||||
ICN001,
|
||||
M,
|
||||
M0,
|
||||
M001,
|
||||
N,
|
||||
N8,
|
||||
N80,
|
||||
@@ -360,6 +382,10 @@ pub enum CheckCodePrefix {
|
||||
T20,
|
||||
T201,
|
||||
T203,
|
||||
TID,
|
||||
TID2,
|
||||
TID25,
|
||||
TID252,
|
||||
U,
|
||||
U0,
|
||||
U00,
|
||||
@@ -485,6 +511,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,
|
||||
@@ -513,6 +565,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::B026,
|
||||
CheckCode::B027,
|
||||
CheckCode::B904,
|
||||
CheckCode::B905,
|
||||
],
|
||||
CheckCodePrefix::B0 => vec![
|
||||
CheckCode::B002,
|
||||
@@ -600,9 +653,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B025 => vec![CheckCode::B025],
|
||||
CheckCodePrefix::B026 => vec![CheckCode::B026],
|
||||
CheckCodePrefix::B027 => vec![CheckCode::B027],
|
||||
CheckCodePrefix::B9 => vec![CheckCode::B904],
|
||||
CheckCodePrefix::B90 => vec![CheckCode::B904],
|
||||
CheckCodePrefix::B9 => vec![CheckCode::B904, CheckCode::B905],
|
||||
CheckCodePrefix::B90 => vec![CheckCode::B904, CheckCode::B905],
|
||||
CheckCodePrefix::B904 => vec![CheckCode::B904],
|
||||
CheckCodePrefix::B905 => vec![CheckCode::B905],
|
||||
CheckCodePrefix::BLE => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::BLE0 => vec![CheckCode::BLE001],
|
||||
CheckCodePrefix::BLE00 => vec![CheckCode::BLE001],
|
||||
@@ -709,6 +763,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::D214,
|
||||
CheckCode::D215,
|
||||
CheckCode::D300,
|
||||
CheckCode::D301,
|
||||
CheckCode::D400,
|
||||
CheckCode::D402,
|
||||
CheckCode::D403,
|
||||
@@ -811,9 +866,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,
|
||||
@@ -976,11 +1032,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![
|
||||
@@ -1108,20 +1166,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],
|
||||
@@ -1131,13 +1194,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,
|
||||
@@ -1366,54 +1484,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]
|
||||
}
|
||||
@@ -1422,7 +1568,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U003` has been renamed to `UP003`".bold()
|
||||
"`U003` has been remapped to `UP003`".bold()
|
||||
);
|
||||
vec![CheckCode::UP003]
|
||||
}
|
||||
@@ -1431,7 +1577,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U004` has been renamed to `UP004`".bold()
|
||||
"`U004` has been remapped to `UP004`".bold()
|
||||
);
|
||||
vec![CheckCode::UP004]
|
||||
}
|
||||
@@ -1440,7 +1586,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U005` has been renamed to `UP005`".bold()
|
||||
"`U005` has been remapped to `UP005`".bold()
|
||||
);
|
||||
vec![CheckCode::UP005]
|
||||
}
|
||||
@@ -1449,7 +1595,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U006` has been renamed to `UP006`".bold()
|
||||
"`U006` has been remapped to `UP006`".bold()
|
||||
);
|
||||
vec![CheckCode::UP006]
|
||||
}
|
||||
@@ -1458,7 +1604,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U007` has been renamed to `UP007`".bold()
|
||||
"`U007` has been remapped to `UP007`".bold()
|
||||
);
|
||||
vec![CheckCode::UP007]
|
||||
}
|
||||
@@ -1467,7 +1613,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U008` has been renamed to `UP008`".bold()
|
||||
"`U008` has been remapped to `UP008`".bold()
|
||||
);
|
||||
vec![CheckCode::UP008]
|
||||
}
|
||||
@@ -1476,24 +1622,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]
|
||||
}
|
||||
@@ -1502,7 +1656,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U011` has been renamed to `UP011`".bold()
|
||||
"`U011` has been remapped to `UP011`".bold()
|
||||
);
|
||||
vec![CheckCode::UP011]
|
||||
}
|
||||
@@ -1511,7 +1665,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U012` has been renamed to `UP012`".bold()
|
||||
"`U012` has been remapped to `UP012`".bold()
|
||||
);
|
||||
vec![CheckCode::UP012]
|
||||
}
|
||||
@@ -1520,7 +1674,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U013` has been renamed to `UP013`".bold()
|
||||
"`U013` has been remapped to `UP013`".bold()
|
||||
);
|
||||
vec![CheckCode::UP013]
|
||||
}
|
||||
@@ -1529,7 +1683,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U014` has been renamed to `UP014`".bold()
|
||||
"`U014` has been remapped to `UP014`".bold()
|
||||
);
|
||||
vec![CheckCode::UP014]
|
||||
}
|
||||
@@ -1538,7 +1692,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U015` has been renamed to `UP015`".bold()
|
||||
"`U015` has been remapped to `UP015`".bold()
|
||||
);
|
||||
vec![CheckCode::UP015]
|
||||
}
|
||||
@@ -1685,6 +1839,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,
|
||||
@@ -1719,6 +1881,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B9 => SuffixLength::One,
|
||||
CheckCodePrefix::B90 => SuffixLength::Two,
|
||||
CheckCodePrefix::B904 => SuffixLength::Three,
|
||||
CheckCodePrefix::B905 => SuffixLength::Three,
|
||||
CheckCodePrefix::BLE => SuffixLength::Zero,
|
||||
CheckCodePrefix::BLE0 => SuffixLength::One,
|
||||
CheckCodePrefix::BLE00 => SuffixLength::Two,
|
||||
@@ -1779,6 +1942,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,
|
||||
@@ -1883,6 +2047,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,
|
||||
@@ -1891,6 +2057,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,
|
||||
@@ -1907,6 +2074,13 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::I2 => SuffixLength::One,
|
||||
CheckCodePrefix::I25 => SuffixLength::Two,
|
||||
CheckCodePrefix::I252 => SuffixLength::Three,
|
||||
CheckCodePrefix::ICN => SuffixLength::Zero,
|
||||
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,
|
||||
@@ -2011,6 +2185,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,
|
||||
@@ -2078,6 +2256,7 @@ impl CheckCodePrefix {
|
||||
pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::ANN,
|
||||
CheckCodePrefix::ARG,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
@@ -2087,6 +2266,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::F,
|
||||
CheckCodePrefix::FBT,
|
||||
CheckCodePrefix::I,
|
||||
CheckCodePrefix::ICN,
|
||||
CheckCodePrefix::N,
|
||||
CheckCodePrefix::PGH,
|
||||
CheckCodePrefix::PLC,
|
||||
@@ -2098,7 +2278,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::RUF,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::TID,
|
||||
CheckCodePrefix::UP,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
|
||||
@@ -93,32 +93,34 @@ pub fn extract_isort_directives(lxr: &[LexResult], locator: &SourceCodeLocator)
|
||||
continue;
|
||||
}
|
||||
|
||||
if matches!(tok, Tok::Comment) {
|
||||
// TODO(charlie): Modify RustPython to include the comment text in the token.
|
||||
let comment_text = locator.slice_source_code_range(&Range {
|
||||
location: start,
|
||||
end_location: end,
|
||||
});
|
||||
if !matches!(tok, Tok::Comment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if comment_text == "# isort: split" {
|
||||
splits.push(start.row());
|
||||
} else if comment_text == "# isort: skip_file" {
|
||||
skip_file = true;
|
||||
} else if off.is_some() {
|
||||
if comment_text == "# isort: on" {
|
||||
if let Some(start) = off {
|
||||
for row in start.row() + 1..=end.row() {
|
||||
exclusions.insert(row);
|
||||
}
|
||||
// TODO(charlie): Modify RustPython to include the comment text in the token.
|
||||
let comment_text = locator.slice_source_code_range(&Range {
|
||||
location: start,
|
||||
end_location: end,
|
||||
});
|
||||
|
||||
if comment_text == "# isort: split" {
|
||||
splits.push(start.row());
|
||||
} else if comment_text == "# isort: skip_file" {
|
||||
skip_file = true;
|
||||
} else if off.is_some() {
|
||||
if comment_text == "# isort: on" {
|
||||
if let Some(start) = off {
|
||||
for row in start.row() + 1..=end.row() {
|
||||
exclusions.insert(row);
|
||||
}
|
||||
off = None;
|
||||
}
|
||||
} else {
|
||||
if comment_text.contains("isort: skip") {
|
||||
exclusions.insert(start.row());
|
||||
} else if comment_text == "# isort: off" {
|
||||
off = Some(start);
|
||||
}
|
||||
off = None;
|
||||
}
|
||||
} else {
|
||||
if comment_text.contains("isort: skip") {
|
||||
exclusions.insert(start.row());
|
||||
} else if comment_text == "# isort: off" {
|
||||
off = Some(start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'", "\"", "'",
|
||||
];
|
||||
|
||||
@@ -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());
|
||||
|
||||
63
src/flake8_annotations/helpers.rs
Normal file
63
src/flake8_annotations/helpers.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
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;
|
||||
|
||||
pub(super) fn match_function_def(
|
||||
stmt: &Stmt,
|
||||
) -> (&str, &Arguments, &Option<Box<Expr>>, &Vec<Stmt>) {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef {
|
||||
name,
|
||||
args,
|
||||
returns,
|
||||
body,
|
||||
..
|
||||
}
|
||||
| StmtKind::AsyncFunctionDef {
|
||||
name,
|
||||
args,
|
||||
returns,
|
||||
body,
|
||||
..
|
||||
} => (name, args, returns, body),
|
||||
_ => panic!("Found non-FunctionDef in match_name"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the name of the function, if it's overloaded.
|
||||
pub fn overloaded_name(checker: &Checker, definition: &Definition) -> Option<String> {
|
||||
if let DefinitionKind::Function(stmt)
|
||||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the definition is the implementation for an overloaded
|
||||
/// function.
|
||||
pub fn is_overload_impl(checker: &Checker, definition: &Definition, overloaded_name: &str) -> bool {
|
||||
if let DefinitionKind::Function(stmt)
|
||||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
false
|
||||
} else {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
name == overloaded_name
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod helpers;
|
||||
pub mod plugins;
|
||||
pub mod settings;
|
||||
|
||||
@@ -134,4 +135,24 @@ mod tests {
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allow_overload() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/allow_overload.py"),
|
||||
&Settings {
|
||||
..Settings::for_rules(vec![
|
||||
CheckCode::ANN201,
|
||||
CheckCode::ANN202,
|
||||
CheckCode::ANN204,
|
||||
CheckCode::ANN205,
|
||||
CheckCode::ANN206,
|
||||
])
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use rustpython_ast::{Arguments, Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||
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};
|
||||
use crate::flake8_annotations::helpers::match_function_def;
|
||||
use crate::visibility::Visibility;
|
||||
use crate::{visibility, Check};
|
||||
|
||||
@@ -61,26 +62,6 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
fn match_function_def(stmt: &Stmt) -> (&str, &Arguments, &Option<Box<Expr>>, &Vec<Stmt>) {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef {
|
||||
name,
|
||||
args,
|
||||
returns,
|
||||
body,
|
||||
..
|
||||
}
|
||||
| StmtKind::AsyncFunctionDef {
|
||||
name,
|
||||
args,
|
||||
returns,
|
||||
body,
|
||||
..
|
||||
} => (name, args, returns, body),
|
||||
_ => panic!("Found non-FunctionDef in match_name"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate flake8-annotation checks for a given `Definition`.
|
||||
pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &Visibility) {
|
||||
// TODO(charlie): Consider using the AST directly here rather than `Definition`.
|
||||
@@ -211,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
|
||||
@@ -283,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()),
|
||||
@@ -319,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()),
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/flake8_annotations/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
MissingReturnTypePublicFunction: bar
|
||||
location:
|
||||
row: 29
|
||||
column: 4
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
fix: ~
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind};
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
pub fn blind_except(checker: &mut Checker, handlers: &[Excepthandler]) {
|
||||
for handler in handlers {
|
||||
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
|
||||
continue;
|
||||
};
|
||||
let ExprKind::Name { id, .. } = &type_.node else {
|
||||
continue;
|
||||
};
|
||||
for exception in ["BaseException", "Exception"] {
|
||||
if id == exception {
|
||||
/// BLE001
|
||||
pub fn blind_except(
|
||||
checker: &mut Checker,
|
||||
type_: Option<&Expr>,
|
||||
name: Option<&str>,
|
||||
body: &[Stmt],
|
||||
) {
|
||||
let Some(type_) = type_ else {
|
||||
return;
|
||||
};
|
||||
let ExprKind::Name { id, .. } = &type_.node else {
|
||||
return;
|
||||
};
|
||||
for exception in ["BaseException", "Exception"] {
|
||||
if id == exception && checker.is_builtin(exception) {
|
||||
// If the exception is re-raised, don't flag an error.
|
||||
if !body.iter().any(|stmt| {
|
||||
if let StmtKind::Raise { exc, .. } = &stmt.node {
|
||||
if let Some(exc) = exc {
|
||||
if let ExprKind::Name { id, .. } = &exc.node {
|
||||
name.map_or(false, |name| name == id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::BlindExcept,
|
||||
CheckKind::BlindExcept(id.to_string()),
|
||||
Range::from_located(type_),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -2,31 +2,8 @@
|
||||
source: src/flake8_blind_except/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 5
|
||||
column: 7
|
||||
end_location:
|
||||
row: 5
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 13
|
||||
column: 7
|
||||
end_location:
|
||||
row: 13
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 23
|
||||
column: 7
|
||||
end_location:
|
||||
row: 23
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
- kind:
|
||||
BlindExcept: BaseException
|
||||
location:
|
||||
row: 25
|
||||
column: 7
|
||||
@@ -34,7 +11,8 @@ expression: checks
|
||||
row: 25
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
- kind:
|
||||
BlindExcept: Exception
|
||||
location:
|
||||
row: 31
|
||||
column: 7
|
||||
@@ -42,15 +20,8 @@ expression: checks
|
||||
row: 31
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 36
|
||||
column: 11
|
||||
end_location:
|
||||
row: 36
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
- kind:
|
||||
BlindExcept: Exception
|
||||
location:
|
||||
row: 42
|
||||
column: 7
|
||||
@@ -58,7 +29,8 @@ expression: checks
|
||||
row: 42
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
- kind:
|
||||
BlindExcept: BaseException
|
||||
location:
|
||||
row: 45
|
||||
column: 11
|
||||
@@ -66,15 +38,8 @@ expression: checks
|
||||
row: 45
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
location:
|
||||
row: 52
|
||||
column: 11
|
||||
end_location:
|
||||
row: 52
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: BlindExcept
|
||||
- kind:
|
||||
BlindExcept: Exception
|
||||
location:
|
||||
row: 54
|
||||
column: 7
|
||||
@@ -82,4 +47,22 @@ expression: checks
|
||||
row: 54
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
BlindExcept: Exception
|
||||
location:
|
||||
row: 60
|
||||
column: 7
|
||||
end_location:
|
||||
row: 60
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
BlindExcept: BaseException
|
||||
location:
|
||||
row: 62
|
||||
column: 7
|
||||
end_location:
|
||||
row: 62
|
||||
column: 20
|
||||
fix: ~
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ mod tests {
|
||||
#[test_case(CheckCode::B026, Path::new("B026.py"); "B026")]
|
||||
#[test_case(CheckCode::B027, Path::new("B027.py"); "B027")]
|
||||
#[test_case(CheckCode::B904, Path::new("B904.py"); "B904")]
|
||||
#[test_case(CheckCode::B905, Path::new("B905.py"); "B905")]
|
||||
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,6 +23,7 @@ pub use unused_loop_control_variable::unused_loop_control_variable;
|
||||
pub use useless_comparison::useless_comparison;
|
||||
pub use useless_contextlib_suppress::useless_contextlib_suppress;
|
||||
pub use useless_expression::useless_expression;
|
||||
pub use zip_without_explicit_strict::zip_without_explicit_strict;
|
||||
|
||||
mod abstract_base_class;
|
||||
mod assert_false;
|
||||
@@ -49,3 +50,4 @@ mod unused_loop_control_variable;
|
||||
mod useless_comparison;
|
||||
mod useless_contextlib_suppress;
|
||||
mod useless_expression;
|
||||
mod zip_without_explicit_strict;
|
||||
|
||||
31
src/flake8_bugbear/plugins/zip_without_explicit_strict.rs
Normal file
31
src/flake8_bugbear/plugins/zip_without_explicit_strict.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use rustpython_ast::{Expr, ExprKind, Keyword};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// B905
|
||||
pub fn zip_without_explicit_strict(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
kwargs: &[Keyword],
|
||||
) {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "zip"
|
||||
&& checker.is_builtin("zip")
|
||||
&& !kwargs.iter().any(|keyword| {
|
||||
keyword
|
||||
.node
|
||||
.arg
|
||||
.as_ref()
|
||||
.map_or(false, |name| name == "strict")
|
||||
})
|
||||
{
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::ZipWithoutExplicitStrict,
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
source: src/flake8_bugbear/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ZipWithoutExplicitStrict
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 5
|
||||
fix: ~
|
||||
- kind: ZipWithoutExplicitStrict
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind: ZipWithoutExplicitStrict
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind: ZipWithoutExplicitStrict
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: ZipWithoutExplicitStrict
|
||||
location:
|
||||
row: 4
|
||||
column: 15
|
||||
end_location:
|
||||
row: 4
|
||||
column: 23
|
||||
fix: ~
|
||||
- kind: ZipWithoutExplicitStrict
|
||||
location:
|
||||
row: 5
|
||||
column: 4
|
||||
end_location:
|
||||
row: 5
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind: ZipWithoutExplicitStrict
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 26
|
||||
fix: ~
|
||||
|
||||
36
src/flake8_import_conventions/checks.rs
Normal file
36
src/flake8_import_conventions/checks.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::Stmt;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// ICN001
|
||||
pub fn check_conventional_import(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: Option<&str>,
|
||||
conventions: &FxHashMap<String, String>,
|
||||
) -> Option<Check> {
|
||||
let mut is_valid_import = true;
|
||||
if let Some(expected_alias) = conventions.get(name) {
|
||||
if !expected_alias.is_empty() {
|
||||
if let Some(alias) = asname {
|
||||
if expected_alias != alias {
|
||||
is_valid_import = false;
|
||||
}
|
||||
} else {
|
||||
is_valid_import = false;
|
||||
}
|
||||
}
|
||||
if !is_valid_import {
|
||||
return Some(Check::new(
|
||||
CheckKind::ImportAliasIsNotConventional(
|
||||
name.to_string(),
|
||||
expected_alias.to_string(),
|
||||
),
|
||||
Range::from_located(import_from),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
101
src/flake8_import_conventions/mod.rs
Normal file
101
src/flake8_import_conventions/mod.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
pub mod checks;
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
use crate::{flake8_import_conventions, Settings};
|
||||
|
||||
#[test]
|
||||
fn defaults() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_import_conventions/defaults.py"),
|
||||
&Settings::for_rule(CheckCode::ICN001),
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!("defaults", checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_import_conventions/custom.py"),
|
||||
&Settings {
|
||||
flake8_import_conventions:
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: None,
|
||||
extend_aliases: Some(FxHashMap::from_iter([
|
||||
("dask.array".to_string(), "da".to_string()),
|
||||
("dask.dataframe".to_string(), "dd".to_string()),
|
||||
])),
|
||||
},
|
||||
),
|
||||
..Settings::for_rule(CheckCode::ICN001)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!("custom", checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_defaults() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_import_conventions/remove_default.py"),
|
||||
&Settings {
|
||||
flake8_import_conventions:
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: Some(FxHashMap::from_iter([
|
||||
("altair".to_string(), "alt".to_string()),
|
||||
("matplotlib.pyplot".to_string(), "plt".to_string()),
|
||||
("pandas".to_string(), "pd".to_string()),
|
||||
("seaborn".to_string(), "sns".to_string()),
|
||||
])),
|
||||
extend_aliases: None,
|
||||
},
|
||||
),
|
||||
..Settings::for_rule(CheckCode::ICN001)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!("remove_default", checks);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn override_defaults() -> Result<()> {
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_import_conventions/override_default.py"),
|
||||
&Settings {
|
||||
flake8_import_conventions:
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: None,
|
||||
extend_aliases: Some(FxHashMap::from_iter([(
|
||||
"numpy".to_string(),
|
||||
"nmp".to_string(),
|
||||
)])),
|
||||
},
|
||||
),
|
||||
..Settings::for_rule(CheckCode::ICN001)
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!("override_default", checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
94
src/flake8_import_conventions/settings.rs
Normal file
94
src/flake8_import_conventions/settings.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
//! Settings for import conventions.
|
||||
|
||||
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)] = &[
|
||||
("altair", "alt"),
|
||||
("matplotlib.pyplot", "plt"),
|
||||
("numpy", "np"),
|
||||
("pandas", "pd"),
|
||||
("seaborn", "sns"),
|
||||
];
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, ConfigurationOptions)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
#[option(
|
||||
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 = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
# Declare the default aliases.
|
||||
altair = "alt"
|
||||
matplotlib.pyplot = "plt"
|
||||
numpy = "np"
|
||||
pandas = "pd"
|
||||
seaborn = "sns"
|
||||
"#
|
||||
)]
|
||||
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 = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
# Declare a custom alias for the `matplotlib` module.
|
||||
"dask.dataframe" = "dd"
|
||||
"#
|
||||
)]
|
||||
pub extend_aliases: Option<FxHashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub aliases: FxHashMap<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::<FxHashMap<_, _>>()
|
||||
}
|
||||
|
||||
fn resolve_aliases(options: Options) -> FxHashMap<String, String> {
|
||||
let mut aliases = match options.aliases {
|
||||
Some(options_aliases) => options_aliases,
|
||||
None => default_aliases(),
|
||||
};
|
||||
if let Some(extend_aliases) = options.extend_aliases {
|
||||
aliases.extend(extend_aliases);
|
||||
}
|
||||
aliases
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
aliases: resolve_aliases(options),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
aliases: default_aliases(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
source: src/flake8_import_conventions/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- dask.array
|
||||
- da
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- dask.dataframe
|
||||
- dd
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- numpy
|
||||
- np
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 8
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 11
|
||||
column: 0
|
||||
end_location:
|
||||
row: 11
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 32
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- dask.array
|
||||
- da
|
||||
location:
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
column: 27
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- dask.dataframe
|
||||
- dd
|
||||
location:
|
||||
row: 14
|
||||
column: 0
|
||||
end_location:
|
||||
row: 14
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- numpy
|
||||
- np
|
||||
location:
|
||||
row: 15
|
||||
column: 0
|
||||
end_location:
|
||||
row: 15
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 16
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 17
|
||||
column: 0
|
||||
end_location:
|
||||
row: 17
|
||||
column: 22
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
source: src/flake8_import_conventions/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- numpy
|
||||
- np
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 32
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- numpy
|
||||
- np
|
||||
location:
|
||||
row: 11
|
||||
column: 0
|
||||
end_location:
|
||||
row: 11
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
column: 22
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
---
|
||||
source: src/flake8_import_conventions/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- numpy
|
||||
- nmp
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 32
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- numpy
|
||||
- nmp
|
||||
location:
|
||||
row: 11
|
||||
column: 0
|
||||
end_location:
|
||||
row: 11
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
column: 22
|
||||
fix: ~
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
source: src/flake8_import_conventions/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 13
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- altair
|
||||
- alt
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- matplotlib.pyplot
|
||||
- plt
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 32
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- pandas
|
||||
- pd
|
||||
location:
|
||||
row: 12
|
||||
column: 0
|
||||
end_location:
|
||||
row: 12
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
ImportAliasIsNotConventional:
|
||||
- seaborn
|
||||
- sns
|
||||
location:
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 13
|
||||
column: 22
|
||||
fix: ~
|
||||
|
||||
@@ -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"))]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user