Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a710e35ebc | ||
|
|
49df43bb78 | ||
|
|
e338d9acbe | ||
|
|
5c8655f479 | ||
|
|
60987888a2 | ||
|
|
a81581c781 | ||
|
|
3152dd7a8e | ||
|
|
528416f07a | ||
|
|
4405a6a903 | ||
|
|
35fa2a3c32 | ||
|
|
bb67fbb73a |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.167
|
||||
rev: v0.0.170
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.167-dev.0"
|
||||
version = "0.0.170-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1821,7 +1821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.167"
|
||||
version = "0.0.170"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1874,7 +1874,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.167"
|
||||
version = "0.0.170"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1892,7 +1892,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.161"
|
||||
version = "0.0.170"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.167"
|
||||
version = "0.0.170"
|
||||
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.170", 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)
|
||||
|
||||
43
README.md
43
README.md
@@ -72,7 +72,7 @@ of [Conda](https://docs.conda.io/en/latest/):
|
||||
1. [Pyflakes (F)](#pyflakes-f)
|
||||
1. [pycodestyle (E, W)](#pycodestyle-e-w)
|
||||
1. [mccabe (C90)](#mccabe-c90)
|
||||
1. [isort (I00)](#isort-i00)
|
||||
1. [isort (I)](#isort-i)
|
||||
1. [pydocstyle (D)](#pydocstyle-d)
|
||||
1. [pyupgrade (UP)](#pyupgrade-up)
|
||||
1. [pep8-naming (N)](#pep8-naming-n)
|
||||
@@ -89,7 +89,8 @@ of [Conda](https://docs.conda.io/en/latest/):
|
||||
1. [flake8-print (T20)](#flake8-print-t20)
|
||||
1. [flake8-quotes (Q)](#flake8-quotes-q)
|
||||
1. [flake8-return (RET)](#flake8-return-ret)
|
||||
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports-i25)
|
||||
1. [flake8-tidy-imports (TID)](#flake8-tidy-imports-tid)
|
||||
1. [flake8-unused-arguments (ARG)](#flake8-unused-arguments-arg)
|
||||
1. [eradicate (ERA)](#eradicate-era)
|
||||
1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh)
|
||||
1. [Pylint (PLC, PLE, PLR, PLW)](#pylint-plc-ple-plr-plw)
|
||||
@@ -146,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.167
|
||||
rev: v0.0.170
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -475,7 +476,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
|
||||
| ---- | ---- | ------- | --- |
|
||||
| C901 | FunctionIsTooComplex | `...` is too complex (10) | |
|
||||
|
||||
### isort (I00)
|
||||
### isort (I)
|
||||
|
||||
For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
|
||||
|
||||
@@ -760,13 +761,25 @@ For more, see [flake8-return](https://pypi.org/project/flake8-return/1.2.0/) on
|
||||
| RET507 | SuperfluousElseContinue | Unnecessary `else` after `continue` statement | |
|
||||
| RET508 | SuperfluousElseBreak | Unnecessary `else` after `break` statement | |
|
||||
|
||||
### flake8-tidy-imports (I25)
|
||||
### flake8-tidy-imports (TID)
|
||||
|
||||
For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/4.8.0/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| I252 | BannedRelativeImport | Relative imports are banned | |
|
||||
| TID252 | BannedRelativeImport | Relative imports are banned | |
|
||||
|
||||
### flake8-unused-arguments (ARG)
|
||||
|
||||
For more, see [flake8-unused-arguments](https://pypi.org/project/flake8-unused-arguments/0.0.12/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| ARG001 | UnusedFunctionArgument | Unused function argument: `...` | |
|
||||
| ARG002 | UnusedMethodArgument | Unused method argument: `...` | |
|
||||
| ARG003 | UnusedClassMethodArgument | Unused class method argument: `...` | |
|
||||
| ARG004 | UnusedStaticMethodArgument | Unused static method argument: `...` | |
|
||||
| ARG005 | UnusedLambdaArgument | Unused lambda argument: `...` | |
|
||||
|
||||
### eradicate (ERA)
|
||||
|
||||
@@ -795,9 +808,9 @@ 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 (RUF)
|
||||
@@ -967,6 +980,12 @@ natively, including:
|
||||
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (1/10)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
Note that, in some cases, Ruff uses different error code prefixes than would be found in the
|
||||
originating Flake8 plugins. For example, Ruff uses `TID252` to represent the `I252` rule from
|
||||
`flake8-tidy-imports`. This helps minimize conflicts across plugins and allows any individual plugin
|
||||
to be toggled on or off with a single (e.g.) `--select TID`, as opposed to `--select I2` (to avoid
|
||||
conflicts with the `isort` rules, like `I001`).
|
||||
|
||||
Beyond the rule set, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
|
||||
1. Ruff does not yet support a few Python 3.9 and 3.10 language features, including structural
|
||||
@@ -1465,7 +1484,7 @@ fix = true
|
||||
|
||||
A list of check code prefixes to consider autofix-able.
|
||||
|
||||
**Default value**: `["A", "ANN", "B", "BLE", "C", "D", "E", "F", "FBT", "I", "M", "N", "Q", "RUF", "S", "T", "U", "W", "YTT"]`
|
||||
**Default value**: `["A", "ANN", "ARG", "B", "BLE", "C", "D", "E", "ERA", "F", "FBT", "I", "ICN", "N", "PGH", "PLC", "PLE", "PLR", "PLW", "Q", "RET", "RUF", "S", "T", "TID", "UP", "W", "YTT"]`
|
||||
|
||||
**Type**: `Vec<CheckCodePrefix>`
|
||||
|
||||
@@ -1782,7 +1801,7 @@ The conventional aliases for imports. These aliases can be extended by the `exte
|
||||
|
||||
**Default value**: `{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}`
|
||||
|
||||
**Type**: `BTreeMap<String, String>`
|
||||
**Type**: `FxHashMap<String, String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
@@ -1804,7 +1823,7 @@ A mapping of modules to their conventional import aliases. These aliases will be
|
||||
|
||||
**Default value**: `{}`
|
||||
|
||||
**Type**: `BTreeMap<String, String>`
|
||||
**Type**: `FxHashMap<String, String>`
|
||||
|
||||
**Example usage**:
|
||||
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.167"
|
||||
version = "0.0.170"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.167"
|
||||
version = "0.0.170"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.167-dev.0"
|
||||
version = "0.0.170-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.167"
|
||||
version = "0.0.170"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -8,7 +8,7 @@ use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use codegen::{Scope, Type, Variant};
|
||||
use itertools::Itertools;
|
||||
use ruff::checks::{CheckCode, REDIRECTS};
|
||||
use ruff::checks::{CheckCode, CODE_REDIRECTS, PREFIX_REDIRECTS};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
const FILE: &str = "src/checks_gen.rs";
|
||||
@@ -39,34 +39,26 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Add any aliases (e.g., "U001" to "UP001").
|
||||
for (alias, check_code) in REDIRECTS.iter() {
|
||||
// Compute the length of the prefix and suffix for both codes.
|
||||
let code_str: String = check_code.as_ref().to_string();
|
||||
let code_prefix_len = code_str
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
.count();
|
||||
let code_suffix_len = code_str.len() - code_prefix_len;
|
||||
let alias_prefix_len = alias
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
.count();
|
||||
let alias_suffix_len = alias.len() - alias_prefix_len;
|
||||
assert_eq!(code_suffix_len, alias_suffix_len);
|
||||
for i in 0..=code_suffix_len {
|
||||
let source = code_str[..code_prefix_len + i].to_string();
|
||||
let destination = alias[..alias_prefix_len + i].to_string();
|
||||
if source != destination {
|
||||
prefix_to_codes.insert(
|
||||
destination,
|
||||
prefix_to_codes
|
||||
.get(&source)
|
||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {source:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Add any prefix aliases (e.g., "U" to "UP").
|
||||
for (alias, source) in PREFIX_REDIRECTS.iter() {
|
||||
prefix_to_codes.insert(
|
||||
(*alias).to_string(),
|
||||
prefix_to_codes
|
||||
.get(&(*source).to_string())
|
||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {source:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
// Add any check code aliases (e.g., "U001" to "UP001").
|
||||
for (alias, check_code) in CODE_REDIRECTS.iter() {
|
||||
prefix_to_codes.insert(
|
||||
(*alias).to_string(),
|
||||
prefix_to_codes
|
||||
.get(&check_code.as_ref().to_string())
|
||||
.unwrap_or_else(|| panic!("Unknown CheckCode: {alias:?}"))
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut scope = Scope::new();
|
||||
@@ -113,10 +105,10 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.line("#[allow(clippy::match_same_arms)]")
|
||||
.line("match self {");
|
||||
for (prefix, codes) in &prefix_to_codes {
|
||||
if let Some(target) = REDIRECTS.get(&prefix.as_str()) {
|
||||
if let Some(target) = CODE_REDIRECTS.get(&prefix.as_str()) {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
|
||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been renamed to \
|
||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
|
||||
`{}`\".bold()); \n vec![{}] }}",
|
||||
prefix,
|
||||
target.as_ref(),
|
||||
@@ -125,6 +117,18 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
||||
.join(", ")
|
||||
));
|
||||
} else if let Some(target) = PREFIX_REDIRECTS.get(&prefix.as_str()) {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
|
||||
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been remapped to \
|
||||
`{}`\".bold()); \n vec![{}] }}",
|
||||
prefix,
|
||||
target,
|
||||
codes
|
||||
.iter()
|
||||
.map(|code| format!("CheckCode::{}", code.as_ref()))
|
||||
.join(", ")
|
||||
));
|
||||
} else {
|
||||
gen = gen.line(format!(
|
||||
"CheckCodePrefix::{prefix} => vec![{}],",
|
||||
@@ -187,7 +191,9 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
output.push_str("pub const CATEGORIES: &[CheckCodePrefix] = &[");
|
||||
output.push('\n');
|
||||
for prefix in prefix_to_codes.keys() {
|
||||
if prefix.chars().all(char::is_alphabetic) {
|
||||
if prefix.chars().all(char::is_alphabetic)
|
||||
&& !PREFIX_REDIRECTS.contains_key(&prefix.as_str())
|
||||
{
|
||||
output.push_str(&format!("CheckCodePrefix::{prefix},"));
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.161"
|
||||
version = "0.0.170"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
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,5 @@
|
||||
pub mod cast;
|
||||
pub mod function_type;
|
||||
pub mod helpers;
|
||||
pub mod operations;
|
||||
pub mod relocate;
|
||||
|
||||
@@ -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,35 +30,49 @@ 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 uses_locals: bool,
|
||||
pub values: FxHashMap<&'a str, Binding>,
|
||||
}
|
||||
|
||||
@@ -68,6 +82,7 @@ impl<'a> Scope<'a> {
|
||||
id: id(),
|
||||
kind,
|
||||
import_starred: false,
|
||||
uses_locals: false,
|
||||
values: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
120
src/check_ast.rs
120
src/check_ast.rs
@@ -17,7 +17,8 @@ use crate::ast::helpers::{
|
||||
use crate::ast::operations::extract_all_names;
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
Binding, BindingContext, BindingKind, ClassScope, FunctionScope, Node, Range, Scope, ScopeKind,
|
||||
Binding, BindingContext, BindingKind, ClassDef, FunctionDef, Lambda, Node, Range, Scope,
|
||||
ScopeKind,
|
||||
};
|
||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
||||
use crate::ast::{helpers, operations, visitor};
|
||||
@@ -35,8 +36,9 @@ use crate::visibility::{module_visibility, transition_scope, Modifier, Visibilit
|
||||
use crate::{
|
||||
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
|
||||
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_debugger,
|
||||
flake8_import_conventions, flake8_print, flake8_return, flake8_tidy_imports, mccabe,
|
||||
pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade,
|
||||
flake8_import_conventions, flake8_print, flake8_return, flake8_tidy_imports,
|
||||
flake8_unused_arguments, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks,
|
||||
pylint, pyupgrade,
|
||||
};
|
||||
|
||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
@@ -71,7 +73,7 @@ pub struct Checker<'a> {
|
||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext)>,
|
||||
deferred_functions: Vec<(&'a Stmt, DeferralContext, VisibleScope)>,
|
||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext)>,
|
||||
deferred_assignments: Vec<usize>,
|
||||
deferred_assignments: Vec<(usize, DeferralContext)>,
|
||||
// Internal, derivative state.
|
||||
visible_scope: VisibleScope,
|
||||
in_f_string: Option<Range>,
|
||||
@@ -580,7 +582,7 @@ where
|
||||
for expr in decorator_list {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
self.push_scope(Scope::new(ScopeKind::Class(ClassScope {
|
||||
self.push_scope(Scope::new(ScopeKind::Class(ClassDef {
|
||||
name,
|
||||
bases,
|
||||
keywords,
|
||||
@@ -671,7 +673,7 @@ where
|
||||
pylint::plugins::useless_import_alias(self, alias);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::PLR0402) {
|
||||
pylint::plugins::consider_using_from_import(self, alias);
|
||||
pylint::plugins::use_from_import(self, alias);
|
||||
}
|
||||
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
@@ -902,7 +904,7 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::I252) {
|
||||
if self.settings.enabled.contains(&CheckCode::TID252) {
|
||||
if let Some(check) = flake8_tidy_imports::checks::banned_relative_import(
|
||||
stmt,
|
||||
level.as_ref(),
|
||||
@@ -1406,20 +1408,18 @@ where
|
||||
}
|
||||
Ok(summary) => {
|
||||
if self.settings.enabled.contains(&CheckCode::F522) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::string_dot_format_extra_named_arguments(
|
||||
&summary, keywords, location,
|
||||
)
|
||||
if let Some(check) = pyflakes::checks::string_dot_format_extra_named_arguments(
|
||||
&summary, keywords, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F523) {
|
||||
if let Some(check) =
|
||||
pyflakes::checks::string_dot_format_extra_positional_arguments(
|
||||
&summary, args, location,
|
||||
)
|
||||
if let Some(check) = pyflakes::checks::string_dot_format_extra_positional_arguments(
|
||||
&summary, args, location,
|
||||
)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
@@ -1486,7 +1486,7 @@ where
|
||||
.scope_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.any(|index| matches!(self.scopes[*index].kind, ScopeKind::Lambda))
|
||||
.any(|index| matches!(self.scopes[*index].kind, ScopeKind::Lambda(..)))
|
||||
{
|
||||
flake8_bugbear::plugins::setattr_with_constant(self, expr, func, args);
|
||||
}
|
||||
@@ -1747,9 +1747,7 @@ where
|
||||
if id == "locals" && matches!(ctx, ExprContext::Load) {
|
||||
let scope = &mut self.scopes
|
||||
[*(self.scope_stack.last().expect("No current scope found"))];
|
||||
if let ScopeKind::Function(inner) = &mut scope.kind {
|
||||
inner.uses_locals = true;
|
||||
}
|
||||
scope.uses_locals = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1775,7 +1773,7 @@ where
|
||||
pylint::plugins::unnecessary_direct_lambda_call(self, expr, func);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::PLR1722) {
|
||||
pylint::plugins::consider_using_sys_exit(self, func);
|
||||
pylint::plugins::use_sys_exit(self, func);
|
||||
}
|
||||
}
|
||||
ExprKind::Dict { keys, .. } => {
|
||||
@@ -2070,7 +2068,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Lambda { args, .. } => {
|
||||
ExprKind::Lambda { args, body, .. } => {
|
||||
// Visit the arguments, but avoid the body, which will be deferred.
|
||||
for arg in &args.posonlyargs {
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
@@ -2103,7 +2101,7 @@ where
|
||||
for expr in &args.defaults {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
self.push_scope(Scope::new(ScopeKind::Lambda));
|
||||
self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body })));
|
||||
}
|
||||
ExprKind::ListComp { elt, generators } | ExprKind::SetComp { elt, generators } => {
|
||||
if self.settings.enabled.contains(&CheckCode::C416) {
|
||||
@@ -2131,7 +2129,7 @@ where
|
||||
}
|
||||
ExprKind::BoolOp { op, values } => {
|
||||
if self.settings.enabled.contains(&CheckCode::PLR1701) {
|
||||
pylint::plugins::consider_merging_isinstance(self, expr, op, values);
|
||||
pylint::plugins::merge_isinstance(self, expr, op, values);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -2958,27 +2956,44 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn check_deferred_functions(&mut self) {
|
||||
while let Some((stmt, (scopes, parents), visibility)) = self.deferred_functions.pop() {
|
||||
self.scope_stack = scopes;
|
||||
self.parent_stack = parents;
|
||||
self.scope_stack = scopes.clone();
|
||||
self.parent_stack = parents.clone();
|
||||
self.visible_scope = visibility;
|
||||
self.push_scope(Scope::new(ScopeKind::Function(FunctionScope {
|
||||
async_: matches!(stmt.node, StmtKind::AsyncFunctionDef { .. }),
|
||||
uses_locals: false,
|
||||
})));
|
||||
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { body, args, .. }
|
||||
| StmtKind::AsyncFunctionDef { body, args, .. } => {
|
||||
StmtKind::FunctionDef {
|
||||
name,
|
||||
body,
|
||||
args,
|
||||
decorator_list,
|
||||
..
|
||||
}
|
||||
| StmtKind::AsyncFunctionDef {
|
||||
name,
|
||||
body,
|
||||
args,
|
||||
decorator_list,
|
||||
..
|
||||
} => {
|
||||
self.push_scope(Scope::new(ScopeKind::Function(FunctionDef {
|
||||
name,
|
||||
body,
|
||||
args,
|
||||
decorator_list,
|
||||
async_: matches!(stmt.node, StmtKind::AsyncFunctionDef { .. }),
|
||||
})));
|
||||
self.visit_arguments(args);
|
||||
for stmt in body {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => unreachable!("Expected StmtKind::FunctionDef | StmtKind::AsyncFunctionDef"),
|
||||
}
|
||||
|
||||
self.deferred_assignments
|
||||
.push(*self.scope_stack.last().expect("No current scope found"));
|
||||
self.deferred_assignments.push((
|
||||
*self.scope_stack.last().expect("No current scope found"),
|
||||
(scopes, parents),
|
||||
));
|
||||
|
||||
self.pop_scope();
|
||||
}
|
||||
@@ -2986,25 +3001,29 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn check_deferred_lambdas(&mut self) {
|
||||
while let Some((expr, (scopes, parents))) = self.deferred_lambdas.pop() {
|
||||
self.scope_stack = scopes;
|
||||
self.parent_stack = parents;
|
||||
self.push_scope(Scope::new(ScopeKind::Lambda));
|
||||
self.scope_stack = scopes.clone();
|
||||
self.parent_stack = parents.clone();
|
||||
|
||||
if let ExprKind::Lambda { args, body } = &expr.node {
|
||||
self.push_scope(Scope::new(ScopeKind::Lambda(Lambda { args, body })));
|
||||
self.visit_arguments(args);
|
||||
self.visit_expr(body);
|
||||
} else {
|
||||
unreachable!("Expected ExprKind::Lambda");
|
||||
}
|
||||
|
||||
self.deferred_assignments
|
||||
.push(*self.scope_stack.last().expect("No current scope found"));
|
||||
self.deferred_assignments.push((
|
||||
*self.scope_stack.last().expect("No current scope found"),
|
||||
(scopes, parents),
|
||||
));
|
||||
|
||||
self.pop_scope();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_deferred_assignments(&mut self) {
|
||||
if self.settings.enabled.contains(&CheckCode::F841) {
|
||||
while let Some(index) = self.deferred_assignments.pop() {
|
||||
while let Some((index, (scopes, _parents))) = self.deferred_assignments.pop() {
|
||||
if self.settings.enabled.contains(&CheckCode::F841) {
|
||||
self.add_checks(
|
||||
pyflakes::checks::unused_variables(
|
||||
&self.scopes[index],
|
||||
@@ -3013,6 +3032,23 @@ impl<'a> Checker<'a> {
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::ARG001)
|
||||
|| self.settings.enabled.contains(&CheckCode::ARG002)
|
||||
|| self.settings.enabled.contains(&CheckCode::ARG003)
|
||||
|| self.settings.enabled.contains(&CheckCode::ARG004)
|
||||
|| self.settings.enabled.contains(&CheckCode::ARG005)
|
||||
{
|
||||
self.add_checks(
|
||||
flake8_unused_arguments::plugins::unused_arguments(
|
||||
self,
|
||||
&self.scopes[*scopes
|
||||
.last()
|
||||
.expect("Expected parent scope above function scope")],
|
||||
&self.scopes[index],
|
||||
)
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3092,7 +3128,7 @@ impl<'a> Checker<'a> {
|
||||
for (name, binding) in &scope.values {
|
||||
let (BindingKind::Importation(_, full_name, context)
|
||||
| BindingKind::SubmoduleImportation(_, full_name, context)
|
||||
| BindingKind::FromImportation(_, full_name, context)) = &binding.kind else { continue };
|
||||
| BindingKind::FromImportation(_, full_name, context)) = &binding.kind else { continue; };
|
||||
|
||||
// Skip used exports from `__all__`
|
||||
if binding.used.is_some()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Lint rules based on import analysis.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_parser::ast::Suite;
|
||||
|
||||
use crate::ast::visitor::Visitor;
|
||||
@@ -33,8 +35,9 @@ pub fn check_imports(
|
||||
directives: &IsortDirectives,
|
||||
settings: &Settings,
|
||||
autofix: bool,
|
||||
path: &Path,
|
||||
) -> Vec<Check> {
|
||||
let mut tracker = ImportTracker::new(directives);
|
||||
let mut tracker = ImportTracker::new(directives, path);
|
||||
for stmt in python_ast {
|
||||
tracker.visit_stmt(stmt);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::checks::{Check, CheckCode, CheckKind, REDIRECTS};
|
||||
use crate::checks::{Check, CheckCode, CheckKind, CODE_REDIRECTS};
|
||||
use crate::noqa;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::settings::Settings;
|
||||
@@ -209,7 +209,7 @@ pub fn check_lines(
|
||||
let mut invalid_codes = vec![];
|
||||
let mut valid_codes = vec![];
|
||||
for code in codes {
|
||||
let code = REDIRECTS.get(code).map_or(code, AsRef::as_ref);
|
||||
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
valid_codes.push(code.to_string());
|
||||
} else {
|
||||
|
||||
@@ -160,7 +160,7 @@ pub enum CheckCode {
|
||||
// mccabe
|
||||
C901,
|
||||
// flake8-tidy-imports
|
||||
I252,
|
||||
TID252,
|
||||
// flake8-return
|
||||
RET501,
|
||||
RET502,
|
||||
@@ -292,6 +292,12 @@ pub enum CheckCode {
|
||||
FBT001,
|
||||
FBT002,
|
||||
FBT003,
|
||||
// flake8-unused-arguments
|
||||
ARG001,
|
||||
ARG002,
|
||||
ARG003,
|
||||
ARG004,
|
||||
ARG005,
|
||||
// flake8-import-conventions
|
||||
ICN001,
|
||||
// Ruff
|
||||
@@ -326,6 +332,7 @@ pub enum CheckCategory {
|
||||
Flake8Quotes,
|
||||
Flake8Return,
|
||||
Flake8TidyImports,
|
||||
Flake8UnusedArguments,
|
||||
Eradicate,
|
||||
PygrepHooks,
|
||||
Pylint,
|
||||
@@ -364,6 +371,7 @@ impl CheckCategory {
|
||||
CheckCategory::Flake8Quotes => "flake8-quotes",
|
||||
CheckCategory::Flake8Return => "flake8-return",
|
||||
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
|
||||
CheckCategory::Flake8UnusedArguments => "flake8-unused-arguments",
|
||||
CheckCategory::Isort => "isort",
|
||||
CheckCategory::McCabe => "mccabe",
|
||||
CheckCategory::PEP8Naming => "pep8-naming",
|
||||
@@ -392,8 +400,9 @@ impl CheckCategory {
|
||||
CheckCategory::Flake8Print => vec![CheckCodePrefix::T20],
|
||||
CheckCategory::Flake8Quotes => vec![CheckCodePrefix::Q],
|
||||
CheckCategory::Flake8Return => vec![CheckCodePrefix::RET],
|
||||
CheckCategory::Flake8TidyImports => vec![CheckCodePrefix::I25],
|
||||
CheckCategory::Isort => vec![CheckCodePrefix::I00],
|
||||
CheckCategory::Flake8TidyImports => vec![CheckCodePrefix::TID],
|
||||
CheckCategory::Flake8UnusedArguments => vec![CheckCodePrefix::ARG],
|
||||
CheckCategory::Isort => vec![CheckCodePrefix::I],
|
||||
CheckCategory::McCabe => vec![CheckCodePrefix::C90],
|
||||
CheckCategory::PEP8Naming => vec![CheckCodePrefix::N],
|
||||
CheckCategory::Pycodestyle => vec![CheckCodePrefix::E, CheckCodePrefix::W],
|
||||
@@ -470,6 +479,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))
|
||||
}
|
||||
@@ -625,7 +638,7 @@ pub enum CheckKind {
|
||||
ConsiderUsingFromImport(String, String),
|
||||
AwaitOutsideAsync,
|
||||
UselessElseOnLoop,
|
||||
ConsiderUsingSysExit,
|
||||
UseSysExit(String),
|
||||
// flake8-builtins
|
||||
BuiltinVariableShadowing(String),
|
||||
BuiltinArgumentShadowing(String),
|
||||
@@ -817,6 +830,12 @@ pub enum CheckKind {
|
||||
BooleanPositionalValueInFunctionCall,
|
||||
// pygrep-hooks
|
||||
NoEval,
|
||||
// flake8-unused-arguments
|
||||
UnusedFunctionArgument(String),
|
||||
UnusedMethodArgument(String),
|
||||
UnusedClassMethodArgument(String),
|
||||
UnusedStaticMethodArgument(String),
|
||||
UnusedLambdaArgument(String),
|
||||
// flake8-import-conventions
|
||||
ImportAliasIsNotConventional(String, String),
|
||||
// Ruff
|
||||
@@ -931,7 +950,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()),
|
||||
@@ -1003,7 +1022,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,
|
||||
@@ -1159,6 +1178,12 @@ impl CheckCode {
|
||||
CheckCode::FBT003 => CheckKind::BooleanPositionalValueInFunctionCall,
|
||||
// pygrep-hooks
|
||||
CheckCode::PGH001 => CheckKind::NoEval,
|
||||
// flake8-unused-arguments
|
||||
CheckCode::ARG001 => CheckKind::UnusedFunctionArgument("...".to_string()),
|
||||
CheckCode::ARG002 => CheckKind::UnusedMethodArgument("...".to_string()),
|
||||
CheckCode::ARG003 => CheckKind::UnusedClassMethodArgument("...".to_string()),
|
||||
CheckCode::ARG004 => CheckKind::UnusedStaticMethodArgument("...".to_string()),
|
||||
CheckCode::ARG005 => CheckKind::UnusedLambdaArgument("...".to_string()),
|
||||
// flake8-import-conventions
|
||||
CheckCode::ICN001 => {
|
||||
CheckKind::ImportAliasIsNotConventional("...".to_string(), "...".to_string())
|
||||
@@ -1188,6 +1213,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,
|
||||
@@ -1339,8 +1369,8 @@ impl CheckCode {
|
||||
CheckCode::FBT002 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
|
||||
CheckCode::I001 => CheckCategory::Isort,
|
||||
CheckCode::TID252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::ICN001 => CheckCategory::Flake8ImportConventions,
|
||||
CheckCode::I252 => CheckCategory::Flake8TidyImports,
|
||||
CheckCode::N801 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N802 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N803 => CheckCategory::PEP8Naming,
|
||||
@@ -1493,7 +1523,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,
|
||||
@@ -1550,7 +1580,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,
|
||||
@@ -1686,6 +1716,12 @@ impl CheckKind {
|
||||
CheckKind::BooleanPositionalValueInFunctionCall => &CheckCode::FBT003,
|
||||
// pygrep-hooks
|
||||
CheckKind::NoEval => &CheckCode::PGH001,
|
||||
// flake8-unused-arguments
|
||||
CheckKind::UnusedFunctionArgument(..) => &CheckCode::ARG001,
|
||||
CheckKind::UnusedMethodArgument(..) => &CheckCode::ARG002,
|
||||
CheckKind::UnusedClassMethodArgument(..) => &CheckCode::ARG003,
|
||||
CheckKind::UnusedStaticMethodArgument(..) => &CheckCode::ARG004,
|
||||
CheckKind::UnusedLambdaArgument(..) => &CheckCode::ARG005,
|
||||
// flake8-import-conventions
|
||||
CheckKind::ImportAliasIsNotConventional(..) => &CheckCode::ICN001,
|
||||
// Ruff
|
||||
@@ -1885,7 +1921,7 @@ 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}")
|
||||
@@ -1897,7 +1933,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()
|
||||
@@ -1905,7 +1941,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")
|
||||
@@ -2477,6 +2513,18 @@ impl CheckKind {
|
||||
}
|
||||
// pygrep-hooks
|
||||
CheckKind::NoEval => "No builtin `eval()` allowed".to_string(),
|
||||
// flake8-unused-arguments
|
||||
CheckKind::UnusedFunctionArgument(name) => {
|
||||
format!("Unused function argument: `{name}`")
|
||||
}
|
||||
CheckKind::UnusedMethodArgument(name) => format!("Unused method argument: `{name}`"),
|
||||
CheckKind::UnusedClassMethodArgument(name) => {
|
||||
format!("Unused class method argument: `{name}`")
|
||||
}
|
||||
CheckKind::UnusedStaticMethodArgument(name) => {
|
||||
format!("Unused static method argument: `{name}`")
|
||||
}
|
||||
CheckKind::UnusedLambdaArgument(name) => format!("Unused lambda argument: `{name}`"),
|
||||
// flake8-import-conventions
|
||||
CheckKind::ImportAliasIsNotConventional(name, asname) => {
|
||||
format!("`{name}` should be imported as `{asname}`")
|
||||
@@ -2551,7 +2599,6 @@ impl CheckKind {
|
||||
| CheckKind::BlankLineBeforeSection(..)
|
||||
| CheckKind::CapitalizeSectionName(..)
|
||||
| CheckKind::CommentedOutCode
|
||||
| CheckKind::ConsiderUsingSysExit
|
||||
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
|
||||
| CheckKind::ConvertTypedDictFunctionalToClass(..)
|
||||
| CheckKind::DashedUnderlineAfterSection(..)
|
||||
@@ -2617,6 +2664,7 @@ impl CheckKind {
|
||||
| CheckKind::UnusedNOQA(..)
|
||||
| CheckKind::UsePEP585Annotation(..)
|
||||
| CheckKind::UsePEP604Annotation
|
||||
| CheckKind::UseSysExit(..)
|
||||
| CheckKind::UselessImportAlias
|
||||
| CheckKind::UselessMetaclassType
|
||||
| CheckKind::UselessObjectInheritance(..)
|
||||
@@ -2648,7 +2696,7 @@ impl Check {
|
||||
}
|
||||
|
||||
/// A hash map from deprecated to latest `CheckCode`.
|
||||
pub static REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||
FxHashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U001", CheckCode::UP001),
|
||||
@@ -2665,6 +2713,25 @@ pub static REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
|
||||
("U013", CheckCode::UP013),
|
||||
("U014", CheckCode::UP014),
|
||||
("U015", CheckCode::UP015),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I252", CheckCode::TID252),
|
||||
("M001", CheckCode::RUF100),
|
||||
])
|
||||
});
|
||||
|
||||
/// A hash map from deprecated `CheckCodePrefix` to latest `CheckCodePrefix`.
|
||||
pub static PREFIX_REDIRECTS: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
FxHashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U", "UP"),
|
||||
("U0", "UP0"),
|
||||
("U00", "UP00"),
|
||||
("U01", "UP01"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I2", "TID2"),
|
||||
("I25", "TID25"),
|
||||
("M", "RUF100"),
|
||||
("M0", "RUF100"),
|
||||
])
|
||||
});
|
||||
|
||||
|
||||
@@ -36,6 +36,14 @@ pub enum CheckCodePrefix {
|
||||
ANN4,
|
||||
ANN40,
|
||||
ANN401,
|
||||
ARG,
|
||||
ARG0,
|
||||
ARG00,
|
||||
ARG001,
|
||||
ARG002,
|
||||
ARG003,
|
||||
ARG004,
|
||||
ARG005,
|
||||
B,
|
||||
B0,
|
||||
B00,
|
||||
@@ -263,6 +271,9 @@ pub enum CheckCodePrefix {
|
||||
ICN0,
|
||||
ICN00,
|
||||
ICN001,
|
||||
M,
|
||||
M0,
|
||||
M001,
|
||||
N,
|
||||
N8,
|
||||
N80,
|
||||
@@ -367,6 +378,10 @@ pub enum CheckCodePrefix {
|
||||
T20,
|
||||
T201,
|
||||
T203,
|
||||
TID,
|
||||
TID2,
|
||||
TID25,
|
||||
TID252,
|
||||
U,
|
||||
U0,
|
||||
U00,
|
||||
@@ -492,6 +507,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,
|
||||
@@ -1140,17 +1181,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,
|
||||
@@ -1379,54 +1471,82 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T201 => vec![CheckCode::T201],
|
||||
CheckCodePrefix::T203 => vec![CheckCode::T203],
|
||||
CheckCodePrefix::U => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U0 => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U00 => vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
],
|
||||
CheckCodePrefix::TID => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::TID2 => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::TID25 => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::TID252 => vec![CheckCode::TID252],
|
||||
CheckCodePrefix::U => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U` has been remapped to `UP`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U0 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U0` has been remapped to `UP0`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U00 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U00` has been remapped to `UP00`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP001,
|
||||
CheckCode::UP003,
|
||||
CheckCode::UP004,
|
||||
CheckCode::UP005,
|
||||
CheckCode::UP006,
|
||||
CheckCode::UP007,
|
||||
CheckCode::UP008,
|
||||
CheckCode::UP009,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U001 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U001` has been renamed to `UP001`".bold()
|
||||
"`U001` has been remapped to `UP001`".bold()
|
||||
);
|
||||
vec![CheckCode::UP001]
|
||||
}
|
||||
@@ -1435,7 +1555,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U003` has been renamed to `UP003`".bold()
|
||||
"`U003` has been remapped to `UP003`".bold()
|
||||
);
|
||||
vec![CheckCode::UP003]
|
||||
}
|
||||
@@ -1444,7 +1564,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U004` has been renamed to `UP004`".bold()
|
||||
"`U004` has been remapped to `UP004`".bold()
|
||||
);
|
||||
vec![CheckCode::UP004]
|
||||
}
|
||||
@@ -1453,7 +1573,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U005` has been renamed to `UP005`".bold()
|
||||
"`U005` has been remapped to `UP005`".bold()
|
||||
);
|
||||
vec![CheckCode::UP005]
|
||||
}
|
||||
@@ -1462,7 +1582,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U006` has been renamed to `UP006`".bold()
|
||||
"`U006` has been remapped to `UP006`".bold()
|
||||
);
|
||||
vec![CheckCode::UP006]
|
||||
}
|
||||
@@ -1471,7 +1591,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U007` has been renamed to `UP007`".bold()
|
||||
"`U007` has been remapped to `UP007`".bold()
|
||||
);
|
||||
vec![CheckCode::UP007]
|
||||
}
|
||||
@@ -1480,7 +1600,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U008` has been renamed to `UP008`".bold()
|
||||
"`U008` has been remapped to `UP008`".bold()
|
||||
);
|
||||
vec![CheckCode::UP008]
|
||||
}
|
||||
@@ -1489,24 +1609,32 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U009` has been renamed to `UP009`".bold()
|
||||
"`U009` has been remapped to `UP009`".bold()
|
||||
);
|
||||
vec![CheckCode::UP009]
|
||||
}
|
||||
CheckCodePrefix::U01 => vec![
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
],
|
||||
CheckCodePrefix::U01 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U01` has been remapped to `UP01`".bold()
|
||||
);
|
||||
vec![
|
||||
CheckCode::UP010,
|
||||
CheckCode::UP011,
|
||||
CheckCode::UP012,
|
||||
CheckCode::UP013,
|
||||
CheckCode::UP014,
|
||||
CheckCode::UP015,
|
||||
]
|
||||
}
|
||||
CheckCodePrefix::U010 => {
|
||||
eprintln!(
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U010` has been renamed to `UP010`".bold()
|
||||
"`U010` has been remapped to `UP010`".bold()
|
||||
);
|
||||
vec![CheckCode::UP010]
|
||||
}
|
||||
@@ -1515,7 +1643,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U011` has been renamed to `UP011`".bold()
|
||||
"`U011` has been remapped to `UP011`".bold()
|
||||
);
|
||||
vec![CheckCode::UP011]
|
||||
}
|
||||
@@ -1524,7 +1652,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U012` has been renamed to `UP012`".bold()
|
||||
"`U012` has been remapped to `UP012`".bold()
|
||||
);
|
||||
vec![CheckCode::UP012]
|
||||
}
|
||||
@@ -1533,7 +1661,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U013` has been renamed to `UP013`".bold()
|
||||
"`U013` has been remapped to `UP013`".bold()
|
||||
);
|
||||
vec![CheckCode::UP013]
|
||||
}
|
||||
@@ -1542,7 +1670,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U014` has been renamed to `UP014`".bold()
|
||||
"`U014` has been remapped to `UP014`".bold()
|
||||
);
|
||||
vec![CheckCode::UP014]
|
||||
}
|
||||
@@ -1551,7 +1679,7 @@ impl CheckCodePrefix {
|
||||
"{}{} {}",
|
||||
"warning".yellow().bold(),
|
||||
":".bold(),
|
||||
"`U015` has been renamed to `UP015`".bold()
|
||||
"`U015` has been remapped to `UP015`".bold()
|
||||
);
|
||||
vec![CheckCode::UP015]
|
||||
}
|
||||
@@ -1698,6 +1826,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,
|
||||
@@ -1925,6 +2061,9 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::ICN0 => SuffixLength::One,
|
||||
CheckCodePrefix::ICN00 => SuffixLength::Two,
|
||||
CheckCodePrefix::ICN001 => SuffixLength::Three,
|
||||
CheckCodePrefix::M => SuffixLength::Zero,
|
||||
CheckCodePrefix::M0 => SuffixLength::One,
|
||||
CheckCodePrefix::M001 => SuffixLength::Three,
|
||||
CheckCodePrefix::N => SuffixLength::Zero,
|
||||
CheckCodePrefix::N8 => SuffixLength::One,
|
||||
CheckCodePrefix::N80 => SuffixLength::Two,
|
||||
@@ -2029,6 +2168,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::T20 => SuffixLength::Two,
|
||||
CheckCodePrefix::T201 => SuffixLength::Three,
|
||||
CheckCodePrefix::T203 => SuffixLength::Three,
|
||||
CheckCodePrefix::TID => SuffixLength::Zero,
|
||||
CheckCodePrefix::TID2 => SuffixLength::One,
|
||||
CheckCodePrefix::TID25 => SuffixLength::Two,
|
||||
CheckCodePrefix::TID252 => SuffixLength::Three,
|
||||
CheckCodePrefix::U => SuffixLength::Zero,
|
||||
CheckCodePrefix::U0 => SuffixLength::One,
|
||||
CheckCodePrefix::U00 => SuffixLength::Two,
|
||||
@@ -2096,6 +2239,7 @@ impl CheckCodePrefix {
|
||||
pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::A,
|
||||
CheckCodePrefix::ANN,
|
||||
CheckCodePrefix::ARG,
|
||||
CheckCodePrefix::B,
|
||||
CheckCodePrefix::BLE,
|
||||
CheckCodePrefix::C,
|
||||
@@ -2117,7 +2261,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
|
||||
CheckCodePrefix::RUF,
|
||||
CheckCodePrefix::S,
|
||||
CheckCodePrefix::T,
|
||||
CheckCodePrefix::U,
|
||||
CheckCodePrefix::TID,
|
||||
CheckCodePrefix::UP,
|
||||
CheckCodePrefix::W,
|
||||
CheckCodePrefix::YTT,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use rustpython_ast::{Arguments, Expr, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::cast;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
use crate::visibility;
|
||||
@@ -32,7 +33,7 @@ pub fn overloaded_name(checker: &Checker, definition: &Definition) -> Option<Str
|
||||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, stmt) {
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
Some(name.to_string())
|
||||
} else {
|
||||
@@ -50,7 +51,7 @@ pub fn is_overload_impl(checker: &Checker, definition: &Definition, overloaded_n
|
||||
| DefinitionKind::NestedFunction(stmt)
|
||||
| DefinitionKind::Method(stmt) = definition.kind
|
||||
{
|
||||
if visibility::is_overload(checker, stmt) {
|
||||
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
|
||||
false
|
||||
} else {
|
||||
let (name, ..) = match_function_def(stmt);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::ast::{cast, visitor};
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{CheckCode, CheckKind};
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
@@ -192,7 +192,10 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
.chain(args.kwonlyargs.iter())
|
||||
.skip(
|
||||
// If this is a non-static method, skip `cls` or `self`.
|
||||
usize::from(!visibility::is_staticmethod(checker, stmt)),
|
||||
usize::from(!visibility::is_staticmethod(
|
||||
checker,
|
||||
cast::decorator_list(stmt),
|
||||
)),
|
||||
)
|
||||
{
|
||||
// ANN401 for dynamically typed arguments
|
||||
@@ -264,10 +267,10 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
}
|
||||
|
||||
// ANN101, ANN102
|
||||
if !visibility::is_staticmethod(checker, stmt) {
|
||||
if !visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||
if let Some(arg) = args.args.first() {
|
||||
if arg.node.annotation.is_none() {
|
||||
if visibility::is_classmethod(checker, stmt) {
|
||||
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.enabled.contains(&CheckCode::ANN102) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MissingTypeCls(arg.node.arg.to_string()),
|
||||
@@ -300,14 +303,14 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
return;
|
||||
}
|
||||
|
||||
if visibility::is_classmethod(checker, stmt) {
|
||||
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.enabled.contains(&CheckCode::ANN206) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MissingReturnTypeClassMethod(name.to_string()),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
} else if visibility::is_staticmethod(checker, stmt) {
|
||||
} else if visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.enabled.contains(&CheckCode::ANN205) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MissingReturnTypeStaticMethod(name.to_string()),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{
|
||||
Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location, Stmt,
|
||||
};
|
||||
@@ -27,9 +26,9 @@ fn duplicate_handler_exceptions<'a>(
|
||||
checker: &mut Checker,
|
||||
expr: &'a Expr,
|
||||
elts: &'a [Expr],
|
||||
) -> BTreeSet<Vec<&'a str>> {
|
||||
let mut seen: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
let mut duplicates: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
) -> FxHashSet<Vec<&'a str>> {
|
||||
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut unique_elts: Vec<&Expr> = Vec::default();
|
||||
for type_ in elts {
|
||||
let call_path = helpers::collect_call_paths(type_);
|
||||
@@ -76,8 +75,8 @@ fn duplicate_handler_exceptions<'a>(
|
||||
}
|
||||
|
||||
pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Excepthandler]) {
|
||||
let mut seen: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
let mut duplicates: BTreeSet<Vec<&str>> = BTreeSet::default();
|
||||
let mut seen: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
let mut duplicates: FxHashSet<Vec<&str>> = FxHashSet::default();
|
||||
for handler in handlers {
|
||||
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
|
||||
continue;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_ast::Stmt;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
@@ -10,7 +9,7 @@ pub fn check_conventional_import(
|
||||
import_from: &Stmt,
|
||||
name: &str,
|
||||
asname: Option<&str>,
|
||||
conventions: &BTreeMap<String, String>,
|
||||
conventions: &FxHashMap<String, String>,
|
||||
) -> Option<Check> {
|
||||
let mut is_valid_import = true;
|
||||
if let Some(expected_alias) = conventions.get(name) {
|
||||
|
||||
@@ -3,10 +3,11 @@ pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
@@ -33,7 +34,7 @@ mod tests {
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: None,
|
||||
extend_aliases: Some(BTreeMap::from([
|
||||
extend_aliases: Some(FxHashMap::from_iter([
|
||||
("dask.array".to_string(), "da".to_string()),
|
||||
("dask.dataframe".to_string(), "dd".to_string()),
|
||||
])),
|
||||
@@ -56,7 +57,7 @@ mod tests {
|
||||
flake8_import_conventions:
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: Some(BTreeMap::from([
|
||||
aliases: Some(FxHashMap::from_iter([
|
||||
("altair".to_string(), "alt".to_string()),
|
||||
("matplotlib.pyplot".to_string(), "plt".to_string()),
|
||||
("pandas".to_string(), "pd".to_string()),
|
||||
@@ -83,7 +84,7 @@ mod tests {
|
||||
flake8_import_conventions::settings::Settings::from_options(
|
||||
flake8_import_conventions::settings::Options {
|
||||
aliases: None,
|
||||
extend_aliases: Some(BTreeMap::from([(
|
||||
extend_aliases: Some(FxHashMap::from_iter([(
|
||||
"numpy".to_string(),
|
||||
"nmp".to_string(),
|
||||
)])),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
//! Settings for import conventions.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_macros::ConfigurationOptions;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
|
||||
@@ -20,7 +22,7 @@ pub struct Options {
|
||||
doc = "The conventional aliases for imports. These aliases can be extended by the \
|
||||
`extend_aliases` option.",
|
||||
default = r#"{"altair": "alt", "matplotlib.pyplot": "plt", "numpy": "np", "pandas": "pd", "seaborn": "sns"}"#,
|
||||
value_type = "BTreeMap<String, String>",
|
||||
value_type = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
# Declare the default aliases.
|
||||
altair = "alt"
|
||||
@@ -30,33 +32,41 @@ pub struct Options {
|
||||
seaborn = "sns"
|
||||
"#
|
||||
)]
|
||||
pub aliases: Option<BTreeMap<String, String>>,
|
||||
pub aliases: Option<FxHashMap<String, String>>,
|
||||
#[option(
|
||||
doc = "A mapping of modules to their conventional import aliases. These aliases will be \
|
||||
added to the `aliases` mapping.",
|
||||
default = r#"{}"#,
|
||||
value_type = "BTreeMap<String, String>",
|
||||
value_type = "FxHashMap<String, String>",
|
||||
example = r#"
|
||||
# Declare a custom alias for the `matplotlib` module.
|
||||
"dask.dataframe" = "dd"
|
||||
"#
|
||||
)]
|
||||
pub extend_aliases: Option<BTreeMap<String, String>>,
|
||||
pub extend_aliases: Option<FxHashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub aliases: BTreeMap<String, String>,
|
||||
pub aliases: FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
fn default_aliases() -> BTreeMap<String, String> {
|
||||
impl Hash for Settings {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for value in self.aliases.iter().sorted() {
|
||||
value.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_aliases() -> FxHashMap<String, String> {
|
||||
CONVENTIONAL_ALIASES
|
||||
.iter()
|
||||
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
.collect::<FxHashMap<_, _>>()
|
||||
}
|
||||
|
||||
fn resolve_aliases(options: Options) -> BTreeMap<String, String> {
|
||||
fn resolve_aliases(options: Options) -> FxHashMap<String, String> {
|
||||
let mut aliases = match options.aliases {
|
||||
Some(options_aliases) => options_aliases,
|
||||
None => default_aliases(),
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
184
src/flake8_unused_arguments/plugins.rs
Normal file
184
src/flake8_unused_arguments/plugins.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
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,
|
||||
bindings: &FxHashMap<&str, 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) = bindings.get(arg_name) {
|
||||
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,
|
||||
bindings: &FxHashMap<&str, 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) = bindings.get(&arg.node.arg.as_str()) {
|
||||
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) -> 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,
|
||||
&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,
|
||||
&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,
|
||||
&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,
|
||||
&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,
|
||||
&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"))]
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 0
|
||||
fix:
|
||||
content: "import a\nimport b\n\n"
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
fix:
|
||||
content: "import os\nimport sys\n\n"
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 14
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
column: 0
|
||||
fix:
|
||||
content: "import os\nimport sys\n\n"
|
||||
location:
|
||||
row: 14
|
||||
column: 0
|
||||
end_location:
|
||||
row: 16
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
fix:
|
||||
content: " import collections\n import typing\n\n"
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_ast::{
|
||||
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
|
||||
ExcepthandlerKind, Expr, ExprContext, Keyword, MatchCase, Operator, Pattern, Stmt, StmtKind,
|
||||
@@ -20,16 +22,18 @@ pub struct Block<'a> {
|
||||
}
|
||||
|
||||
pub struct ImportTracker<'a> {
|
||||
blocks: Vec<Block<'a>>,
|
||||
directives: &'a IsortDirectives,
|
||||
pyi: bool,
|
||||
blocks: Vec<Block<'a>>,
|
||||
split_index: usize,
|
||||
nested: bool,
|
||||
}
|
||||
|
||||
impl<'a> ImportTracker<'a> {
|
||||
pub fn new(directives: &'a IsortDirectives) -> Self {
|
||||
pub fn new(directives: &'a IsortDirectives, path: &'a Path) -> Self {
|
||||
Self {
|
||||
directives,
|
||||
pyi: path.extension().map_or(false, |ext| ext == "pyi"),
|
||||
blocks: vec![Block::default()],
|
||||
split_index: 0,
|
||||
nested: false,
|
||||
@@ -41,6 +45,34 @@ impl<'a> ImportTracker<'a> {
|
||||
self.blocks[index].imports.push(stmt);
|
||||
}
|
||||
|
||||
fn trailer_for(&self, stmt: &'a Stmt) -> Option<Trailer> {
|
||||
if self.pyi {
|
||||
// Black treats interface files differently, limiting to one newline
|
||||
// (`Trailing::Sibling`), and avoiding inserting any newlines in nested function
|
||||
// blocks.
|
||||
if self.nested
|
||||
&& matches!(
|
||||
stmt.node,
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. }
|
||||
)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(Trailer::Sibling)
|
||||
}
|
||||
} else if self.nested {
|
||||
Some(Trailer::Sibling)
|
||||
} else {
|
||||
Some(match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
StmtKind::ClassDef { .. } => Trailer::ClassDef,
|
||||
_ => Trailer::Sibling,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(&mut self, trailer: Option<Trailer>) {
|
||||
let index = self.blocks.len() - 1;
|
||||
if !self.blocks[index].imports.is_empty() {
|
||||
@@ -62,17 +94,7 @@ where
|
||||
// Track manual splits.
|
||||
while self.split_index < self.directives.splits.len() {
|
||||
if stmt.location.row() >= self.directives.splits[self.split_index] {
|
||||
self.finalize(Some(if self.nested {
|
||||
Trailer::Sibling
|
||||
} else {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
StmtKind::ClassDef { .. } => Trailer::ClassDef,
|
||||
_ => Trailer::Sibling,
|
||||
}
|
||||
}));
|
||||
self.finalize(self.trailer_for(stmt));
|
||||
self.split_index += 1;
|
||||
} else {
|
||||
break;
|
||||
@@ -87,17 +109,7 @@ where
|
||||
{
|
||||
self.track_import(stmt);
|
||||
} else {
|
||||
self.finalize(Some(if self.nested {
|
||||
Trailer::Sibling
|
||||
} else {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
StmtKind::ClassDef { .. } => Trailer::ClassDef,
|
||||
_ => Trailer::Sibling,
|
||||
}
|
||||
}));
|
||||
self.finalize(self.trailer_for(stmt));
|
||||
}
|
||||
|
||||
// Track scope.
|
||||
|
||||
@@ -54,6 +54,7 @@ mod flake8_print;
|
||||
pub mod flake8_quotes;
|
||||
mod flake8_return;
|
||||
pub mod flake8_tidy_imports;
|
||||
mod flake8_unused_arguments;
|
||||
pub mod fs;
|
||||
mod isort;
|
||||
mod lex;
|
||||
|
||||
@@ -92,6 +92,7 @@ pub(crate) fn check_path(
|
||||
&directives.isort,
|
||||
settings,
|
||||
autofix,
|
||||
path,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -133,7 +134,7 @@ pub(crate) fn check_path(
|
||||
Ok(checks)
|
||||
}
|
||||
|
||||
const MAX_ITERATIONS: usize = 1;
|
||||
const MAX_ITERATIONS: usize = 100;
|
||||
|
||||
/// Lint the source code at the given `Path`.
|
||||
pub fn lint_path(
|
||||
|
||||
@@ -392,7 +392,7 @@ fn inner_main() -> Result<ExitCode> {
|
||||
// unless we're writing fixes via stdin (in which case, the transformed
|
||||
// source code goes to stdout).
|
||||
if !(is_stdin && matches!(autofix, fixer::Mode::Apply)) {
|
||||
printer.write_once(&diagnostics)?;
|
||||
printer.write_once(&diagnostics, &autofix)?;
|
||||
}
|
||||
|
||||
// Check for updates if we're in a non-silent log level.
|
||||
|
||||
26
src/noqa.rs
26
src/noqa.rs
@@ -1,4 +1,3 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -7,8 +6,9 @@ use itertools::Itertools;
|
||||
use nohash_hasher::IntMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::checks::{Check, CheckCode, REDIRECTS};
|
||||
use crate::checks::{Check, CheckCode, CODE_REDIRECTS};
|
||||
|
||||
static NO_QA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
@@ -70,7 +70,7 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
pub fn includes(needle: &CheckCode, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.as_ref();
|
||||
haystack.iter().any(|candidate| {
|
||||
if let Some(candidate) = REDIRECTS.get(candidate) {
|
||||
if let Some(candidate) = CODE_REDIRECTS.get(candidate) {
|
||||
needle == candidate.as_ref()
|
||||
} else {
|
||||
&needle == candidate
|
||||
@@ -83,7 +83,7 @@ pub fn add_noqa(
|
||||
checks: &[Check],
|
||||
contents: &str,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &BTreeSet<String>,
|
||||
external: &FxHashSet<String>,
|
||||
) -> Result<usize> {
|
||||
let (count, output) = add_noqa_inner(checks, contents, noqa_line_for, external);
|
||||
fs::write(path, output)?;
|
||||
@@ -94,16 +94,16 @@ fn add_noqa_inner(
|
||||
checks: &[Check],
|
||||
contents: &str,
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
external: &BTreeSet<String>,
|
||||
external: &FxHashSet<String>,
|
||||
) -> (usize, String) {
|
||||
let mut matches_by_line: BTreeMap<usize, BTreeSet<&CheckCode>> = BTreeMap::new();
|
||||
let mut matches_by_line: FxHashMap<usize, FxHashSet<&CheckCode>> = FxHashMap::default();
|
||||
for (lineno, line) in contents.lines().enumerate() {
|
||||
// If we hit an exemption for the entire file, bail.
|
||||
if is_file_exempt(line) {
|
||||
return (0, contents.to_string());
|
||||
}
|
||||
|
||||
let mut codes: BTreeSet<&CheckCode> = BTreeSet::new();
|
||||
let mut codes: FxHashSet<&CheckCode> = FxHashSet::default();
|
||||
for check in checks {
|
||||
if check.location.row() == lineno + 1 {
|
||||
codes.insert(check.kind.code());
|
||||
@@ -117,7 +117,7 @@ fn add_noqa_inner(
|
||||
|
||||
if !codes.is_empty() {
|
||||
let matches = matches_by_line.entry(noqa_lineno).or_default();
|
||||
matches.append(&mut codes);
|
||||
matches.extend(codes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,9 +199,9 @@ fn add_noqa_inner(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use nohash_hasher::IntMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
@@ -227,7 +227,7 @@ mod tests {
|
||||
let checks = vec![];
|
||||
let contents = "x = 1";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 0);
|
||||
assert_eq!(output.trim(), contents.trim());
|
||||
@@ -241,7 +241,7 @@ mod tests {
|
||||
)];
|
||||
let contents = "x = 1";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(output.trim(), "x = 1 # noqa: F841".trim());
|
||||
@@ -264,7 +264,7 @@ mod tests {
|
||||
];
|
||||
let contents = "x = 1 # noqa: E741";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(output.trim(), "x = 1 # noqa: E741, F841".trim());
|
||||
@@ -287,7 +287,7 @@ mod tests {
|
||||
];
|
||||
let contents = "x = 1 # noqa";
|
||||
let noqa_line_for = IntMap::default();
|
||||
let external = BTreeSet::default();
|
||||
let external = FxHashSet::default();
|
||||
let (count, output) = add_noqa_inner(&checks, contents, &noqa_line_for, &external);
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(output.trim(), "x = 1 # noqa: E741, F841".trim());
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::function_type;
|
||||
use crate::ast::types::{Range, Scope, ScopeKind};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::pep8_naming::helpers;
|
||||
use crate::pep8_naming::helpers::FunctionType;
|
||||
use crate::pep8_naming::settings::Settings;
|
||||
use crate::python::string::{self};
|
||||
|
||||
@@ -58,15 +58,16 @@ pub fn invalid_first_argument_name_for_class_method(
|
||||
settings: &Settings,
|
||||
) -> Option<Check> {
|
||||
if !matches!(
|
||||
helpers::function_type(
|
||||
function_type::classify(
|
||||
scope,
|
||||
name,
|
||||
decorator_list,
|
||||
from_imports,
|
||||
import_aliases,
|
||||
settings,
|
||||
&settings.classmethod_decorators,
|
||||
&settings.staticmethod_decorators,
|
||||
),
|
||||
FunctionType::ClassMethod
|
||||
function_type::FunctionType::ClassMethod
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
@@ -99,15 +100,16 @@ pub fn invalid_first_argument_name_for_method(
|
||||
settings: &Settings,
|
||||
) -> Option<Check> {
|
||||
if !matches!(
|
||||
helpers::function_type(
|
||||
function_type::classify(
|
||||
scope,
|
||||
name,
|
||||
decorator_list,
|
||||
from_imports,
|
||||
import_aliases,
|
||||
settings,
|
||||
&settings.classmethod_decorators,
|
||||
&settings.staticmethod_decorators,
|
||||
),
|
||||
FunctionType::Method
|
||||
function_type::FunctionType::Method
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -1,71 +1,10 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||
use rustpython_ast::{Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers::{
|
||||
collect_call_paths, dealias_call_path, match_call_path, to_module_and_member,
|
||||
};
|
||||
use crate::ast::types::{Scope, ScopeKind};
|
||||
use crate::pep8_naming::settings::Settings;
|
||||
use crate::ast::helpers::{collect_call_paths, match_call_path};
|
||||
use crate::python::string::{is_lower, is_upper};
|
||||
|
||||
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
|
||||
|
||||
pub enum FunctionType {
|
||||
Function,
|
||||
Method,
|
||||
ClassMethod,
|
||||
StaticMethod,
|
||||
}
|
||||
|
||||
/// Classify a function based on its scope, name, and decorators.
|
||||
pub fn function_type(
|
||||
scope: &Scope,
|
||||
name: &str,
|
||||
decorator_list: &[Expr],
|
||||
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
|
||||
import_aliases: &FxHashMap<&str, &str>,
|
||||
settings: &Settings,
|
||||
) -> FunctionType {
|
||||
let ScopeKind::Class(scope) = &scope.kind else {
|
||||
return FunctionType::Function;
|
||||
};
|
||||
// Special-case class method, like `__new__`.
|
||||
if CLASS_METHODS.contains(&name)
|
||||
|| scope.bases.iter().any(|expr| {
|
||||
// The class itself extends a known metaclass, so all methods are class methods.
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
METACLASS_BASES
|
||||
.iter()
|
||||
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
|
||||
})
|
||||
|| decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a class method decorator (like `@classmethod`).
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
settings.classmethod_decorators.iter().any(|decorator| {
|
||||
let (module, member) = to_module_and_member(decorator);
|
||||
match_call_path(&call_path, module, member, from_imports)
|
||||
})
|
||||
})
|
||||
{
|
||||
FunctionType::ClassMethod
|
||||
} else if decorator_list.iter().any(|expr| {
|
||||
// The method is decorated with a static method decorator (like
|
||||
// `@staticmethod`).
|
||||
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
|
||||
settings.staticmethod_decorators.iter().any(|decorator| {
|
||||
let (module, member) = to_module_and_member(decorator);
|
||||
match_call_path(&call_path, module, member, from_imports)
|
||||
})
|
||||
}) {
|
||||
FunctionType::StaticMethod
|
||||
} else {
|
||||
// It's an instance method.
|
||||
FunctionType::Method
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_camelcase(name: &str) -> bool {
|
||||
!is_lower(name) && !is_upper(name) && !name.contains('_')
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use itertools::iterate;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::autofix::Fix;
|
||||
use crate::autofix::{fixer, Fix};
|
||||
use crate::checks::CheckCode;
|
||||
use crate::fs::relativize_path;
|
||||
use crate::linter::Diagnostics;
|
||||
@@ -57,15 +57,15 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn post_text(&self, num_fixable: usize) {
|
||||
fn post_text(&self, num_fixable: usize, autofix: &fixer::Mode) {
|
||||
if self.log_level >= &LogLevel::Default {
|
||||
if num_fixable > 0 {
|
||||
if num_fixable > 0 && !matches!(autofix, fixer::Mode::Apply) {
|
||||
println!("{num_fixable} potentially fixable with the --fix option.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_once(&self, diagnostics: &Diagnostics) -> Result<()> {
|
||||
pub fn write_once(&self, diagnostics: &Diagnostics, autofix: &fixer::Mode) -> Result<()> {
|
||||
if matches!(self.log_level, LogLevel::Silent) {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -147,7 +147,7 @@ impl<'a> Printer<'a> {
|
||||
print_message(message);
|
||||
}
|
||||
|
||||
self.post_text(num_fixable);
|
||||
self.post_text(num_fixable, autofix);
|
||||
}
|
||||
SerializationFormat::Grouped => {
|
||||
self.pre_text(diagnostics);
|
||||
@@ -190,7 +190,7 @@ impl<'a> Printer<'a> {
|
||||
println!();
|
||||
}
|
||||
|
||||
self.post_text(num_fixable);
|
||||
self.post_text(num_fixable, autofix);
|
||||
}
|
||||
SerializationFormat::Github => {
|
||||
self.pre_text(diagnostics);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
@@ -7,8 +5,8 @@ use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{Constant, ExprKind, Location, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::whitespace;
|
||||
use crate::ast::whitespace::LinesWithTrailingNewline;
|
||||
use crate::ast::{cast, whitespace};
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
@@ -77,7 +75,7 @@ pub fn not_missing(
|
||||
false
|
||||
}
|
||||
DefinitionKind::Function(stmt) | DefinitionKind::NestedFunction(stmt) => {
|
||||
if is_overload(checker, stmt) {
|
||||
if is_overload(checker, cast::decorator_list(stmt)) {
|
||||
true
|
||||
} else {
|
||||
if checker.settings.enabled.contains(&CheckCode::D103) {
|
||||
@@ -90,7 +88,9 @@ pub fn not_missing(
|
||||
}
|
||||
}
|
||||
DefinitionKind::Method(stmt) => {
|
||||
if is_overload(checker, stmt) || is_override(checker, stmt) {
|
||||
if is_overload(checker, cast::decorator_list(stmt))
|
||||
|| is_override(checker, cast::decorator_list(stmt))
|
||||
{
|
||||
true
|
||||
} else if is_magic(stmt) {
|
||||
if checker.settings.enabled.contains(&CheckCode::D105) {
|
||||
@@ -916,7 +916,7 @@ pub fn if_needed(checker: &mut Checker, definition: &Definition) {
|
||||
) = definition.kind else {
|
||||
return
|
||||
};
|
||||
if !is_overload(checker, stmt) {
|
||||
if !is_overload(checker, cast::decorator_list(stmt)) {
|
||||
return;
|
||||
}
|
||||
checker.add_check(Check::new(
|
||||
@@ -1400,7 +1400,7 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||
};
|
||||
|
||||
// Look for arguments that weren't included in the docstring.
|
||||
let mut missing_arg_names: BTreeSet<String> = BTreeSet::default();
|
||||
let mut missing_arg_names: FxHashSet<String> = FxHashSet::default();
|
||||
for arg in arguments
|
||||
.args
|
||||
.iter()
|
||||
@@ -1410,7 +1410,7 @@ fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args:
|
||||
// If this is a non-static method, skip `cls` or `self`.
|
||||
usize::from(
|
||||
matches!(definition.kind, DefinitionKind::Method(_))
|
||||
&& !is_staticmethod(checker, parent),
|
||||
&& !is_staticmethod(checker, cast::decorator_list(parent)),
|
||||
),
|
||||
)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind,
|
||||
};
|
||||
|
||||
use crate::ast::types::{BindingKind, FunctionScope, Range, Scope, ScopeKind};
|
||||
use crate::ast::types::{BindingKind, Range, Scope, ScopeKind};
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::pyflakes::cformat::CFormatSummary;
|
||||
use crate::pyflakes::format::FormatSummary;
|
||||
@@ -391,13 +391,7 @@ pub fn undefined_local(scopes: &[&Scope], name: &str) -> Option<Check> {
|
||||
pub fn unused_variables(scope: &Scope, dummy_variable_rgx: &Regex) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = vec![];
|
||||
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(FunctionScope {
|
||||
uses_locals: true,
|
||||
..
|
||||
})
|
||||
) {
|
||||
if scope.uses_locals && matches!(scope.kind, ScopeKind::Function(..)) {
|
||||
return checks;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustpython_ast::Expr;
|
||||
|
||||
use crate::ast::types::{FunctionScope, Range, ScopeKind};
|
||||
use crate::ast::types::{FunctionDef, Range, ScopeKind};
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::CheckKind;
|
||||
use crate::Check;
|
||||
@@ -10,7 +10,7 @@ pub fn await_outside_async(checker: &mut Checker, expr: &Expr) {
|
||||
if !checker
|
||||
.current_scopes()
|
||||
.find_map(|scope| {
|
||||
if let ScopeKind::Function(FunctionScope { async_, .. }) = &scope.kind {
|
||||
if let ScopeKind::Function(FunctionDef { async_, .. }) = &scope.kind {
|
||||
Some(*async_)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -8,12 +8,7 @@ use crate::checks::CheckKind;
|
||||
use crate::Check;
|
||||
|
||||
/// PLR1701
|
||||
pub fn consider_merging_isinstance(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
op: &Boolop,
|
||||
values: &[Expr],
|
||||
) {
|
||||
pub fn merge_isinstance(checker: &mut Checker, expr: &Expr, op: &Boolop, values: &[Expr]) {
|
||||
if !matches!(op, Boolop::Or) || !checker.is_builtin("isinstance") {
|
||||
return;
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
pub use await_outside_async::await_outside_async;
|
||||
pub use consider_merging_isinstance::consider_merging_isinstance;
|
||||
pub use consider_using_from_import::consider_using_from_import;
|
||||
pub use consider_using_sys_exit::consider_using_sys_exit;
|
||||
pub use merge_isinstance::merge_isinstance;
|
||||
pub use misplaced_comparison_constant::misplaced_comparison_constant;
|
||||
pub use property_with_parameters::property_with_parameters;
|
||||
pub use unnecessary_direct_lambda_call::unnecessary_direct_lambda_call;
|
||||
pub use use_from_import::use_from_import;
|
||||
pub use use_sys_exit::use_sys_exit;
|
||||
pub use useless_else_on_loop::useless_else_on_loop;
|
||||
pub use useless_import_alias::useless_import_alias;
|
||||
|
||||
mod await_outside_async;
|
||||
mod consider_merging_isinstance;
|
||||
mod consider_using_from_import;
|
||||
mod consider_using_sys_exit;
|
||||
mod merge_isinstance;
|
||||
mod misplaced_comparison_constant;
|
||||
mod property_with_parameters;
|
||||
mod unnecessary_direct_lambda_call;
|
||||
mod use_from_import;
|
||||
mod use_sys_exit;
|
||||
mod useless_else_on_loop;
|
||||
mod useless_import_alias;
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::checks::CheckKind;
|
||||
use crate::Check;
|
||||
|
||||
/// PLR0402
|
||||
pub fn consider_using_from_import(checker: &mut Checker, alias: &Alias) {
|
||||
pub fn use_from_import(checker: &mut Checker, alias: &Alias) {
|
||||
let Some(asname) = &alias.node.asname else {
|
||||
return;
|
||||
};
|
||||
@@ -60,7 +60,7 @@ fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -
|
||||
}
|
||||
|
||||
/// RUF004
|
||||
pub fn consider_using_sys_exit(checker: &mut Checker, func: &Expr) {
|
||||
pub fn use_sys_exit(checker: &mut Checker, func: &Expr) {
|
||||
let ExprKind::Name { id, .. } = &func.node else {
|
||||
return;
|
||||
};
|
||||
@@ -74,7 +74,10 @@ pub fn consider_using_sys_exit(checker: &mut Checker, func: &Expr) {
|
||||
if !checker.is_builtin(name) {
|
||||
continue;
|
||||
}
|
||||
let mut check = Check::new(CheckKind::ConsiderUsingSysExit, Range::from_located(func));
|
||||
let mut check = Check::new(
|
||||
CheckKind::UseSysExit(name.to_string()),
|
||||
Range::from_located(func),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
if let Some(content) = get_member_import_name_alias(checker, "sys", "exit") {
|
||||
check.amend(Fix::replacement(
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/pylint/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
@@ -10,7 +11,8 @@ expression: checks
|
||||
row: 1
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
@@ -18,7 +20,8 @@ expression: checks
|
||||
row: 2
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 6
|
||||
column: 4
|
||||
@@ -26,7 +29,8 @@ expression: checks
|
||||
row: 6
|
||||
column: 8
|
||||
fix: ~
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 7
|
||||
column: 4
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/pylint/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
@@ -17,7 +18,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 3
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
@@ -32,7 +34,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 4
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
@@ -47,7 +50,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 8
|
||||
column: 8
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/pylint/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
@@ -17,7 +18,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 3
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
@@ -32,7 +34,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 4
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
@@ -47,7 +50,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 8
|
||||
column: 8
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/pylint/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
@@ -17,7 +18,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 4
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/pylint/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
@@ -17,7 +18,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 3
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
@@ -32,7 +34,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 4
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
@@ -47,7 +50,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 8
|
||||
column: 8
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/pylint/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
@@ -17,7 +18,8 @@ expression: checks
|
||||
end_location:
|
||||
row: 4
|
||||
column: 4
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/pylint/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: exit
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
@@ -10,7 +11,8 @@ expression: checks
|
||||
row: 1
|
||||
column: 4
|
||||
fix: ~
|
||||
- kind: ConsiderUsingSysExit
|
||||
- kind:
|
||||
UseSysExit: quit
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_ast::{AliasData, Located};
|
||||
use rustpython_parser::ast::Stmt;
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
|
||||
let target_version = checker.settings.target_version;
|
||||
|
||||
let mut removable_index: Vec<usize> = vec![];
|
||||
let mut removable_names: BTreeSet<&str> = BTreeSet::new();
|
||||
let mut removable_names: FxHashSet<&str> = FxHashSet::default();
|
||||
for (index, alias) in names.iter().enumerate() {
|
||||
let name = alias.node.name.as_str();
|
||||
if (target_version >= PythonVersion::Py33 && PY33_PLUS_REMOVE_FUTURES.contains(&name))
|
||||
@@ -53,7 +53,13 @@ pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Lo
|
||||
return;
|
||||
}
|
||||
let mut check = Check::new(
|
||||
CheckKind::UnnecessaryFutureImport(removable_names.into_iter().map(String::from).collect()),
|
||||
CheckKind::UnnecessaryFutureImport(
|
||||
removable_names
|
||||
.into_iter()
|
||||
.map(String::from)
|
||||
.sorted()
|
||||
.collect(),
|
||||
),
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
if checker.patch(check.kind.code()) {
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
//! command-line options. Structure is optimized for internal usage, as opposed
|
||||
//! to external visibility or parsing.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use globset::{Glob, GlobMatcher, GlobSet};
|
||||
use itertools::Itertools;
|
||||
use path_absolutize::path_dedot;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashSet;
|
||||
@@ -34,12 +34,12 @@ pub struct Settings {
|
||||
pub enabled: FxHashSet<CheckCode>,
|
||||
pub exclude: GlobSet,
|
||||
pub extend_exclude: GlobSet,
|
||||
pub external: BTreeSet<String>,
|
||||
pub external: FxHashSet<String>,
|
||||
pub fixable: FxHashSet<CheckCode>,
|
||||
pub format: SerializationFormat,
|
||||
pub ignore_init_module_imports: bool,
|
||||
pub line_length: usize,
|
||||
pub per_file_ignores: Vec<(GlobMatcher, GlobMatcher, BTreeSet<CheckCode>)>,
|
||||
pub per_file_ignores: Vec<(GlobMatcher, GlobMatcher, FxHashSet<CheckCode>)>,
|
||||
pub show_source: bool,
|
||||
pub src: Vec<PathBuf>,
|
||||
pub target_version: PythonVersion,
|
||||
@@ -77,7 +77,7 @@ impl Settings {
|
||||
),
|
||||
exclude: resolve_globset(config.exclude, project_root)?,
|
||||
extend_exclude: resolve_globset(config.extend_exclude, project_root)?,
|
||||
external: BTreeSet::from_iter(config.external),
|
||||
external: FxHashSet::from_iter(config.external),
|
||||
fixable: resolve_codes(&config.fixable, &config.unfixable),
|
||||
format: config.format,
|
||||
flake8_annotations: config.flake8_annotations,
|
||||
@@ -105,7 +105,7 @@ impl Settings {
|
||||
enabled: FxHashSet::from_iter([check_code.clone()]),
|
||||
exclude: GlobSet::empty(),
|
||||
extend_exclude: GlobSet::empty(),
|
||||
external: BTreeSet::default(),
|
||||
external: FxHashSet::default(),
|
||||
fixable: FxHashSet::from_iter([check_code]),
|
||||
format: SerializationFormat::Text,
|
||||
ignore_init_module_imports: false,
|
||||
@@ -133,7 +133,7 @@ impl Settings {
|
||||
enabled: FxHashSet::from_iter(check_codes.clone()),
|
||||
exclude: GlobSet::empty(),
|
||||
extend_exclude: GlobSet::empty(),
|
||||
external: BTreeSet::default(),
|
||||
external: FxHashSet::default(),
|
||||
fixable: FxHashSet::from_iter(check_codes),
|
||||
format: SerializationFormat::Text,
|
||||
ignore_init_module_imports: false,
|
||||
@@ -162,18 +162,23 @@ impl Hash for Settings {
|
||||
confusable.hash(state);
|
||||
}
|
||||
self.dummy_variable_rgx.as_str().hash(state);
|
||||
for value in &self.enabled {
|
||||
for value in self.enabled.iter().sorted() {
|
||||
value.hash(state);
|
||||
}
|
||||
self.external.hash(state);
|
||||
for value in &self.fixable {
|
||||
for value in self.external.iter().sorted() {
|
||||
value.hash(state);
|
||||
}
|
||||
for value in self.fixable.iter().sorted() {
|
||||
value.hash(state);
|
||||
}
|
||||
self.ignore_init_module_imports.hash(state);
|
||||
self.line_length.hash(state);
|
||||
for (absolute, basename, codes) in &self.per_file_ignores {
|
||||
absolute.glob().hash(state);
|
||||
basename.glob().hash(state);
|
||||
codes.hash(state);
|
||||
for value in codes.iter().sorted() {
|
||||
value.hash(state);
|
||||
}
|
||||
}
|
||||
self.show_source.hash(state);
|
||||
self.target_version.hash(state);
|
||||
@@ -206,7 +211,7 @@ pub fn resolve_globset(
|
||||
pub fn resolve_per_file_ignores(
|
||||
per_file_ignores: Vec<PerFileIgnore>,
|
||||
project_root: Option<&PathBuf>,
|
||||
) -> Result<Vec<(GlobMatcher, GlobMatcher, BTreeSet<CheckCode>)>> {
|
||||
) -> Result<Vec<(GlobMatcher, GlobMatcher, FxHashSet<CheckCode>)>> {
|
||||
per_file_ignores
|
||||
.into_iter()
|
||||
.map(|per_file_ignore| {
|
||||
|
||||
@@ -125,7 +125,7 @@ pub struct Options {
|
||||
pub fix: Option<bool>,
|
||||
#[option(
|
||||
doc = "A list of check code prefixes to consider autofix-able.",
|
||||
default = r#"["A", "ANN", "B", "BLE", "C", "D", "E", "F", "FBT", "I", "M", "N", "Q", "RUF", "S", "T", "U", "W", "YTT"]"#,
|
||||
default = r#"["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"]"#,
|
||||
value_type = "Vec<CheckCodePrefix>",
|
||||
example = r#"
|
||||
# Only allow autofix behavior for `E` and `F` checks.
|
||||
|
||||
@@ -96,7 +96,6 @@ pub fn load_options(pyproject: Option<&PathBuf>) -> Result<Options> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
use std::env::current_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
@@ -432,8 +431,11 @@ other-attribute = 1
|
||||
ban_relative_imports: Some(Strictness::Parents)
|
||||
}),
|
||||
flake8_import_conventions: Some(flake8_import_conventions::settings::Options {
|
||||
aliases: Some(BTreeMap::from([("pandas".to_string(), "pd".to_string(),)])),
|
||||
extend_aliases: Some(BTreeMap::from([(
|
||||
aliases: Some(FxHashMap::from_iter([(
|
||||
"pandas".to_string(),
|
||||
"pd".to_string(),
|
||||
)])),
|
||||
extend_aliases: Some(FxHashMap::from_iter([(
|
||||
"dask.dataframe".to_string(),
|
||||
"dd".to_string(),
|
||||
)])),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::env;
|
||||
use std::hash::Hash;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -7,6 +6,7 @@ use std::str::FromStr;
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use clap::ValueEnum;
|
||||
use globset::{Glob, GlobSetBuilder};
|
||||
use rustc_hash::FxHashSet;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
@@ -89,10 +89,10 @@ impl FromStr for FilePattern {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PerFileIgnore {
|
||||
pub pattern: String,
|
||||
pub codes: BTreeSet<CheckCode>,
|
||||
pub codes: FxHashSet<CheckCode>,
|
||||
}
|
||||
|
||||
impl PerFileIgnore {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_ast::{Stmt, StmtKind};
|
||||
use rustpython_ast::{Expr, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::helpers::match_module_member;
|
||||
use crate::check_ast::Checker;
|
||||
@@ -29,59 +29,56 @@ pub struct VisibleScope {
|
||||
}
|
||||
|
||||
/// Returns `true` if a function is a "static method".
|
||||
pub fn is_staticmethod(checker: &Checker, stmt: &Stmt) -> bool {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { decorator_list, .. }
|
||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list.iter().any(|expr| {
|
||||
match_module_member(
|
||||
expr,
|
||||
"",
|
||||
"staticmethod",
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
)
|
||||
}),
|
||||
_ => panic!("Found non-FunctionDef in is_staticmethod"),
|
||||
}
|
||||
pub fn is_staticmethod(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list.iter().any(|expr| {
|
||||
match_module_member(
|
||||
expr,
|
||||
"",
|
||||
"staticmethod",
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if a function is a "class method".
|
||||
pub fn is_classmethod(checker: &Checker, stmt: &Stmt) -> bool {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { decorator_list, .. }
|
||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list.iter().any(|expr| {
|
||||
match_module_member(
|
||||
expr,
|
||||
"",
|
||||
"classmethod",
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
)
|
||||
}),
|
||||
_ => panic!("Found non-FunctionDef in is_classmethod"),
|
||||
}
|
||||
pub fn is_classmethod(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list.iter().any(|expr| {
|
||||
match_module_member(
|
||||
expr,
|
||||
"",
|
||||
"classmethod",
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if a function definition is an `@overload`.
|
||||
pub fn is_overload(checker: &Checker, stmt: &Stmt) -> bool {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { decorator_list, .. }
|
||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
||||
.iter()
|
||||
.any(|expr| checker.match_typing_expr(expr, "overload")),
|
||||
_ => panic!("Found non-FunctionDef in is_overload"),
|
||||
}
|
||||
pub fn is_overload(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list
|
||||
.iter()
|
||||
.any(|expr| checker.match_typing_expr(expr, "overload"))
|
||||
}
|
||||
|
||||
/// Returns `true` if a function definition is an `@override` (PEP 698).
|
||||
pub fn is_override(checker: &Checker, stmt: &Stmt) -> bool {
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { decorator_list, .. }
|
||||
| StmtKind::AsyncFunctionDef { decorator_list, .. } => decorator_list
|
||||
.iter()
|
||||
.any(|expr| checker.match_typing_expr(expr, "override")),
|
||||
_ => panic!("Found non-FunctionDef in is_override"),
|
||||
}
|
||||
pub fn is_override(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list
|
||||
.iter()
|
||||
.any(|expr| checker.match_typing_expr(expr, "override"))
|
||||
}
|
||||
|
||||
/// Returns `true` if a function definition is an `@abstractmethod`.
|
||||
pub fn is_abstract(checker: &Checker, decorator_list: &[Expr]) -> bool {
|
||||
decorator_list.iter().any(|expr| {
|
||||
match_module_member(
|
||||
expr,
|
||||
"abc",
|
||||
"abstractmethod",
|
||||
&checker.from_imports,
|
||||
&checker.import_aliases,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if a function is a "magic method".
|
||||
|
||||
Reference in New Issue
Block a user