Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a310aed128 | ||
|
|
43cc8bc84e | ||
|
|
84bf36194b | ||
|
|
e4d168bb4f | ||
|
|
439642addf | ||
|
|
f5b1f957e3 | ||
|
|
8f99705795 | ||
|
|
9ec7e6bcd6 | ||
|
|
695b06ba60 | ||
|
|
3a2e6926d4 | ||
|
|
d16c3a1186 | ||
|
|
53a2187f02 | ||
|
|
00b5d1059c | ||
|
|
b7acf76aaf | ||
|
|
8cfc0e5cf5 | ||
|
|
aa7681f9ad | ||
|
|
2493d48725 | ||
|
|
1b422a7f12 | ||
|
|
da051624e4 | ||
|
|
da9ae6a42a | ||
|
|
afa59d78bb | ||
|
|
bbc38fea73 | ||
|
|
6bcc11a90f | ||
|
|
6f36e5dd25 | ||
|
|
1d13752eb1 | ||
|
|
394af0dcff | ||
|
|
51cee471a0 | ||
|
|
8df3a5437a |
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -77,7 +77,7 @@ jobs:
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- run: cargo clippy --all -- -D warnings
|
||||
- run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
cargo_test:
|
||||
name: "cargo test"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.113
|
||||
rev: v0.0.116
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
27
Cargo.lock
generated
27
Cargo.lock
generated
@@ -427,11 +427,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@@ -933,7 +930,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.113-dev.0"
|
||||
version = "0.0.116-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -2240,7 +2237,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.113"
|
||||
version = "0.0.116"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
@@ -2256,6 +2253,7 @@ dependencies = [
|
||||
"dirs 4.0.0",
|
||||
"fern",
|
||||
"filetime",
|
||||
"fnv",
|
||||
"getrandom 0.2.8",
|
||||
"glob",
|
||||
"insta",
|
||||
@@ -2287,7 +2285,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.113"
|
||||
version = "0.0.116"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.22",
|
||||
@@ -2781,17 +2779,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
@@ -3078,12 +3065,6 @@ version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.113"
|
||||
version = "0.0.116"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
@@ -16,13 +16,14 @@ name = "ruff"
|
||||
anyhow = { version = "1.0.66" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bitflags = { version = "1.3.2" }
|
||||
chrono = { version = "0.4.21" }
|
||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
common-path = { version = "1.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
fern = { version = "0.6.1" }
|
||||
filetime = { version = "0.2.17" }
|
||||
fnv = { version = "1.0.7" }
|
||||
glob = { version = "0.3.0" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
|
||||
|
||||
61
README.md
61
README.md
@@ -99,7 +99,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.113
|
||||
rev: v0.0.116
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -335,7 +335,7 @@ For more, see [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/) on PyPI.
|
||||
| F702 | ContinueOutsideLoop | `continue` not properly in loop | |
|
||||
| F704 | YieldOutsideFunction | `yield` or `yield from` statement outside of a function | |
|
||||
| F706 | ReturnOutsideFunction | `return` statement outside of a function/method | |
|
||||
| F707 | DefaultExceptNotLast | An `except:` block as not the last exception handler | |
|
||||
| F707 | DefaultExceptNotLast | An `except` block as not the last exception handler | |
|
||||
| F722 | ForwardAnnotationSyntaxError | Syntax error in forward annotation: `...` | |
|
||||
| F821 | UndefinedName | Undefined name `...` | |
|
||||
| F822 | UndefinedExport | Undefined name `...` in `__all__` | |
|
||||
@@ -409,7 +409,7 @@ For more, see [pydocstyle](https://pypi.org/project/pydocstyle/6.1.1/) on PyPI.
|
||||
| D400 | EndsInPeriod | First line should end with a period | |
|
||||
| D402 | NoSignature | First line should not be the function's signature | |
|
||||
| D403 | FirstLineCapitalized | First word of the first line should be properly capitalized | |
|
||||
| D404 | NoThisPrefix | First word of the docstring should not be 'This' | |
|
||||
| D404 | NoThisPrefix | First word of the docstring should not be "This" | |
|
||||
| D405 | CapitalizeSectionName | Section name should be properly capitalized ("returns") | 🛠 |
|
||||
| D406 | NewLineAfterSectionName | Section name should end with a newline ("Returns") | 🛠 |
|
||||
| D407 | DashedUnderlineAfterSection | Missing dashed underline after section ("Returns") | 🛠 |
|
||||
@@ -440,9 +440,10 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
|
||||
| U006 | UsePEP585Annotation | Use `list` instead of `List` for type annotations | 🛠 |
|
||||
| U007 | UsePEP604Annotation | Use `X \| Y` for type annotations | 🛠 |
|
||||
| U008 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | 🛠 |
|
||||
| U009 | PEP3120UnnecessaryCodingComment | utf-8 encoding declaration is unnecessary | 🛠 |
|
||||
| U010 | UnnecessaryFutureImport | Unnessary __future__ import `...` for target Python version | |
|
||||
| U011 | UnnecessaryLRUCacheParams | Unnessary parameters to functools.lru_cache | 🛠 |
|
||||
| U009 | PEP3120UnnecessaryCodingComment | UTF-8 encoding declaration is unnecessary | 🛠 |
|
||||
| U010 | UnnecessaryFutureImport | Unnecessary `__future__` import `...` for target Python version | 🛠 |
|
||||
| U011 | UnnecessaryLRUCacheParams | Unnecessary parameters to `functools.lru_cache` | 🛠 |
|
||||
| U012 | UnnecessaryEncodeUTF8 | Unnecessary call to `encode` as UTF-8 | 🛠 |
|
||||
|
||||
### pep8-naming
|
||||
|
||||
@@ -466,6 +467,19 @@ For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyP
|
||||
| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` | |
|
||||
| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix | |
|
||||
|
||||
### flake8-bandit
|
||||
|
||||
For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on PyPI.
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| S101 | AssertUsed | Use of `assert` detected | |
|
||||
| S102 | ExecUsed | Use of `exec` detected | |
|
||||
| S104 | HardcodedBindAllInterfaces | Possible binding to all interfaces | |
|
||||
| S105 | HardcodedPasswordString | Possible hardcoded password: `"..."` | |
|
||||
| S106 | HardcodedPasswordFuncArg | Possible hardcoded password: `"..."` | |
|
||||
| S107 | HardcodedPasswordDefault | Possible hardcoded password: `"..."` | |
|
||||
|
||||
### flake8-comprehensions
|
||||
|
||||
For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/3.10.1/) on PyPI.
|
||||
@@ -495,24 +509,27 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| B002 | UnaryPrefixIncrement | Python does not support the unary prefix increment. | |
|
||||
| B003 | AssignmentToOsEnviron | Assigning to `os.environ` doesn't clear the environment. | |
|
||||
| B002 | UnaryPrefixIncrement | Python does not support the unary prefix increment | |
|
||||
| B003 | AssignmentToOsEnviron | Assigning to `os.environ` doesn't clear the environment | |
|
||||
| B004 | UnreliableCallableCheck | Using `hasattr(x, '__call__')` to test if x is callable is unreliable. Use `callable(x)` for consistent results. | |
|
||||
| B005 | StripWithMultiCharacters | Using `.strip()` with multi-character strings is misleading the reader. | |
|
||||
| B006 | MutableArgumentDefault | Do not use mutable data structures for argument defaults. | |
|
||||
| B007 | UnusedLoopControlVariable | Loop control variable `i` not used within the loop body. | 🛠 |
|
||||
| B008 | FunctionCallArgumentDefault | Do not perform function calls in argument defaults. | |
|
||||
| B009 | GetAttrWithConstant | Do not call `getattr` with a constant attribute value, it is not any safer than normal property access. | 🛠 |
|
||||
| B010 | SetAttrWithConstant | Do not call `setattr` with a constant attribute value, it is not any safer than normal property access. | |
|
||||
| B005 | StripWithMultiCharacters | Using `.strip()` with multi-character strings is misleading the reader | |
|
||||
| B006 | MutableArgumentDefault | Do not use mutable data structures for argument defaults | |
|
||||
| B007 | UnusedLoopControlVariable | Loop control variable `i` not used within the loop body | 🛠 |
|
||||
| B008 | FunctionCallArgumentDefault | Do not perform function call in argument defaults | |
|
||||
| B009 | GetAttrWithConstant | Do not call `getattr` with a constant attribute value. It is not any safer than normal property access. | 🛠 |
|
||||
| B010 | SetAttrWithConstant | Do not call `setattr` with a constant attribute value. It is not any safer than normal property access. | |
|
||||
| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 |
|
||||
| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError:` instead of `except (ValueError,):`. | |
|
||||
| B012 | JumpStatementInFinally | `return/continue/break` inside finally blocks cause exceptions to be silenced | |
|
||||
| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError` instead of `except (ValueError,)`. | |
|
||||
| B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` | 🛠 |
|
||||
| B015 | UselessComparison | Pointless comparison. This comparison does nothing but waste CPU instructions. Either prepend `assert` or remove it. | |
|
||||
| B016 | CannotRaiseLiteral | Cannot raise a literal. Did you intend to return it or raise an Exception? | |
|
||||
| B017 | NoAssertRaisesException | `assertRaises(Exception):` should be considered evil. | |
|
||||
| B017 | NoAssertRaisesException | `assertRaises(Exception)` should be considered evil | |
|
||||
| B018 | UselessExpression | Found useless expression. Either assign it to a variable or remove it. | |
|
||||
| B019 | CachedInstanceMethod | Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks | |
|
||||
| B021 | FStringDocstring | f-string used as docstring. This will be interpreted by python as a joined string rather than a docstring. | |
|
||||
| B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | |
|
||||
| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged. | |
|
||||
| B026 | StarArgUnpackingAfterKeywordArg | Star-arg unpacking after a keyword argument is strongly discouraged | |
|
||||
|
||||
### flake8-builtins
|
||||
|
||||
@@ -684,9 +701,10 @@ including:
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (20/32)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (22/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/34)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/34)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
|
||||
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
|
||||
@@ -707,12 +725,13 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-print`](https://pypi.org/project/flake8-print/)
|
||||
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (20/32)
|
||||
- [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (22/32)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
|
||||
Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/34).
|
||||
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/34).
|
||||
|
||||
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use ropey::Rope;
|
||||
use ruff::fs;
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let contents = fs::read_file(Path::new("resources/test/fixtures/D.py")).unwrap();
|
||||
let contents = fs::read_to_string(Path::new("resources/test/fixtures/D.py")).unwrap();
|
||||
c.bench_function("rope", |b| {
|
||||
b.iter(|| {
|
||||
let rope = Rope::from_str(black_box(&contents));
|
||||
|
||||
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.113"
|
||||
version = "0.0.116"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.113"
|
||||
version = "0.0.116"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.113-dev.0"
|
||||
version = "0.0.116-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff::checks_gen::CheckCodePrefix;
|
||||
use ruff::flake8_quotes::settings::Quote;
|
||||
use ruff::settings::options::Options;
|
||||
use ruff::settings::pyproject::Pyproject;
|
||||
use ruff::{flake8_annotations, flake8_quotes, pep8_naming};
|
||||
use ruff::{flake8_annotations, flake8_bugbear, flake8_quotes, pep8_naming};
|
||||
|
||||
use crate::plugin::Plugin;
|
||||
use crate::{parser, plugin};
|
||||
@@ -69,6 +69,7 @@ pub fn convert(
|
||||
// Parse each supported option.
|
||||
let mut options: Options = Default::default();
|
||||
let mut flake8_annotations: flake8_annotations::settings::Options = Default::default();
|
||||
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
|
||||
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
|
||||
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
|
||||
for (key, value) in flake8 {
|
||||
@@ -109,6 +110,11 @@ pub fn convert(
|
||||
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
|
||||
}
|
||||
}
|
||||
// flake8-bugbear
|
||||
"extend-immutable-calls" | "extend_immutable_calls" => {
|
||||
flake8_bugbear.extend_immutable_calls =
|
||||
Some(parser::parse_strings(value.as_ref()));
|
||||
}
|
||||
// flake8-annotations
|
||||
"suppress-none-returning" | "suppress_none_returning" => {
|
||||
match parser::parse_bool(value.as_ref()) {
|
||||
@@ -179,6 +185,12 @@ pub fn convert(
|
||||
// Deduplicate and sort.
|
||||
options.select = Some(Vec::from_iter(select));
|
||||
options.ignore = Some(Vec::from_iter(ignore));
|
||||
if flake8_annotations != Default::default() {
|
||||
options.flake8_annotations = Some(flake8_annotations);
|
||||
}
|
||||
if flake8_bugbear != Default::default() {
|
||||
options.flake8_bugbear = Some(flake8_bugbear);
|
||||
}
|
||||
if flake8_quotes != Default::default() {
|
||||
options.flake8_quotes = Some(flake8_quotes);
|
||||
}
|
||||
@@ -224,6 +236,7 @@ mod tests {
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -257,6 +270,7 @@ mod tests {
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -290,6 +304,7 @@ mod tests {
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -323,6 +338,7 @@ mod tests {
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -356,6 +372,7 @@ mod tests {
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
@@ -432,6 +449,7 @@ mod tests {
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -466,6 +484,7 @@ mod tests {
|
||||
dummy_variable_rgx: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
inline_quotes: Some(flake8_quotes::settings::Quote::Single),
|
||||
multiline_quotes: None,
|
||||
|
||||
@@ -6,6 +6,7 @@ use ruff::checks_gen::CheckCodePrefix;
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Plugin {
|
||||
Flake8Bandit,
|
||||
Flake8Bugbear,
|
||||
Flake8Builtins,
|
||||
Flake8Comprehensions,
|
||||
@@ -22,6 +23,7 @@ impl FromStr for Plugin {
|
||||
|
||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||
match string {
|
||||
"flake8-bandit" => Ok(Plugin::Flake8Bandit),
|
||||
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
|
||||
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
|
||||
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
|
||||
@@ -39,6 +41,7 @@ impl FromStr for Plugin {
|
||||
impl Plugin {
|
||||
pub fn default(&self) -> CheckCodePrefix {
|
||||
match self {
|
||||
Plugin::Flake8Bandit => CheckCodePrefix::S,
|
||||
Plugin::Flake8Bugbear => CheckCodePrefix::B,
|
||||
Plugin::Flake8Builtins => CheckCodePrefix::A,
|
||||
Plugin::Flake8Comprehensions => CheckCodePrefix::C,
|
||||
@@ -53,6 +56,7 @@ impl Plugin {
|
||||
|
||||
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
|
||||
match self {
|
||||
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
|
||||
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
|
||||
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
|
||||
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C],
|
||||
@@ -265,6 +269,10 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
"docstring-convention" | "docstring_convention" => {
|
||||
plugins.insert(Plugin::Flake8Docstrings);
|
||||
}
|
||||
// flake8-bugbear
|
||||
"extend-immutable-calls" | "extend_immutable_calls" => {
|
||||
plugins.insert(Plugin::Flake8Bugbear);
|
||||
}
|
||||
// flake8-builtins
|
||||
"builtins-ignorelist" | "builtins_ignorelist" => {
|
||||
plugins.insert(Plugin::Flake8Builtins);
|
||||
@@ -329,6 +337,7 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
/// `flake8-annotations` is active.
|
||||
pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin> {
|
||||
[
|
||||
Plugin::Flake8Bandit,
|
||||
Plugin::Flake8Bugbear,
|
||||
Plugin::Flake8Builtins,
|
||||
Plugin::Flake8Comprehensions,
|
||||
|
||||
20
resources/test/fixtures/B008_extended.py
vendored
Normal file
20
resources/test/fixtures/B008_extended.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
from typing import List
|
||||
|
||||
import fastapi
|
||||
from fastapi import Query
|
||||
|
||||
|
||||
def okay(db=fastapi.Depends(get_db)):
|
||||
...
|
||||
|
||||
|
||||
def okay(data: List[str] = fastapi.Query(None)):
|
||||
...
|
||||
|
||||
|
||||
def okay(data: List[str] = Query(None)):
|
||||
...
|
||||
|
||||
|
||||
def error_due_to_missing_import(data: List[str] = Depends(None)):
|
||||
...
|
||||
107
resources/test/fixtures/B012.py
vendored
Normal file
107
resources/test/fixtures/B012.py
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
def a():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
return # warning
|
||||
|
||||
|
||||
def b():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
if 1 + 0 == 2 - 1:
|
||||
return # warning
|
||||
|
||||
|
||||
def c():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
return # warning
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def d():
|
||||
try:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
return # warning
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
def e():
|
||||
if 1 == 2 - 1:
|
||||
try:
|
||||
|
||||
def f():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
return # warning
|
||||
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
def g():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
|
||||
def h():
|
||||
return # no warning
|
||||
|
||||
e()
|
||||
|
||||
|
||||
def i():
|
||||
while True:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
break # warning
|
||||
|
||||
def j():
|
||||
while True:
|
||||
break # no warning
|
||||
|
||||
|
||||
def h():
|
||||
while True:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
continue # warning
|
||||
|
||||
def j():
|
||||
while True:
|
||||
continue # no warning
|
||||
|
||||
|
||||
def k():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
while True:
|
||||
break # no warning
|
||||
while True:
|
||||
continue # no warning
|
||||
while True:
|
||||
return # warning
|
||||
|
||||
|
||||
while True:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
continue # warning
|
||||
|
||||
while True:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
break # warning
|
||||
108
resources/test/fixtures/B019.py
vendored
Normal file
108
resources/test/fixtures/B019.py
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
Should emit:
|
||||
B019 - on lines 73, 77, 81, 85, 89, 93, 97, 101
|
||||
"""
|
||||
import functools
|
||||
from functools import cache, cached_property, lru_cache
|
||||
|
||||
|
||||
def some_other_cache():
|
||||
...
|
||||
|
||||
|
||||
@functools.cache
|
||||
def compute_func(self, y):
|
||||
...
|
||||
|
||||
|
||||
class Foo:
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
def compute_method(self, y):
|
||||
...
|
||||
|
||||
@some_other_cache
|
||||
def user_cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
@functools.cache
|
||||
def cached_classmethod(cls, y):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
@cache
|
||||
def other_cached_classmethod(cls, y):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
@functools.lru_cache
|
||||
def lru_cached_classmethod(cls, y):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
@lru_cache
|
||||
def other_lru_cached_classmethod(cls, y):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
@functools.cache
|
||||
def cached_staticmethod(y):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
@cache
|
||||
def other_cached_staticmethod(y):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache
|
||||
def lru_cached_staticmethod(y):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
@lru_cache
|
||||
def other_lru_cached_staticmethod(y):
|
||||
...
|
||||
|
||||
@functools.cached_property
|
||||
def some_cached_property(self):
|
||||
...
|
||||
|
||||
@cached_property
|
||||
def some_other_cached_property(self):
|
||||
...
|
||||
|
||||
# Remaining methods should emit B019
|
||||
@functools.cache
|
||||
def cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@cache
|
||||
def another_cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@functools.cache()
|
||||
def called_cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@cache()
|
||||
def another_called_cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@functools.lru_cache
|
||||
def lru_cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@lru_cache
|
||||
def another_lru_cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@functools.lru_cache()
|
||||
def called_lru_cached_instance_method(self, y):
|
||||
...
|
||||
|
||||
@lru_cache()
|
||||
def another_called_lru_cached_instance_method(self, y):
|
||||
...
|
||||
76
resources/test/fixtures/B021.py
vendored
Normal file
76
resources/test/fixtures/B021.py
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
f"""
|
||||
Should emit:
|
||||
B021 - on lines 14, 22, 30, 38, 46, 54, 62, 70, 73
|
||||
"""
|
||||
|
||||
VARIABLE = "world"
|
||||
|
||||
|
||||
def foo1():
|
||||
"""hello world!"""
|
||||
|
||||
|
||||
def foo2():
|
||||
f"""hello {VARIABLE}!"""
|
||||
|
||||
|
||||
class bar1:
|
||||
"""hello world!"""
|
||||
|
||||
|
||||
class bar2:
|
||||
f"""hello {VARIABLE}!"""
|
||||
|
||||
|
||||
def foo1():
|
||||
"""hello world!"""
|
||||
|
||||
|
||||
def foo2():
|
||||
f"""hello {VARIABLE}!"""
|
||||
|
||||
|
||||
class bar1:
|
||||
"""hello world!"""
|
||||
|
||||
|
||||
class bar2:
|
||||
f"""hello {VARIABLE}!"""
|
||||
|
||||
|
||||
def foo1():
|
||||
"hello world!"
|
||||
|
||||
|
||||
def foo2():
|
||||
f"hello {VARIABLE}!"
|
||||
|
||||
|
||||
class bar1:
|
||||
"hello world!"
|
||||
|
||||
|
||||
class bar2:
|
||||
f"hello {VARIABLE}!"
|
||||
|
||||
|
||||
def foo1():
|
||||
"hello world!"
|
||||
|
||||
|
||||
def foo2():
|
||||
f"hello {VARIABLE}!"
|
||||
|
||||
|
||||
class bar1:
|
||||
"hello world!"
|
||||
|
||||
|
||||
class bar2:
|
||||
f"hello {VARIABLE}!"
|
||||
|
||||
|
||||
def baz():
|
||||
f"""I'm probably a docstring: {VARIABLE}!"""
|
||||
print(f"""I'm a normal string""")
|
||||
f"""Don't detect me!"""
|
||||
1
resources/test/fixtures/F401_0.py
vendored
1
resources/test/fixtures/F401_0.py
vendored
@@ -28,7 +28,6 @@ from blah import ClassA, ClassB, ClassC
|
||||
if TYPE_CHECKING:
|
||||
from models import Fruit, Nut, Vegetable
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import shelve
|
||||
import importlib
|
||||
|
||||
11
resources/test/fixtures/S101.py
vendored
Normal file
11
resources/test/fixtures/S101.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# Error
|
||||
assert True
|
||||
|
||||
def fn():
|
||||
x = 1
|
||||
|
||||
# Error
|
||||
assert x == 1
|
||||
|
||||
# Error
|
||||
assert x == 2
|
||||
5
resources/test/fixtures/S102.py
vendored
Normal file
5
resources/test/fixtures/S102.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
def fn():
|
||||
# Error
|
||||
exec('x = 2')
|
||||
|
||||
exec('y = 3')
|
||||
19
resources/test/fixtures/S104.py
vendored
Normal file
19
resources/test/fixtures/S104.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
def func(address):
|
||||
print(address)
|
||||
|
||||
|
||||
# OK
|
||||
"OK"
|
||||
|
||||
# Error
|
||||
"0.0.0.0"
|
||||
'0.0.0.0'
|
||||
|
||||
|
||||
# Error
|
||||
func("0.0.0.0")
|
||||
|
||||
|
||||
def my_func():
|
||||
x = "0.0.0.0"
|
||||
print(x)
|
||||
53
resources/test/fixtures/S105.py
vendored
Normal file
53
resources/test/fixtures/S105.py
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
d = {}
|
||||
|
||||
# OK
|
||||
safe = "s3cr3t"
|
||||
password = True
|
||||
password = safe
|
||||
password is True
|
||||
password == 1
|
||||
d["safe"] = "s3cr3t"
|
||||
|
||||
# Errors
|
||||
password = "s3cr3t"
|
||||
_pass = "s3cr3t"
|
||||
passwd = "s3cr3t"
|
||||
pwd = "s3cr3t"
|
||||
secret = "s3cr3t"
|
||||
token = "s3cr3t"
|
||||
secrete = "s3cr3t"
|
||||
safe = password = "s3cr3t"
|
||||
password = safe = "s3cr3t"
|
||||
|
||||
d["password"] = "s3cr3t"
|
||||
d["pass"] = "s3cr3t"
|
||||
d["passwd"] = "s3cr3t"
|
||||
d["pwd"] = "s3cr3t"
|
||||
d["secret"] = "s3cr3t"
|
||||
d["token"] = "s3cr3t"
|
||||
d["secrete"] = "s3cr3t"
|
||||
safe = d["password"] = "s3cr3t"
|
||||
d["password"] = safe = "s3cr3t"
|
||||
|
||||
|
||||
class MyClass:
|
||||
password = "s3cr3t"
|
||||
safe = password
|
||||
|
||||
|
||||
MyClass.password = "s3cr3t"
|
||||
MyClass._pass = "s3cr3t"
|
||||
MyClass.passwd = "s3cr3t"
|
||||
MyClass.pwd = "s3cr3t"
|
||||
MyClass.secret = "s3cr3t"
|
||||
MyClass.token = "s3cr3t"
|
||||
MyClass.secrete = "s3cr3t"
|
||||
|
||||
password == "s3cr3t"
|
||||
_pass == "s3cr3t"
|
||||
passwd == "s3cr3t"
|
||||
pwd == "s3cr3t"
|
||||
secret == "s3cr3t"
|
||||
token == "s3cr3t"
|
||||
secrete == "s3cr3t"
|
||||
password == safe == "s3cr3t"
|
||||
13
resources/test/fixtures/S106.py
vendored
Normal file
13
resources/test/fixtures/S106.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
def func(pos, password):
|
||||
pass
|
||||
|
||||
|
||||
string = "Hello World"
|
||||
|
||||
# OK
|
||||
func("s3cr3t")
|
||||
func(1, password=string)
|
||||
func(pos="s3cr3t", password=string)
|
||||
|
||||
# Error
|
||||
func(1, password="s3cr3t")
|
||||
30
resources/test/fixtures/S107.py
vendored
Normal file
30
resources/test/fixtures/S107.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
def ok(first, default="default"):
|
||||
pass
|
||||
|
||||
|
||||
def default(first, password="default"):
|
||||
pass
|
||||
|
||||
|
||||
def ok_posonly(first, /, pos, default="posonly"):
|
||||
pass
|
||||
|
||||
|
||||
def default_posonly(first, /, pos, password="posonly"):
|
||||
pass
|
||||
|
||||
|
||||
def ok_kwonly(first, *, default="kwonly"):
|
||||
pass
|
||||
|
||||
|
||||
def default_kwonly(first, *, password="kwonly"):
|
||||
pass
|
||||
|
||||
|
||||
def ok_all(first, /, pos, default="posonly", *, kwonly="kwonly"):
|
||||
pass
|
||||
|
||||
|
||||
def default_all(first, /, pos, secret="posonly", *, password="kwonly"):
|
||||
pass
|
||||
15
resources/test/fixtures/U010.py
vendored
15
resources/test/fixtures/U010.py
vendored
@@ -1,5 +1,14 @@
|
||||
from __future__ import annotations, nested_scopes, generators
|
||||
|
||||
from __future__ import nested_scopes, generators
|
||||
from __future__ import with_statement, unicode_literals
|
||||
from __future__ import absolute_import, division
|
||||
|
||||
from __future__ import generator_stop
|
||||
from __future__ import print_function, generator_stop
|
||||
from __future__ import invalid_module, generators
|
||||
|
||||
if True:
|
||||
from __future__ import generator_stop
|
||||
from __future__ import generators
|
||||
|
||||
if True:
|
||||
from __future__ import generator_stop
|
||||
from __future__ import invalid_module, generators
|
||||
|
||||
52
resources/test/fixtures/U012.py
vendored
Normal file
52
resources/test/fixtures/U012.py
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# ASCII literals should be replaced by a bytes literal
|
||||
"foo".encode("utf-8") # b"foo"
|
||||
"foo".encode("u8") # b"foo"
|
||||
"foo".encode() # b"foo"
|
||||
"foo".encode("UTF8") # b"foo"
|
||||
U"foo".encode("utf-8") # b"foo"
|
||||
"foo".encode(encoding="utf-8") # b"foo"
|
||||
"""
|
||||
Lorem
|
||||
|
||||
Ipsum
|
||||
""".encode(
|
||||
"utf-8"
|
||||
)
|
||||
# b"""
|
||||
# Lorem
|
||||
#
|
||||
# Ipsum
|
||||
# """
|
||||
|
||||
# `encode` on variables should not be processed.
|
||||
string = "hello there"
|
||||
string.encode("utf-8")
|
||||
|
||||
bar = "bar"
|
||||
f"foo{bar}".encode("utf-8") # f"foo{bar}".encode()
|
||||
encoding = "latin"
|
||||
"foo".encode(encoding)
|
||||
f"foo{bar}".encode(encoding)
|
||||
|
||||
# `encode` with custom args and kwargs should not be processed.
|
||||
"foo".encode("utf-8", errors="replace")
|
||||
"foo".encode("utf-8", "replace")
|
||||
"foo".encode(errors="replace")
|
||||
"foo".encode(encoding="utf-8", errors="replace")
|
||||
|
||||
# `encode` with custom args and kwargs on unicode should not be processed.
|
||||
"unicode text©".encode("utf-8", errors="replace")
|
||||
"unicode text©".encode("utf-8", "replace")
|
||||
"unicode text©".encode(errors="replace")
|
||||
"unicode text©".encode(encoding="utf-8", errors="replace")
|
||||
|
||||
# Unicode literals should only be stripped of default encoding.
|
||||
"unicode text©".encode("utf-8") # "unicode text©".encode()
|
||||
"unicode text©".encode()
|
||||
"unicode text©".encode(encoding="UTF8") # "unicode text©".encode()
|
||||
|
||||
r"fo\o".encode("utf-8") # br"fo\o"
|
||||
u"foo".encode("utf-8") # b"foo"
|
||||
R"fo\o".encode("utf-8") # br"fo\o"
|
||||
U"foo".encode("utf-8") # b"foo"
|
||||
print("foo".encode()) # print(b"foo")
|
||||
15
resources/test/fixtures/isort/fit_line_length.py
vendored
15
resources/test/fixtures/isort/fit_line_length.py
vendored
@@ -1 +1,14 @@
|
||||
from collections import Collection
|
||||
from line_with_88 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
from line_with_89 import (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
)
|
||||
|
||||
if indented:
|
||||
from line_with_88 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
from line_with_89 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
from line_with_90 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
from line_with_91 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
from line_with_92 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
from line_with_93 import (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
)
|
||||
|
||||
3
resources/test/fixtures/pyproject.toml
vendored
3
resources/test/fixtures/pyproject.toml
vendored
@@ -7,6 +7,9 @@ extend-exclude = [
|
||||
]
|
||||
per-file-ignores = { "__init__.py" = ["F401"] }
|
||||
|
||||
[tool.ruff.flake8-bugbear]
|
||||
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
|
||||
|
||||
[tool.ruff.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
multiline-quotes = "double"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.113"
|
||||
version = "0.0.116"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, StmtKind};
|
||||
@@ -20,6 +19,7 @@ fn compose_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an `Expr` to its call path (like `List`, or `typing.List`).
|
||||
pub fn compose_call_path(expr: &Expr) -> Option<String> {
|
||||
let mut segments = vec![];
|
||||
compose_call_path_inner(expr, &mut segments);
|
||||
@@ -39,31 +39,6 @@ pub fn match_name_or_attr(expr: &Expr, target: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the `Expr` is a reference to `${module}.${target}`.
|
||||
///
|
||||
/// Useful for, e.g., ensuring that a `Union` reference represents
|
||||
/// `typing.Union`.
|
||||
pub fn match_name_or_attr_from_module(
|
||||
expr: &Expr,
|
||||
target: &str,
|
||||
module: &str,
|
||||
imports: Option<&BTreeSet<&str>>,
|
||||
) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Attribute { value, attr, .. } => match &value.node {
|
||||
ExprKind::Name { id, .. } => id == module && target == attr,
|
||||
_ => false,
|
||||
},
|
||||
ExprKind::Name { id, .. } => {
|
||||
target == id
|
||||
&& imports
|
||||
.map(|imports| imports.contains(&id.as_str()))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
static DUNDER_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"__[^\s]+__").unwrap());
|
||||
|
||||
pub fn is_assignment_to_a_dunder(node: &StmtKind) -> bool {
|
||||
@@ -141,3 +116,145 @@ pub fn to_absolute(relative: &Location, base: &Location) -> Location {
|
||||
Location::new(relative.row() + base.row() - 1, relative.column())
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if the `Expr` is a reference to `${module}.${target}`.
|
||||
///
|
||||
/// Useful for, e.g., ensuring that a `Union` reference represents
|
||||
/// `typing.Union`.
|
||||
pub fn match_module_member(
|
||||
expr: &Expr,
|
||||
target: &str,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
) -> bool {
|
||||
compose_call_path(expr)
|
||||
.map(|expr| match_call_path(&expr, target, from_imports))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Return `true` if the `call_path` is a reference to `${module}.${target}`.
|
||||
///
|
||||
/// Optimized version of `match_module_member` for pre-computed call paths.
|
||||
pub fn match_call_path(
|
||||
call_path: &str,
|
||||
target: &str,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
) -> bool {
|
||||
// Case (1a): it's the same call path (`import typing`, `typing.re.Match`).
|
||||
// Case (1b): it's the same call path (`import typing.re`, `typing.re.Match`).
|
||||
if call_path == target {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some((parent, member)) = target.rsplit_once('.') {
|
||||
// Case (2): We imported star from the parent (`from typing.re import *`,
|
||||
// `Match`).
|
||||
if call_path == member
|
||||
&& from_imports
|
||||
.get(parent)
|
||||
.map(|imports| imports.contains("*"))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Case (3): We imported from the parent (`from typing.re import Match`,
|
||||
// `Match`)
|
||||
if call_path == member
|
||||
&& from_imports
|
||||
.get(parent)
|
||||
.map(|imports| imports.contains(member))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Case (4): We imported from the grandparent (`from typing import re`,
|
||||
// `re.Match`)
|
||||
let mut parts = target.rsplitn(3, '.');
|
||||
let member = parts.next();
|
||||
let parent = parts.next();
|
||||
let grandparent = parts.next();
|
||||
if let (Some(member), Some(parent), Some(grandparent)) = (member, parent, grandparent) {
|
||||
if call_path == format!("{parent}.{member}")
|
||||
&& from_imports
|
||||
.get(grandparent)
|
||||
.map(|imports| imports.contains(parent))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use rustpython_parser::parser;
|
||||
|
||||
use crate::ast::helpers::match_module_member;
|
||||
|
||||
#[test]
|
||||
fn fully_qualified() -> Result<()> {
|
||||
let expr = parser::parse_expression("typing.re.Match", "<filename>")?;
|
||||
assert!(match_module_member(
|
||||
&expr,
|
||||
"typing.re.Match",
|
||||
&FnvHashMap::default()
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unimported() -> Result<()> {
|
||||
let expr = parser::parse_expression("Match", "<filename>")?;
|
||||
assert!(!match_module_member(
|
||||
&expr,
|
||||
"typing.re.Match",
|
||||
&FnvHashMap::default(),
|
||||
));
|
||||
let expr = parser::parse_expression("re.Match", "<filename>")?;
|
||||
assert!(!match_module_member(
|
||||
&expr,
|
||||
"typing.re.Match",
|
||||
&FnvHashMap::default(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_star() -> Result<()> {
|
||||
let expr = parser::parse_expression("Match", "<filename>")?;
|
||||
assert!(match_module_member(
|
||||
&expr,
|
||||
"typing.re.Match",
|
||||
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["*"]))])
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_parent() -> Result<()> {
|
||||
let expr = parser::parse_expression("Match", "<filename>")?;
|
||||
assert!(match_module_member(
|
||||
&expr,
|
||||
"typing.re.Match",
|
||||
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["Match"]))])
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_grandparent() -> Result<()> {
|
||||
let expr = parser::parse_expression("re.Match", "<filename>")?;
|
||||
assert!(match_module_member(
|
||||
&expr,
|
||||
"typing.re.Match",
|
||||
&FnvHashMap::from_iter([("typing", FnvHashSet::from_iter(["re"]))])
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,8 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
|
||||
}
|
||||
|
||||
/// Check if a node is parent of a conditional branch.
|
||||
pub fn on_conditional_branch(parent_stack: &[usize], parents: &[&Stmt]) -> bool {
|
||||
for index in parent_stack.iter().rev() {
|
||||
let parent = parents[*index];
|
||||
pub fn on_conditional_branch<'a>(parents: &mut impl Iterator<Item = &'a Stmt>) -> bool {
|
||||
parents.any(|parent| {
|
||||
if matches!(parent.node, StmtKind::If { .. } | StmtKind::While { .. }) {
|
||||
return true;
|
||||
}
|
||||
@@ -78,24 +77,18 @@ pub fn on_conditional_branch(parent_stack: &[usize], parents: &[&Stmt]) -> bool
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if a node is in a nested block.
|
||||
pub fn in_nested_block(parent_stack: &[usize], parents: &[&Stmt]) -> bool {
|
||||
for index in parent_stack.iter().rev() {
|
||||
let parent = parents[*index];
|
||||
if matches!(
|
||||
pub fn in_nested_block<'a>(parents: &mut impl Iterator<Item = &'a Stmt>) -> bool {
|
||||
parents.any(|parent| {
|
||||
matches!(
|
||||
parent.node,
|
||||
StmtKind::Try { .. } | StmtKind::If { .. } | StmtKind::With { .. }
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if a node represents an unpacking assignment.
|
||||
|
||||
185
src/check_ast.rs
185
src/check_ast.rs
@@ -1,9 +1,10 @@
|
||||
//! Lint rules based on AST traversal.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use log::error;
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
|
||||
@@ -11,7 +12,7 @@ use rustpython_parser::ast::{
|
||||
};
|
||||
use rustpython_parser::parser;
|
||||
|
||||
use crate::ast::helpers::{extract_handler_names, match_name_or_attr_from_module};
|
||||
use crate::ast::helpers::{extract_handler_names, match_module_member};
|
||||
use crate::ast::operations::extract_all_names;
|
||||
use crate::ast::relocate::relocate_expr;
|
||||
use crate::ast::types::{
|
||||
@@ -32,25 +33,11 @@ use crate::settings::Settings;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::visibility::{module_visibility, transition_scope, Modifier, Visibility, VisibleScope};
|
||||
use crate::{
|
||||
docstrings, flake8_2020, flake8_annotations, flake8_bugbear, flake8_builtins,
|
||||
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins,
|
||||
flake8_comprehensions, flake8_print, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
|
||||
};
|
||||
|
||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
const TRACK_FROM_IMPORTS: [&str; 12] = [
|
||||
"collections",
|
||||
"collections.abc",
|
||||
"contextlib",
|
||||
"functools",
|
||||
"re",
|
||||
"six",
|
||||
"sys",
|
||||
"typing",
|
||||
"typing.io",
|
||||
"typing.re",
|
||||
"typing_extensions",
|
||||
"weakref",
|
||||
];
|
||||
|
||||
pub struct Checker<'a> {
|
||||
// Input data.
|
||||
@@ -64,9 +51,9 @@ pub struct Checker<'a> {
|
||||
definitions: Vec<(Definition<'a>, Visibility)>,
|
||||
// Edit tracking.
|
||||
// TODO(charlie): Instead of exposing deletions, wrap in a public API.
|
||||
pub(crate) deletions: BTreeSet<usize>,
|
||||
pub(crate) deletions: FnvHashSet<usize>,
|
||||
// Import tracking.
|
||||
pub(crate) from_imports: BTreeMap<&'a str, BTreeSet<&'a str>>,
|
||||
pub(crate) from_imports: FnvHashMap<&'a str, FnvHashSet<&'a str>>,
|
||||
// Retain all scopes and parent nodes, along with a stack of indexes to track which are active
|
||||
// at various points in time.
|
||||
pub(crate) parents: Vec<&'a Stmt>,
|
||||
@@ -168,13 +155,12 @@ impl<'a> Checker<'a> {
|
||||
|
||||
/// Return `true` if the `Expr` is a reference to `typing.${target}`.
|
||||
pub fn match_typing_module(&self, expr: &Expr, target: &str) -> bool {
|
||||
match_name_or_attr_from_module(expr, target, "typing", self.from_imports.get("typing"))
|
||||
match_module_member(expr, &format!("typing.{target}"), &self.from_imports)
|
||||
|| (typing::in_extensions(target)
|
||||
&& match_name_or_attr_from_module(
|
||||
&& match_module_member(
|
||||
expr,
|
||||
target,
|
||||
"typing_extensions",
|
||||
self.from_imports.get("typing_extensions"),
|
||||
&format!("typing_extensions.{target}"),
|
||||
&self.from_imports,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -205,7 +191,13 @@ where
|
||||
self.futures_allowed = false;
|
||||
if !self.seen_import_boundary
|
||||
&& !helpers::is_assignment_to_a_dunder(node)
|
||||
&& !operations::in_nested_block(&self.parent_stack, &self.parents)
|
||||
&& !operations::in_nested_block(
|
||||
&mut self
|
||||
.parent_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|index| self.parents[*index]),
|
||||
)
|
||||
{
|
||||
self.seen_import_boundary = true;
|
||||
}
|
||||
@@ -346,6 +338,15 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::B018) {
|
||||
flake8_bugbear::plugins::useless_expression(self, body);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::B019) {
|
||||
flake8_bugbear::plugins::cached_instance_method(self, decorator_list);
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::S107) {
|
||||
self.add_checks(
|
||||
flake8_bandit::plugins::hardcoded_password_default(args).into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
self.check_builtin_shadowing(name, Range::from_located(stmt), true);
|
||||
|
||||
@@ -400,14 +401,14 @@ where
|
||||
StmtKind::Return { .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::F706) {
|
||||
if let Some(index) = self.scope_stack.last().cloned() {
|
||||
match self.scopes[index].kind {
|
||||
ScopeKind::Class(_) | ScopeKind::Module => {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::ReturnOutsideFunction,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
if matches!(
|
||||
self.scopes[index].kind,
|
||||
ScopeKind::Class(_) | ScopeKind::Module
|
||||
) {
|
||||
self.add_check(Check::new(
|
||||
CheckKind::ReturnOutsideFunction,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -580,17 +581,15 @@ where
|
||||
// references like `from typing import Union`.
|
||||
if level.map(|level| level == 0).unwrap_or(true) {
|
||||
if let Some(module) = module {
|
||||
if TRACK_FROM_IMPORTS.contains(&module.as_str()) {
|
||||
self.from_imports
|
||||
.entry(module)
|
||||
.or_insert_with(BTreeSet::new)
|
||||
.extend(
|
||||
names
|
||||
.iter()
|
||||
.filter(|alias| alias.node.asname.is_none())
|
||||
.map(|alias| alias.node.name.as_str()),
|
||||
)
|
||||
}
|
||||
self.from_imports
|
||||
.entry(module)
|
||||
.or_insert_with(FnvHashSet::default)
|
||||
.extend(
|
||||
names
|
||||
.iter()
|
||||
.filter(|alias| alias.node.asname.is_none())
|
||||
.map(|alias| alias.node.name.as_str()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,6 +602,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if let Some("__future__") = module.as_deref() {
|
||||
if self.settings.enabled.contains(&CheckCode::U010) {
|
||||
pyupgrade::plugins::unnecessary_future_import(self, stmt, names);
|
||||
}
|
||||
}
|
||||
|
||||
for alias in names {
|
||||
if let Some("__future__") = module.as_deref() {
|
||||
let name = alias.node.asname.as_ref().unwrap_or(&alias.node.name);
|
||||
@@ -635,14 +640,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::U010) {
|
||||
pyupgrade::plugins::unnecessary_future_import(
|
||||
self,
|
||||
stmt,
|
||||
&alias.node.name,
|
||||
);
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::F404) && !self.futures_allowed
|
||||
{
|
||||
self.add_check(Check::new(
|
||||
@@ -802,6 +799,9 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::B011) {
|
||||
flake8_bugbear::plugins::assert_false(self, stmt, test, msg);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::S101) {
|
||||
self.add_check(flake8_bandit::plugins::assert_used(stmt));
|
||||
}
|
||||
}
|
||||
StmtKind::With { items, .. } | StmtKind::AsyncWith { items, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::B017) {
|
||||
@@ -842,6 +842,13 @@ where
|
||||
if self.settings.enabled.contains(&CheckCode::B003) {
|
||||
flake8_bugbear::plugins::assignment_to_os_environ(self, targets);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::S105) {
|
||||
if let Some(check) =
|
||||
flake8_bandit::plugins::assign_hardcoded_password_string(value, targets)
|
||||
{
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
StmtKind::AnnAssign { value, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::E731) {
|
||||
@@ -868,6 +875,9 @@ where
|
||||
let prev_visible_scope = self.visible_scope.clone();
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { body, .. } | StmtKind::AsyncFunctionDef { body, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::B021) {
|
||||
flake8_bugbear::plugins::f_string_docstring(self, body);
|
||||
}
|
||||
let definition = docstrings::extraction::extract(
|
||||
&self.visible_scope,
|
||||
stmt,
|
||||
@@ -887,6 +897,9 @@ where
|
||||
));
|
||||
}
|
||||
StmtKind::ClassDef { body, .. } => {
|
||||
if self.settings.enabled.contains(&CheckCode::B021) {
|
||||
flake8_bugbear::plugins::f_string_docstring(self, body);
|
||||
}
|
||||
let definition = docstrings::extraction::extract(
|
||||
&self.visible_scope,
|
||||
stmt,
|
||||
@@ -909,6 +922,9 @@ where
|
||||
finalbody,
|
||||
} => {
|
||||
self.except_handlers.push(extract_handler_names(handlers));
|
||||
if self.settings.enabled.contains(&CheckCode::B012) {
|
||||
flake8_bugbear::plugins::jump_statement_in_finally(self, finalbody);
|
||||
}
|
||||
for stmt in body {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
@@ -1017,7 +1033,7 @@ where
|
||||
// Ex) List[...]
|
||||
if self.settings.enabled.contains(&CheckCode::U006)
|
||||
&& self.settings.target_version >= PythonVersion::Py39
|
||||
&& typing::is_pep585_builtin(expr, self.from_imports.get("typing"))
|
||||
&& typing::is_pep585_builtin(expr, &self.from_imports)
|
||||
{
|
||||
pyupgrade::plugins::use_pep585_annotation(self, expr, id);
|
||||
}
|
||||
@@ -1049,7 +1065,7 @@ where
|
||||
// Ex) typing.List[...]
|
||||
if self.settings.enabled.contains(&CheckCode::U006)
|
||||
&& self.settings.target_version >= PythonVersion::Py39
|
||||
&& typing::is_pep585_builtin(expr, self.from_imports.get("typing"))
|
||||
&& typing::is_pep585_builtin(expr, &self.from_imports)
|
||||
{
|
||||
pyupgrade::plugins::use_pep585_annotation(self, expr, attr);
|
||||
}
|
||||
@@ -1072,6 +1088,10 @@ where
|
||||
pyupgrade::plugins::super_call_with_parameters(self, expr, func, args);
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::U012) {
|
||||
pyupgrade::plugins::unnecessary_encode_utf8(self, expr, func, args, keywords);
|
||||
}
|
||||
|
||||
// flake8-print
|
||||
if self.settings.enabled.contains(&CheckCode::T201)
|
||||
|| self.settings.enabled.contains(&CheckCode::T203)
|
||||
@@ -1103,6 +1123,16 @@ where
|
||||
self, args, keywords,
|
||||
);
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::S102) {
|
||||
if let Some(check) = flake8_bandit::plugins::exec_used(expr, func) {
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::S106) {
|
||||
self.add_checks(
|
||||
flake8_bandit::plugins::hardcoded_password_func_arg(keywords).into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
// flake8-comprehensions
|
||||
if self.settings.enabled.contains(&CheckCode::C400) {
|
||||
@@ -1451,6 +1481,16 @@ where
|
||||
{
|
||||
flake8_2020::plugins::compare(self, left, ops, comparators);
|
||||
}
|
||||
|
||||
if self.settings.enabled.contains(&CheckCode::S105) {
|
||||
self.add_checks(
|
||||
flake8_bandit::plugins::compare_to_hardcoded_password_string(
|
||||
left,
|
||||
comparators,
|
||||
)
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
@@ -1460,6 +1500,14 @@ where
|
||||
self.deferred_string_annotations
|
||||
.push((Range::from_located(expr), value));
|
||||
}
|
||||
if self.settings.enabled.contains(&CheckCode::S104) {
|
||||
if let Some(check) = flake8_bandit::plugins::hardcoded_bind_all_interfaces(
|
||||
value,
|
||||
&Range::from_located(expr),
|
||||
) {
|
||||
self.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Lambda { args, .. } => {
|
||||
// Visit the arguments, but avoid the body, which will be deferred.
|
||||
@@ -2180,7 +2228,13 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn handle_node_delete(&mut self, expr: &Expr) {
|
||||
if let ExprKind::Name { id, .. } = &expr.node {
|
||||
if operations::on_conditional_branch(&self.parent_stack, &self.parents) {
|
||||
if operations::on_conditional_branch(
|
||||
&mut self
|
||||
.parent_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|index| self.parents[*index]),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2200,6 +2254,9 @@ impl<'a> Checker<'a> {
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
if self.settings.enabled.contains(&CheckCode::B021) {
|
||||
flake8_bugbear::plugins::f_string_docstring(self, python_ast);
|
||||
}
|
||||
let docstring = docstrings::extraction::docstring_from(python_ast);
|
||||
self.definitions.push((
|
||||
Definition {
|
||||
@@ -2410,16 +2467,20 @@ impl<'a> Checker<'a> {
|
||||
.iter()
|
||||
.map(|index| self.parents[*index])
|
||||
.collect();
|
||||
|
||||
let removal_fn = match kind {
|
||||
match match kind {
|
||||
ImportKind::Import => pyflakes::fixes::remove_unused_imports,
|
||||
ImportKind::ImportFrom => pyflakes::fixes::remove_unused_import_froms,
|
||||
};
|
||||
|
||||
match removal_fn(self.locator, &full_names, child, parent, &deleted) {
|
||||
Ok(fix) => Some(fix),
|
||||
}(
|
||||
self.locator, &full_names, child, parent, &deleted
|
||||
) {
|
||||
Ok(fix) => {
|
||||
if fix.patch.content.is_empty() || fix.patch.content == "pass" {
|
||||
self.deletions.insert(defined_by);
|
||||
}
|
||||
Some(fix)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to fix unused imports: {}", e);
|
||||
error!("Failed to remove unused imports: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ mod tests {
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
);
|
||||
return checks;
|
||||
checks
|
||||
};
|
||||
assert!(!check_with_max_line_length(6).is_empty());
|
||||
assert!(check_with_max_line_length(7).is_empty());
|
||||
|
||||
157
src/checks.rs
157
src/checks.rs
@@ -87,12 +87,15 @@ pub enum CheckCode {
|
||||
B009,
|
||||
B010,
|
||||
B011,
|
||||
B012,
|
||||
B013,
|
||||
B014,
|
||||
B015,
|
||||
B016,
|
||||
B017,
|
||||
B018,
|
||||
B019,
|
||||
B021,
|
||||
B025,
|
||||
B026,
|
||||
// flake8-comprehensions
|
||||
@@ -155,6 +158,7 @@ pub enum CheckCode {
|
||||
U009,
|
||||
U010,
|
||||
U011,
|
||||
U012,
|
||||
// pydocstyle
|
||||
D100,
|
||||
D101,
|
||||
@@ -218,6 +222,13 @@ pub enum CheckCode {
|
||||
N818,
|
||||
// isort
|
||||
I001,
|
||||
// flake8-bandit
|
||||
S101,
|
||||
S102,
|
||||
S104,
|
||||
S105,
|
||||
S106,
|
||||
S107,
|
||||
// Ruff
|
||||
RUF001,
|
||||
RUF002,
|
||||
@@ -234,6 +245,7 @@ pub enum CheckCategory {
|
||||
Pydocstyle,
|
||||
Pyupgrade,
|
||||
PEP8Naming,
|
||||
Flake8Bandit,
|
||||
Flake8Comprehensions,
|
||||
Flake8Bugbear,
|
||||
Flake8Builtins,
|
||||
@@ -251,6 +263,7 @@ impl CheckCategory {
|
||||
CheckCategory::Pycodestyle => "pycodestyle",
|
||||
CheckCategory::Pyflakes => "Pyflakes",
|
||||
CheckCategory::Isort => "isort",
|
||||
CheckCategory::Flake8Bandit => "flake8-bandit",
|
||||
CheckCategory::Flake8Builtins => "flake8-builtins",
|
||||
CheckCategory::Flake8Bugbear => "flake8-bugbear",
|
||||
CheckCategory::Flake8Comprehensions => "flake8-comprehensions",
|
||||
@@ -289,6 +302,7 @@ impl CheckCategory {
|
||||
CheckCategory::Pyupgrade => Some("https://pypi.org/project/pyupgrade/3.2.0/"),
|
||||
CheckCategory::Pydocstyle => Some("https://pypi.org/project/pydocstyle/6.1.1/"),
|
||||
CheckCategory::PEP8Naming => Some("https://pypi.org/project/pep8-naming/0.13.2/"),
|
||||
CheckCategory::Flake8Bandit => Some("https://pypi.org/project/flake8-bandit/4.1.1/"),
|
||||
CheckCategory::Ruff => None,
|
||||
CheckCategory::Meta => None,
|
||||
}
|
||||
@@ -370,16 +384,19 @@ pub enum CheckKind {
|
||||
StripWithMultiCharacters,
|
||||
MutableArgumentDefault,
|
||||
UnusedLoopControlVariable(String),
|
||||
FunctionCallArgumentDefault,
|
||||
FunctionCallArgumentDefault(Option<String>),
|
||||
GetAttrWithConstant,
|
||||
SetAttrWithConstant,
|
||||
DoNotAssertFalse,
|
||||
JumpStatementInFinally(String),
|
||||
RedundantTupleInExceptionHandler(String),
|
||||
DuplicateHandlerException(Vec<String>),
|
||||
UselessComparison,
|
||||
CannotRaiseLiteral,
|
||||
NoAssertRaisesException,
|
||||
UselessExpression,
|
||||
CachedInstanceMethod,
|
||||
FStringDocstring,
|
||||
DuplicateTryBlockException(String),
|
||||
StarArgUnpackingAfterKeywordArg,
|
||||
// flake8-comprehensions
|
||||
@@ -440,8 +457,9 @@ pub enum CheckKind {
|
||||
UsePEP604Annotation,
|
||||
SuperCallWithParameters,
|
||||
PEP3120UnnecessaryCodingComment,
|
||||
UnnecessaryFutureImport(String),
|
||||
UnnecessaryFutureImport(Vec<String>),
|
||||
UnnecessaryLRUCacheParams,
|
||||
UnnecessaryEncodeUTF8,
|
||||
// pydocstyle
|
||||
BlankLineAfterLastSection(String),
|
||||
BlankLineAfterSection(String),
|
||||
@@ -505,6 +523,13 @@ pub enum CheckKind {
|
||||
ErrorSuffixOnExceptionName(String),
|
||||
// isort
|
||||
UnsortedImports,
|
||||
// flake8-bandit
|
||||
AssertUsed,
|
||||
ExecUsed,
|
||||
HardcodedBindAllInterfaces,
|
||||
HardcodedPasswordString(String),
|
||||
HardcodedPasswordFuncArg(String),
|
||||
HardcodedPasswordDefault(String),
|
||||
// Ruff
|
||||
AmbiguousUnicodeCharacterString(char, char),
|
||||
AmbiguousUnicodeCharacterDocstring(char, char),
|
||||
@@ -598,10 +623,13 @@ impl CheckCode {
|
||||
CheckCode::B005 => CheckKind::StripWithMultiCharacters,
|
||||
CheckCode::B006 => CheckKind::MutableArgumentDefault,
|
||||
CheckCode::B007 => CheckKind::UnusedLoopControlVariable("i".to_string()),
|
||||
CheckCode::B008 => CheckKind::FunctionCallArgumentDefault,
|
||||
CheckCode::B008 => CheckKind::FunctionCallArgumentDefault(None),
|
||||
CheckCode::B009 => CheckKind::GetAttrWithConstant,
|
||||
CheckCode::B010 => CheckKind::SetAttrWithConstant,
|
||||
CheckCode::B011 => CheckKind::DoNotAssertFalse,
|
||||
CheckCode::B012 => {
|
||||
CheckKind::JumpStatementInFinally("return/continue/break".to_string())
|
||||
}
|
||||
CheckCode::B013 => {
|
||||
CheckKind::RedundantTupleInExceptionHandler("ValueError".to_string())
|
||||
}
|
||||
@@ -610,6 +638,8 @@ impl CheckCode {
|
||||
CheckCode::B016 => CheckKind::CannotRaiseLiteral,
|
||||
CheckCode::B017 => CheckKind::NoAssertRaisesException,
|
||||
CheckCode::B018 => CheckKind::UselessExpression,
|
||||
CheckCode::B019 => CheckKind::CachedInstanceMethod,
|
||||
CheckCode::B021 => CheckKind::FStringDocstring,
|
||||
CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()),
|
||||
CheckCode::B026 => CheckKind::StarArgUnpackingAfterKeywordArg,
|
||||
// flake8-comprehensions
|
||||
@@ -686,8 +716,9 @@ impl CheckCode {
|
||||
CheckCode::U007 => CheckKind::UsePEP604Annotation,
|
||||
CheckCode::U008 => CheckKind::SuperCallWithParameters,
|
||||
CheckCode::U009 => CheckKind::PEP3120UnnecessaryCodingComment,
|
||||
CheckCode::U010 => CheckKind::UnnecessaryFutureImport("...".to_string()),
|
||||
CheckCode::U010 => CheckKind::UnnecessaryFutureImport(vec!["...".to_string()]),
|
||||
CheckCode::U011 => CheckKind::UnnecessaryLRUCacheParams,
|
||||
CheckCode::U012 => CheckKind::UnnecessaryEncodeUTF8,
|
||||
// pydocstyle
|
||||
CheckCode::D100 => CheckKind::PublicModule,
|
||||
CheckCode::D101 => CheckKind::PublicClass,
|
||||
@@ -767,6 +798,13 @@ impl CheckCode {
|
||||
CheckCode::N818 => CheckKind::ErrorSuffixOnExceptionName("...".to_string()),
|
||||
// isort
|
||||
CheckCode::I001 => CheckKind::UnsortedImports,
|
||||
// flake8-bandit
|
||||
CheckCode::S101 => CheckKind::AssertUsed,
|
||||
CheckCode::S102 => CheckKind::ExecUsed,
|
||||
CheckCode::S104 => CheckKind::HardcodedBindAllInterfaces,
|
||||
CheckCode::S105 => CheckKind::HardcodedPasswordString("...".to_string()),
|
||||
CheckCode::S106 => CheckKind::HardcodedPasswordFuncArg("...".to_string()),
|
||||
CheckCode::S107 => CheckKind::HardcodedPasswordDefault("...".to_string()),
|
||||
// Ruff
|
||||
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
|
||||
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
|
||||
@@ -835,12 +873,15 @@ impl CheckCode {
|
||||
CheckCode::B009 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B010 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B011 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B012 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B013 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B014 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B015 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B016 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B017 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B018 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B019 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B021 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B025 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::B026 => CheckCategory::Flake8Bugbear,
|
||||
CheckCode::C400 => CheckCategory::Flake8Comprehensions,
|
||||
@@ -897,6 +938,7 @@ impl CheckCode {
|
||||
CheckCode::U009 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U010 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U011 => CheckCategory::Pyupgrade,
|
||||
CheckCode::U012 => CheckCategory::Pyupgrade,
|
||||
CheckCode::D100 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D101 => CheckCategory::Pydocstyle,
|
||||
CheckCode::D102 => CheckCategory::Pydocstyle,
|
||||
@@ -957,6 +999,12 @@ impl CheckCode {
|
||||
CheckCode::N817 => CheckCategory::PEP8Naming,
|
||||
CheckCode::N818 => CheckCategory::PEP8Naming,
|
||||
CheckCode::I001 => CheckCategory::Isort,
|
||||
CheckCode::S101 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S102 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S104 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S105 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S106 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::S107 => CheckCategory::Flake8Bandit,
|
||||
CheckCode::RUF001 => CheckCategory::Ruff,
|
||||
CheckCode::RUF002 => CheckCategory::Ruff,
|
||||
CheckCode::RUF003 => CheckCategory::Ruff,
|
||||
@@ -1026,16 +1074,19 @@ impl CheckKind {
|
||||
CheckKind::StripWithMultiCharacters => &CheckCode::B005,
|
||||
CheckKind::MutableArgumentDefault => &CheckCode::B006,
|
||||
CheckKind::UnusedLoopControlVariable(_) => &CheckCode::B007,
|
||||
CheckKind::FunctionCallArgumentDefault => &CheckCode::B008,
|
||||
CheckKind::FunctionCallArgumentDefault(_) => &CheckCode::B008,
|
||||
CheckKind::GetAttrWithConstant => &CheckCode::B009,
|
||||
CheckKind::SetAttrWithConstant => &CheckCode::B010,
|
||||
CheckKind::DoNotAssertFalse => &CheckCode::B011,
|
||||
CheckKind::JumpStatementInFinally(_) => &CheckCode::B012,
|
||||
CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013,
|
||||
CheckKind::DuplicateHandlerException(_) => &CheckCode::B014,
|
||||
CheckKind::UselessComparison => &CheckCode::B015,
|
||||
CheckKind::CannotRaiseLiteral => &CheckCode::B016,
|
||||
CheckKind::NoAssertRaisesException => &CheckCode::B017,
|
||||
CheckKind::UselessExpression => &CheckCode::B018,
|
||||
CheckKind::CachedInstanceMethod => &CheckCode::B019,
|
||||
CheckKind::FStringDocstring => &CheckCode::B021,
|
||||
CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025,
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => &CheckCode::B026,
|
||||
// flake8-comprehensions
|
||||
@@ -1098,6 +1149,7 @@ impl CheckKind {
|
||||
CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::U009,
|
||||
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::U010,
|
||||
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::U011,
|
||||
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::U012,
|
||||
// pydocstyle
|
||||
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
|
||||
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
|
||||
@@ -1161,6 +1213,13 @@ impl CheckKind {
|
||||
CheckKind::ErrorSuffixOnExceptionName(..) => &CheckCode::N818,
|
||||
// isort
|
||||
CheckKind::UnsortedImports => &CheckCode::I001,
|
||||
// flake8-bandit
|
||||
CheckKind::AssertUsed => &CheckCode::S101,
|
||||
CheckKind::ExecUsed => &CheckCode::S102,
|
||||
CheckKind::HardcodedBindAllInterfaces => &CheckCode::S104,
|
||||
CheckKind::HardcodedPasswordString(..) => &CheckCode::S105,
|
||||
CheckKind::HardcodedPasswordFuncArg(..) => &CheckCode::S106,
|
||||
CheckKind::HardcodedPasswordDefault(..) => &CheckCode::S107,
|
||||
// Ruff
|
||||
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
|
||||
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
|
||||
@@ -1189,7 +1248,7 @@ impl CheckKind {
|
||||
CheckKind::BreakOutsideLoop => "`break` outside loop".to_string(),
|
||||
CheckKind::ContinueOutsideLoop => "`continue` not properly in loop".to_string(),
|
||||
CheckKind::DefaultExceptNotLast => {
|
||||
"An `except:` block as not the last exception handler".to_string()
|
||||
"An `except` block as not the last exception handler".to_string()
|
||||
}
|
||||
CheckKind::DoNotAssignLambda => {
|
||||
"Do not assign a lambda expression, use a def".to_string()
|
||||
@@ -1326,40 +1385,47 @@ impl CheckKind {
|
||||
`+(+(n))`, which equals `n`. You meant `n += 1`."
|
||||
.to_string(),
|
||||
CheckKind::AssignmentToOsEnviron => {
|
||||
"Assigning to `os.environ` doesn't clear the environment.".to_string()
|
||||
"Assigning to `os.environ` doesn't clear the environment".to_string()
|
||||
}
|
||||
CheckKind::UnreliableCallableCheck => " Using `hasattr(x, '__call__')` to test if x \
|
||||
is callable is unreliable. Use `callable(x)` \
|
||||
for consistent results."
|
||||
.to_string(),
|
||||
CheckKind::StripWithMultiCharacters => "Using `.strip()` with multi-character strings \
|
||||
is misleading the reader."
|
||||
.to_string(),
|
||||
CheckKind::StripWithMultiCharacters => {
|
||||
"Using `.strip()` with multi-character strings is misleading the reader".to_string()
|
||||
}
|
||||
CheckKind::MutableArgumentDefault => {
|
||||
"Do not use mutable data structures for argument defaults.".to_string()
|
||||
"Do not use mutable data structures for argument defaults".to_string()
|
||||
}
|
||||
CheckKind::UnusedLoopControlVariable(name) => format!(
|
||||
"Loop control variable `{name}` not used within the loop body. If this is \
|
||||
intended, start the name with an underscore."
|
||||
),
|
||||
CheckKind::FunctionCallArgumentDefault => {
|
||||
"Do not perform function calls in argument defaults.".to_string()
|
||||
CheckKind::FunctionCallArgumentDefault(name) => {
|
||||
if let Some(name) = name {
|
||||
format!("Do not perform function call `{name}` in argument defaults")
|
||||
} else {
|
||||
"Do not perform function call in argument defaults".to_string()
|
||||
}
|
||||
}
|
||||
CheckKind::GetAttrWithConstant => "Do not call `getattr` with a constant attribute \
|
||||
value, it is not any safer than normal property \
|
||||
value. It is not any safer than normal property \
|
||||
access."
|
||||
.to_string(),
|
||||
CheckKind::SetAttrWithConstant => "Do not call `setattr` with a constant attribute \
|
||||
value, it is not any safer than normal property \
|
||||
value. It is not any safer than normal property \
|
||||
access."
|
||||
.to_string(),
|
||||
CheckKind::DoNotAssertFalse => "Do not `assert False` (`python -O` removes these \
|
||||
calls), raise `AssertionError()`"
|
||||
.to_string(),
|
||||
CheckKind::JumpStatementInFinally(name) => {
|
||||
format!("`{name}` inside finally blocks cause exceptions to be silenced")
|
||||
}
|
||||
CheckKind::RedundantTupleInExceptionHandler(name) => {
|
||||
format!(
|
||||
"A length-one tuple literal is redundant. Write `except {name}:` instead of \
|
||||
`except ({name},):`."
|
||||
"A length-one tuple literal is redundant. Write `except {name}` instead of \
|
||||
`except ({name},)`."
|
||||
)
|
||||
}
|
||||
CheckKind::UselessComparison => "Pointless comparison. This comparison does nothing \
|
||||
@@ -1379,7 +1445,7 @@ impl CheckKind {
|
||||
}
|
||||
}
|
||||
CheckKind::NoAssertRaisesException => {
|
||||
"`assertRaises(Exception):` should be considered evil. It can lead to your test \
|
||||
"`assertRaises(Exception)` should be considered evil. It can lead to your test \
|
||||
passing even if the code being tested is never executed due to a typo. Either \
|
||||
assert for a more specific exception (builtin or custom), use \
|
||||
`assertRaisesRegex`, or use the context manager form of `assertRaises`."
|
||||
@@ -1388,14 +1454,19 @@ impl CheckKind {
|
||||
CheckKind::UselessExpression => {
|
||||
"Found useless expression. Either assign it to a variable or remove it.".to_string()
|
||||
}
|
||||
CheckKind::CachedInstanceMethod => "Use of `functools.lru_cache` or `functools.cache` \
|
||||
on methods can lead to memory leaks"
|
||||
.to_string(),
|
||||
CheckKind::FStringDocstring => "f-string used as docstring. This will be interpreted \
|
||||
by python as a joined string rather than a docstring."
|
||||
.to_string(),
|
||||
CheckKind::DuplicateTryBlockException(name) => {
|
||||
format!("try-except block with duplicate exception `{name}`")
|
||||
}
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => {
|
||||
"Star-arg unpacking after a keyword argument is strongly discouraged, because it \
|
||||
only works when the keyword parameter is declared after all parameters supplied \
|
||||
by the unpacked sequence, and this change of ordering can surprise and mislead \
|
||||
readers."
|
||||
"Star-arg unpacking after a keyword argument is strongly discouraged. It only \
|
||||
works when the keyword parameter is declared after all parameters supplied by the \
|
||||
unpacked sequence, and this change of ordering can surprise and mislead readers."
|
||||
.to_string()
|
||||
}
|
||||
// flake8-comprehensions
|
||||
@@ -1587,12 +1658,19 @@ impl CheckKind {
|
||||
CheckKind::SuperCallWithParameters => {
|
||||
"Use `super()` instead of `super(__class__, self)`".to_string()
|
||||
}
|
||||
CheckKind::UnnecessaryFutureImport(name) => {
|
||||
format!("Unnessary __future__ import `{name}` for target Python version")
|
||||
CheckKind::UnnecessaryFutureImport(names) => {
|
||||
if names.len() == 1 {
|
||||
let import = &names[0];
|
||||
format!("Unnecessary `__future__` import `{import}` for target Python version")
|
||||
} else {
|
||||
let imports = names.iter().map(|name| format!("`{name}`")).join(", ");
|
||||
format!("Unnecessary `__future__` imports {imports} for target Python version")
|
||||
}
|
||||
}
|
||||
CheckKind::UnnecessaryLRUCacheParams => {
|
||||
"Unnessary parameters to functools.lru_cache".to_string()
|
||||
"Unnecessary parameters to `functools.lru_cache`".to_string()
|
||||
}
|
||||
CheckKind::UnnecessaryEncodeUTF8 => "Unnecessary call to `encode` as UTF-8".to_string(),
|
||||
// pydocstyle
|
||||
CheckKind::FitsOnOneLine => "One-line docstring should fit on one line".to_string(),
|
||||
CheckKind::BlankLineAfterSummary => {
|
||||
@@ -1646,7 +1724,7 @@ impl CheckKind {
|
||||
CheckKind::PublicNestedClass => "Missing docstring in public nested class".to_string(),
|
||||
CheckKind::PublicInit => "Missing docstring in `__init__`".to_string(),
|
||||
CheckKind::NoThisPrefix => {
|
||||
"First word of the docstring should not be 'This'".to_string()
|
||||
"First word of the docstring should not be \"This\"".to_string()
|
||||
}
|
||||
CheckKind::SkipDocstring => {
|
||||
"Function decorated with `@overload` shouldn't contain a docstring".to_string()
|
||||
@@ -1754,10 +1832,25 @@ impl CheckKind {
|
||||
format!("Exception name `{name}` should be named with an Error suffix")
|
||||
}
|
||||
CheckKind::PEP3120UnnecessaryCodingComment => {
|
||||
"utf-8 encoding declaration is unnecessary".to_string()
|
||||
"UTF-8 encoding declaration is unnecessary".to_string()
|
||||
}
|
||||
// isort
|
||||
CheckKind::UnsortedImports => "Import block is un-sorted or un-formatted".to_string(),
|
||||
// flake8-bandit
|
||||
CheckKind::AssertUsed => "Use of `assert` detected".to_string(),
|
||||
CheckKind::ExecUsed => "Use of `exec` detected".to_string(),
|
||||
CheckKind::HardcodedBindAllInterfaces => {
|
||||
"Possible binding to all interfaces".to_string()
|
||||
}
|
||||
CheckKind::HardcodedPasswordString(string) => {
|
||||
format!("Possible hardcoded password: `\"{string}\"`")
|
||||
}
|
||||
CheckKind::HardcodedPasswordFuncArg(string) => {
|
||||
format!("Possible hardcoded password: `\"{string}\"`")
|
||||
}
|
||||
CheckKind::HardcodedPasswordDefault(string) => {
|
||||
format!("Possible hardcoded password: `\"{string}\"`")
|
||||
}
|
||||
// Ruff
|
||||
CheckKind::AmbiguousUnicodeCharacterString(confusable, representant) => {
|
||||
format!(
|
||||
@@ -1802,16 +1895,16 @@ impl CheckKind {
|
||||
pub fn summary(&self) -> String {
|
||||
match self {
|
||||
CheckKind::UnaryPrefixIncrement => {
|
||||
"Python does not support the unary prefix increment.".to_string()
|
||||
"Python does not support the unary prefix increment".to_string()
|
||||
}
|
||||
CheckKind::UnusedLoopControlVariable(name) => {
|
||||
format!("Loop control variable `{name}` not used within the loop body.")
|
||||
format!("Loop control variable `{name}` not used within the loop body")
|
||||
}
|
||||
CheckKind::NoAssertRaisesException => {
|
||||
"`assertRaises(Exception):` should be considered evil.".to_string()
|
||||
"`assertRaises(Exception)` should be considered evil".to_string()
|
||||
}
|
||||
CheckKind::StarArgUnpackingAfterKeywordArg => {
|
||||
"Star-arg unpacking after a keyword argument is strongly discouraged.".to_string()
|
||||
"Star-arg unpacking after a keyword argument is strongly discouraged".to_string()
|
||||
}
|
||||
_ => self.body(),
|
||||
}
|
||||
@@ -1859,6 +1952,8 @@ impl CheckKind {
|
||||
| CheckKind::UnnecessaryAbspath
|
||||
| CheckKind::UnnecessaryCollectionCall(_)
|
||||
| CheckKind::UnnecessaryComprehension(_)
|
||||
| CheckKind::UnnecessaryEncodeUTF8
|
||||
| CheckKind::UnnecessaryFutureImport(_)
|
||||
| CheckKind::UnnecessaryGeneratorDict
|
||||
| CheckKind::UnnecessaryGeneratorList
|
||||
| CheckKind::UnnecessaryGeneratorSet
|
||||
|
||||
@@ -47,13 +47,16 @@ pub enum CheckCodePrefix {
|
||||
B01,
|
||||
B010,
|
||||
B011,
|
||||
B012,
|
||||
B013,
|
||||
B014,
|
||||
B015,
|
||||
B016,
|
||||
B017,
|
||||
B018,
|
||||
B019,
|
||||
B02,
|
||||
B021,
|
||||
B025,
|
||||
B026,
|
||||
C,
|
||||
@@ -244,6 +247,15 @@ pub enum CheckCodePrefix {
|
||||
RUF001,
|
||||
RUF002,
|
||||
RUF003,
|
||||
S,
|
||||
S1,
|
||||
S10,
|
||||
S101,
|
||||
S102,
|
||||
S104,
|
||||
S105,
|
||||
S106,
|
||||
S107,
|
||||
T,
|
||||
T2,
|
||||
T20,
|
||||
@@ -264,6 +276,7 @@ pub enum CheckCodePrefix {
|
||||
U01,
|
||||
U010,
|
||||
U011,
|
||||
U012,
|
||||
W,
|
||||
W2,
|
||||
W29,
|
||||
@@ -362,12 +375,15 @@ impl CheckCodePrefix {
|
||||
CheckCode::B009,
|
||||
CheckCode::B010,
|
||||
CheckCode::B011,
|
||||
CheckCode::B012,
|
||||
CheckCode::B013,
|
||||
CheckCode::B014,
|
||||
CheckCode::B015,
|
||||
CheckCode::B016,
|
||||
CheckCode::B017,
|
||||
CheckCode::B018,
|
||||
CheckCode::B019,
|
||||
CheckCode::B021,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
],
|
||||
@@ -382,12 +398,15 @@ impl CheckCodePrefix {
|
||||
CheckCode::B009,
|
||||
CheckCode::B010,
|
||||
CheckCode::B011,
|
||||
CheckCode::B012,
|
||||
CheckCode::B013,
|
||||
CheckCode::B014,
|
||||
CheckCode::B015,
|
||||
CheckCode::B016,
|
||||
CheckCode::B017,
|
||||
CheckCode::B018,
|
||||
CheckCode::B019,
|
||||
CheckCode::B021,
|
||||
CheckCode::B025,
|
||||
CheckCode::B026,
|
||||
],
|
||||
@@ -412,22 +431,27 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B01 => vec![
|
||||
CheckCode::B010,
|
||||
CheckCode::B011,
|
||||
CheckCode::B012,
|
||||
CheckCode::B013,
|
||||
CheckCode::B014,
|
||||
CheckCode::B015,
|
||||
CheckCode::B016,
|
||||
CheckCode::B017,
|
||||
CheckCode::B018,
|
||||
CheckCode::B019,
|
||||
],
|
||||
CheckCodePrefix::B010 => vec![CheckCode::B010],
|
||||
CheckCodePrefix::B011 => vec![CheckCode::B011],
|
||||
CheckCodePrefix::B012 => vec![CheckCode::B012],
|
||||
CheckCodePrefix::B013 => vec![CheckCode::B013],
|
||||
CheckCodePrefix::B014 => vec![CheckCode::B014],
|
||||
CheckCodePrefix::B015 => vec![CheckCode::B015],
|
||||
CheckCodePrefix::B016 => vec![CheckCode::B016],
|
||||
CheckCodePrefix::B017 => vec![CheckCode::B017],
|
||||
CheckCodePrefix::B018 => vec![CheckCode::B018],
|
||||
CheckCodePrefix::B02 => vec![CheckCode::B025, CheckCode::B026],
|
||||
CheckCodePrefix::B019 => vec![CheckCode::B019],
|
||||
CheckCodePrefix::B02 => vec![CheckCode::B021, CheckCode::B025, CheckCode::B026],
|
||||
CheckCodePrefix::B021 => vec![CheckCode::B021],
|
||||
CheckCodePrefix::B025 => vec![CheckCode::B025],
|
||||
CheckCodePrefix::B026 => vec![CheckCode::B026],
|
||||
CheckCodePrefix::C => vec![
|
||||
@@ -982,6 +1006,36 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::RUF001 => vec![CheckCode::RUF001],
|
||||
CheckCodePrefix::RUF002 => vec![CheckCode::RUF002],
|
||||
CheckCodePrefix::RUF003 => vec![CheckCode::RUF003],
|
||||
CheckCodePrefix::S => vec![
|
||||
CheckCode::S101,
|
||||
CheckCode::S102,
|
||||
CheckCode::S104,
|
||||
CheckCode::S105,
|
||||
CheckCode::S106,
|
||||
CheckCode::S107,
|
||||
],
|
||||
CheckCodePrefix::S1 => vec![
|
||||
CheckCode::S101,
|
||||
CheckCode::S102,
|
||||
CheckCode::S104,
|
||||
CheckCode::S105,
|
||||
CheckCode::S106,
|
||||
CheckCode::S107,
|
||||
],
|
||||
CheckCodePrefix::S10 => vec![
|
||||
CheckCode::S101,
|
||||
CheckCode::S102,
|
||||
CheckCode::S104,
|
||||
CheckCode::S105,
|
||||
CheckCode::S106,
|
||||
CheckCode::S107,
|
||||
],
|
||||
CheckCodePrefix::S101 => vec![CheckCode::S101],
|
||||
CheckCodePrefix::S102 => vec![CheckCode::S102],
|
||||
CheckCodePrefix::S104 => vec![CheckCode::S104],
|
||||
CheckCodePrefix::S105 => vec![CheckCode::S105],
|
||||
CheckCodePrefix::S106 => vec![CheckCode::S106],
|
||||
CheckCodePrefix::S107 => vec![CheckCode::S107],
|
||||
CheckCodePrefix::T => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T2 => vec![CheckCode::T201, CheckCode::T203],
|
||||
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
|
||||
@@ -999,6 +1053,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::U009,
|
||||
CheckCode::U010,
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
],
|
||||
CheckCodePrefix::U0 => vec![
|
||||
CheckCode::U001,
|
||||
@@ -1012,6 +1067,7 @@ impl CheckCodePrefix {
|
||||
CheckCode::U009,
|
||||
CheckCode::U010,
|
||||
CheckCode::U011,
|
||||
CheckCode::U012,
|
||||
],
|
||||
CheckCodePrefix::U00 => vec![
|
||||
CheckCode::U001,
|
||||
@@ -1033,9 +1089,10 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::U007 => vec![CheckCode::U007],
|
||||
CheckCodePrefix::U008 => vec![CheckCode::U008],
|
||||
CheckCodePrefix::U009 => vec![CheckCode::U009],
|
||||
CheckCodePrefix::U01 => vec![CheckCode::U010, CheckCode::U011],
|
||||
CheckCodePrefix::U01 => vec![CheckCode::U010, CheckCode::U011, CheckCode::U012],
|
||||
CheckCodePrefix::U010 => vec![CheckCode::U010],
|
||||
CheckCodePrefix::U011 => vec![CheckCode::U011],
|
||||
CheckCodePrefix::U012 => vec![CheckCode::U012],
|
||||
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
|
||||
CheckCodePrefix::W2 => vec![CheckCode::W292],
|
||||
CheckCodePrefix::W29 => vec![CheckCode::W292],
|
||||
@@ -1128,13 +1185,16 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::B01 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::B010 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B011 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B012 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B013 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B014 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B015 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B016 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B017 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B018 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B019 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B02 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::B021 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::C => PrefixSpecificity::Category,
|
||||
@@ -1325,6 +1385,15 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::RUF001 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::RUF002 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::RUF003 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::S1 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::S10 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::S101 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S102 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S104 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S105 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S106 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::S107 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::T => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::T2 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::T20 => PrefixSpecificity::Tens,
|
||||
@@ -1345,6 +1414,7 @@ impl CheckCodePrefix {
|
||||
CheckCodePrefix::U01 => PrefixSpecificity::Tens,
|
||||
CheckCodePrefix::U010 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U011 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::U012 => PrefixSpecificity::Explicit,
|
||||
CheckCodePrefix::W => PrefixSpecificity::Category,
|
||||
CheckCodePrefix::W2 => PrefixSpecificity::Hundreds,
|
||||
CheckCodePrefix::W29 => PrefixSpecificity::Tens,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use num_bigint::BigInt;
|
||||
use rustpython_ast::{Cmpop, Constant, Expr, ExprKind, Located};
|
||||
|
||||
use crate::ast::helpers::match_name_or_attr_from_module;
|
||||
use crate::ast::helpers::match_module_member;
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckCode, CheckKind};
|
||||
|
||||
fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool {
|
||||
match_name_or_attr_from_module(expr, target, "sys", checker.from_imports.get("sys"))
|
||||
match_module_member(expr, &format!("sys.{target}"), &checker.from_imports)
|
||||
}
|
||||
|
||||
/// YTT101, YTT102, YTT301, YTT303
|
||||
@@ -181,9 +181,7 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
|
||||
|
||||
/// YTT202
|
||||
pub fn name_or_attribute(checker: &mut Checker, expr: &Expr) {
|
||||
if match_name_or_attr_from_module(expr, "PY3", "six", checker.from_imports.get("six"))
|
||||
&& checker.settings.enabled.contains(&CheckCode::YTT202)
|
||||
{
|
||||
if match_module_member(expr, "six.PY3", &checker.from_imports) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::SixPY3Referenced,
|
||||
Range::from_located(expr),
|
||||
|
||||
22
src/flake8_bandit/helpers.rs
Normal file
22
src/flake8_bandit/helpers.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind};
|
||||
|
||||
const PASSWORD_NAMES: [&str; 7] = [
|
||||
"password", "pass", "passwd", "pwd", "secret", "token", "secrete",
|
||||
];
|
||||
|
||||
pub fn string_literal(expr: &Expr) -> Option<&str> {
|
||||
match &expr.node {
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(string),
|
||||
..
|
||||
} => Some(string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe use regex for this?
|
||||
pub fn matches_password_name(string: &str) -> bool {
|
||||
PASSWORD_NAMES
|
||||
.iter()
|
||||
.any(|name| string.to_lowercase().contains(name))
|
||||
}
|
||||
2
src/flake8_bandit/mod.rs
Normal file
2
src/flake8_bandit/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod helpers;
|
||||
pub mod plugins;
|
||||
9
src/flake8_bandit/plugins/assert_used.rs
Normal file
9
src/flake8_bandit/plugins/assert_used.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use rustpython_ast::{Located, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// S101
|
||||
pub fn assert_used(stmt: &Located<StmtKind>) -> Check {
|
||||
Check::new(CheckKind::AssertUsed, Range::from_located(stmt))
|
||||
}
|
||||
14
src/flake8_bandit/plugins/exec_used.rs
Normal file
14
src/flake8_bandit/plugins/exec_used.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// S102
|
||||
pub fn exec_used(expr: &Expr, func: &Expr) -> Option<Check> {
|
||||
if let ExprKind::Name { id, .. } = &func.node {
|
||||
if id == "exec" {
|
||||
return Some(Check::new(CheckKind::ExecUsed, Range::from_located(expr)));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
11
src/flake8_bandit/plugins/hardcoded_bind_all_interfaces.rs
Normal file
11
src/flake8_bandit/plugins/hardcoded_bind_all_interfaces.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// S104
|
||||
pub fn hardcoded_bind_all_interfaces(value: &str, range: &Range) -> Option<Check> {
|
||||
if value == "0.0.0.0" {
|
||||
Some(Check::new(CheckKind::HardcodedBindAllInterfaces, *range))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
51
src/flake8_bandit/plugins/hardcoded_password_default.rs
Normal file
51
src/flake8_bandit/plugins/hardcoded_password_default.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use rustpython_ast::{ArgData, Arguments, Expr, Located};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::flake8_bandit::helpers::{matches_password_name, string_literal};
|
||||
|
||||
fn check_password_kwarg(arg: &Located<ArgData>, default: &Expr) -> Option<Check> {
|
||||
if let Some(string) = string_literal(default) {
|
||||
let kwarg_name = &arg.node.arg;
|
||||
if matches_password_name(kwarg_name) {
|
||||
return Some(Check::new(
|
||||
CheckKind::HardcodedPasswordDefault(string.to_string()),
|
||||
Range::from_located(default),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// S107
|
||||
pub fn hardcoded_password_default(arguments: &Arguments) -> Vec<Check> {
|
||||
let mut checks: Vec<Check> = Vec::new();
|
||||
|
||||
let defaults_start =
|
||||
arguments.posonlyargs.len() + arguments.args.len() - arguments.defaults.len();
|
||||
for (i, arg) in arguments
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(&arguments.args)
|
||||
.enumerate()
|
||||
{
|
||||
if let Some(i) = i.checked_sub(defaults_start) {
|
||||
let default = &arguments.defaults[i];
|
||||
if let Some(check) = check_password_kwarg(arg, default) {
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let defaults_start = arguments.kwonlyargs.len() - arguments.kw_defaults.len();
|
||||
for (i, kwarg) in arguments.kwonlyargs.iter().enumerate() {
|
||||
if let Some(i) = i.checked_sub(defaults_start) {
|
||||
let default = &arguments.kw_defaults[i];
|
||||
if let Some(check) = check_password_kwarg(kwarg, default) {
|
||||
checks.push(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checks
|
||||
}
|
||||
25
src/flake8_bandit/plugins/hardcoded_password_func_arg.rs
Normal file
25
src/flake8_bandit/plugins/hardcoded_password_func_arg.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use rustpython_ast::Keyword;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::flake8_bandit::helpers::{matches_password_name, string_literal};
|
||||
|
||||
/// S106
|
||||
pub fn hardcoded_password_func_arg(keywords: &[Keyword]) -> Vec<Check> {
|
||||
keywords
|
||||
.iter()
|
||||
.filter_map(|keyword| {
|
||||
if let Some(string) = string_literal(&keyword.node.value) {
|
||||
if let Some(arg) = &keyword.node.arg {
|
||||
if matches_password_name(arg) {
|
||||
return Some(Check::new(
|
||||
CheckKind::HardcodedPasswordFuncArg(string.to_string()),
|
||||
Range::from_located(keyword),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
58
src/flake8_bandit/plugins/hardcoded_password_string.rs
Normal file
58
src/flake8_bandit/plugins/hardcoded_password_string.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::flake8_bandit::helpers::{matches_password_name, string_literal};
|
||||
|
||||
fn is_password_target(target: &Expr) -> bool {
|
||||
let target_name = match &target.node {
|
||||
// variable = "s3cr3t"
|
||||
ExprKind::Name { id, .. } => id,
|
||||
// d["password"] = "s3cr3t"
|
||||
ExprKind::Subscript { slice, .. } => match &slice.node {
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(string),
|
||||
..
|
||||
} => string,
|
||||
_ => return false,
|
||||
},
|
||||
// obj.password = "s3cr3t"
|
||||
ExprKind::Attribute { attr, .. } => attr,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
matches_password_name(target_name)
|
||||
}
|
||||
|
||||
/// S105
|
||||
pub fn compare_to_hardcoded_password_string(left: &Expr, comparators: &[Expr]) -> Vec<Check> {
|
||||
comparators
|
||||
.iter()
|
||||
.filter_map(|comp| {
|
||||
if let Some(string) = string_literal(comp) {
|
||||
if is_password_target(left) {
|
||||
return Some(Check::new(
|
||||
CheckKind::HardcodedPasswordString(string.to_string()),
|
||||
Range::from_located(comp),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// S105
|
||||
pub fn assign_hardcoded_password_string(value: &Expr, targets: &Vec<Expr>) -> Option<Check> {
|
||||
if let Some(string) = string_literal(value) {
|
||||
for target in targets {
|
||||
if is_password_target(target) {
|
||||
return Some(Check::new(
|
||||
CheckKind::HardcodedPasswordString(string.to_string()),
|
||||
Range::from_located(value),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
15
src/flake8_bandit/plugins/mod.rs
Normal file
15
src/flake8_bandit/plugins/mod.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
pub use assert_used::assert_used;
|
||||
pub use exec_used::exec_used;
|
||||
pub use hardcoded_bind_all_interfaces::hardcoded_bind_all_interfaces;
|
||||
pub use hardcoded_password_default::hardcoded_password_default;
|
||||
pub use hardcoded_password_func_arg::hardcoded_password_func_arg;
|
||||
pub use hardcoded_password_string::{
|
||||
assign_hardcoded_password_string, compare_to_hardcoded_password_string,
|
||||
};
|
||||
|
||||
mod assert_used;
|
||||
mod exec_used;
|
||||
mod hardcoded_bind_all_interfaces;
|
||||
mod hardcoded_password_default;
|
||||
mod hardcoded_password_func_arg;
|
||||
mod hardcoded_password_string;
|
||||
@@ -1,2 +1,36 @@
|
||||
mod constants;
|
||||
pub mod plugins;
|
||||
pub mod settings;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::autofix::fixer;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::linter::test_path;
|
||||
use crate::{flake8_bugbear, Settings};
|
||||
|
||||
#[test]
|
||||
fn extend_immutable_calls() -> Result<()> {
|
||||
let snapshot = "extend_immutable_calls".to_string();
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures/B008_extended.py"),
|
||||
&Settings {
|
||||
flake8_bugbear: flake8_bugbear::settings::Settings {
|
||||
extend_immutable_calls: vec![
|
||||
"fastapi.Depends".to_string(),
|
||||
"fastapi.Query".to_string(),
|
||||
],
|
||||
},
|
||||
..Settings::for_rules(vec![CheckCode::B008])
|
||||
},
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
insta::assert_yaml_snapshot!(snapshot, checks);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
40
src/flake8_bugbear/plugins/cached_instance_method.rs
Normal file
40
src/flake8_bugbear/plugins/cached_instance_method.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
use crate::ast::helpers::{compose_call_path, match_module_member};
|
||||
use crate::ast::types::{Range, ScopeKind};
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
|
||||
match_module_member(expr, "functools.lru_cache", &checker.from_imports)
|
||||
|| match_module_member(expr, "functools.cache", &checker.from_imports)
|
||||
}
|
||||
|
||||
/// B019
|
||||
pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
|
||||
if matches!(checker.current_scope().kind, ScopeKind::Class(_)) {
|
||||
for decorator in decorator_list {
|
||||
// TODO(charlie): This should take into account `classmethod-decorators` and
|
||||
// `staticmethod-decorators`.
|
||||
if let Some(decorator_path) = compose_call_path(decorator) {
|
||||
if decorator_path == "classmethod" || decorator_path == "staticmethod" {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
for decorator in decorator_list {
|
||||
if is_cache_func(
|
||||
checker,
|
||||
match &decorator.node {
|
||||
ExprKind::Call { func, .. } => func,
|
||||
_ => decorator,
|
||||
},
|
||||
) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::CachedInstanceMethod,
|
||||
Range::from_located(decorator),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/flake8_bugbear/plugins/f_string_docstring.rs
Normal file
19
src/flake8_bugbear/plugins/f_string_docstring.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use rustpython_ast::{ExprKind, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// B021
|
||||
pub fn f_string_docstring(checker: &mut Checker, body: &[Stmt]) {
|
||||
if let Some(stmt) = body.first() {
|
||||
if let StmtKind::Expr { value } = &stmt.node {
|
||||
if let ExprKind::JoinedStr { .. } = value.node {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::FStringDocstring,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use rustpython_ast::{Arguments, Constant, Expr, ExprKind};
|
||||
|
||||
use crate::ast::helpers::compose_call_path;
|
||||
use crate::ast::helpers::{compose_call_path, match_call_path};
|
||||
use crate::ast::types::Range;
|
||||
use crate::ast::visitor;
|
||||
use crate::ast::visitor::Visitor;
|
||||
@@ -8,42 +9,50 @@ use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::flake8_bugbear::plugins::mutable_argument_default::is_mutable_func;
|
||||
|
||||
// TODO(charlie): Verify imports for each of the imported members.
|
||||
const IMMUTABLE_FUNCS: [&str; 11] = [
|
||||
const IMMUTABLE_FUNCS: [&str; 7] = [
|
||||
"tuple",
|
||||
"frozenset",
|
||||
"operator.attrgetter",
|
||||
"operator.itemgetter",
|
||||
"operator.methodcaller",
|
||||
"attrgetter",
|
||||
"itemgetter",
|
||||
"methodcaller",
|
||||
"types.MappingProxyType",
|
||||
"MappingProxyType",
|
||||
"re.compile",
|
||||
];
|
||||
|
||||
fn is_immutable_func(expr: &Expr) -> bool {
|
||||
compose_call_path(expr).map_or_else(|| false, |func| IMMUTABLE_FUNCS.contains(&func.as_str()))
|
||||
fn is_immutable_func(
|
||||
expr: &Expr,
|
||||
extend_immutable_calls: &[&str],
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
) -> bool {
|
||||
compose_call_path(expr)
|
||||
.map(|call_path| {
|
||||
IMMUTABLE_FUNCS
|
||||
.iter()
|
||||
.chain(extend_immutable_calls)
|
||||
.any(|target| match_call_path(&call_path, target, from_imports))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
struct ArgumentDefaultVisitor {
|
||||
struct ArgumentDefaultVisitor<'a> {
|
||||
checks: Vec<(CheckKind, Range)>,
|
||||
extend_immutable_calls: &'a [&'a str],
|
||||
from_imports: &'a FnvHashMap<&'a str, FnvHashSet<&'a str>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Visitor<'b> for ArgumentDefaultVisitor
|
||||
impl<'a, 'b> Visitor<'b> for ArgumentDefaultVisitor<'b>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
match &expr.node {
|
||||
ExprKind::Call { func, args, .. } => {
|
||||
if !is_mutable_func(func)
|
||||
&& !is_immutable_func(func)
|
||||
if !is_mutable_func(func, self.from_imports)
|
||||
&& !is_immutable_func(func, self.extend_immutable_calls, self.from_imports)
|
||||
&& !is_nan_or_infinity(func, args)
|
||||
{
|
||||
self.checks.push((
|
||||
CheckKind::FunctionCallArgumentDefault,
|
||||
CheckKind::FunctionCallArgumentDefault(compose_call_path(expr)),
|
||||
Range::from_located(expr),
|
||||
))
|
||||
}
|
||||
@@ -83,7 +92,18 @@ fn is_nan_or_infinity(expr: &Expr, args: &[Expr]) -> bool {
|
||||
|
||||
/// B008
|
||||
pub fn function_call_argument_default(checker: &mut Checker, arguments: &Arguments) {
|
||||
let mut visitor = ArgumentDefaultVisitor { checks: vec![] };
|
||||
let extend_immutable_cells: Vec<&str> = checker
|
||||
.settings
|
||||
.flake8_bugbear
|
||||
.extend_immutable_calls
|
||||
.iter()
|
||||
.map(|s| s.as_str())
|
||||
.collect();
|
||||
let mut visitor = ArgumentDefaultVisitor {
|
||||
checks: vec![],
|
||||
extend_immutable_calls: &extend_immutable_cells,
|
||||
from_imports: &checker.from_imports,
|
||||
};
|
||||
for expr in arguments
|
||||
.defaults
|
||||
.iter()
|
||||
|
||||
49
src/flake8_bugbear/plugins/jump_statement_in_finally.rs
Normal file
49
src/flake8_bugbear/plugins/jump_statement_in_finally.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use rustpython_ast::{Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
fn walk_stmt(checker: &mut Checker, body: &[Stmt], f: fn(&Stmt) -> bool) {
|
||||
for stmt in body {
|
||||
if f(stmt) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::JumpStatementInFinally(match &stmt.node {
|
||||
StmtKind::Break { .. } => "break".to_string(),
|
||||
StmtKind::Continue { .. } => "continue".to_string(),
|
||||
StmtKind::Return { .. } => "return".to_string(),
|
||||
_ => unreachable!(
|
||||
"Expected StmtKind::Break | StmtKind::Continue | StmtKind::Return"
|
||||
),
|
||||
}),
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
match &stmt.node {
|
||||
StmtKind::While { body, .. }
|
||||
| StmtKind::For { body, .. }
|
||||
| StmtKind::AsyncFor { body, .. } => {
|
||||
walk_stmt(checker, body, |stmt| {
|
||||
matches!(stmt.node, StmtKind::Return { .. })
|
||||
});
|
||||
}
|
||||
StmtKind::If { body, .. }
|
||||
| StmtKind::Try { body, .. }
|
||||
| StmtKind::With { body, .. }
|
||||
| StmtKind::AsyncWith { body, .. } => {
|
||||
walk_stmt(checker, body, f);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// B012
|
||||
pub fn jump_statement_in_finally(checker: &mut Checker, finalbody: &[Stmt]) {
|
||||
walk_stmt(checker, finalbody, |stmt| {
|
||||
matches!(
|
||||
stmt.node,
|
||||
StmtKind::Break | StmtKind::Continue | StmtKind::Return { .. }
|
||||
)
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
pub use assert_false::assert_false;
|
||||
pub use assert_raises_exception::assert_raises_exception;
|
||||
pub use assignment_to_os_environ::assignment_to_os_environ;
|
||||
pub use cached_instance_method::cached_instance_method;
|
||||
pub use cannot_raise_literal::cannot_raise_literal;
|
||||
pub use duplicate_exceptions::{duplicate_exceptions, duplicate_handler_exceptions};
|
||||
pub use f_string_docstring::f_string_docstring;
|
||||
pub use function_call_argument_default::function_call_argument_default;
|
||||
pub use getattr_with_constant::getattr_with_constant;
|
||||
pub use jump_statement_in_finally::jump_statement_in_finally;
|
||||
pub use mutable_argument_default::mutable_argument_default;
|
||||
pub use redundant_tuple_in_exception_handler::redundant_tuple_in_exception_handler;
|
||||
pub use setattr_with_constant::setattr_with_constant;
|
||||
@@ -19,10 +22,13 @@ pub use useless_expression::useless_expression;
|
||||
mod assert_false;
|
||||
mod assert_raises_exception;
|
||||
mod assignment_to_os_environ;
|
||||
mod cached_instance_method;
|
||||
mod cannot_raise_literal;
|
||||
mod duplicate_exceptions;
|
||||
mod f_string_docstring;
|
||||
mod function_call_argument_default;
|
||||
mod getattr_with_constant;
|
||||
mod jump_statement_in_finally;
|
||||
mod mutable_argument_default;
|
||||
mod redundant_tuple_in_exception_handler;
|
||||
mod setattr_with_constant;
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use rustpython_ast::{Arguments, Expr, ExprKind};
|
||||
|
||||
use crate::ast::helpers::{compose_call_path, match_call_path};
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
// TODO(charlie): Verify imports for each of the imported members.
|
||||
pub fn is_mutable_func(expr: &Expr) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Name { id, .. }
|
||||
if id == "dict"
|
||||
|| id == "list"
|
||||
|| id == "set"
|
||||
|| id == "Counter"
|
||||
|| id == "OrderedDict"
|
||||
|| id == "defaultdict"
|
||||
|| id == "deque" =>
|
||||
{
|
||||
true
|
||||
}
|
||||
ExprKind::Attribute { value, attr, .. }
|
||||
if (attr == "Counter"
|
||||
|| attr == "OrderedDict"
|
||||
|| attr == "defaultdict"
|
||||
|| attr == "deque") =>
|
||||
{
|
||||
matches!(&value.node, ExprKind::Name { id, .. } if id == "collections")
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
const MUTABLE_FUNCS: [&str; 7] = [
|
||||
"dict",
|
||||
"list",
|
||||
"set",
|
||||
"collections.Counter",
|
||||
"collections.OrderedDict",
|
||||
"collections.defaultdict",
|
||||
"collections.deque",
|
||||
];
|
||||
|
||||
pub fn is_mutable_func(expr: &Expr, from_imports: &FnvHashMap<&str, FnvHashSet<&str>>) -> bool {
|
||||
compose_call_path(expr)
|
||||
.map(|call_path| {
|
||||
MUTABLE_FUNCS
|
||||
.iter()
|
||||
.any(|target| match_call_path(&call_path, target, from_imports))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// B006
|
||||
@@ -50,7 +46,7 @@ pub fn mutable_argument_default(checker: &mut Checker, arguments: &Arguments) {
|
||||
));
|
||||
}
|
||||
ExprKind::Call { func, .. } => {
|
||||
if is_mutable_func(func) {
|
||||
if is_mutable_func(func, &checker.from_imports) {
|
||||
checker.add_check(Check::new(
|
||||
CheckKind::MutableArgumentDefault,
|
||||
Range::from_located(expr),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use rustpython_ast::{Expr, ExprKind, Stmt};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
@@ -12,7 +11,7 @@ use crate::checks::{Check, CheckKind};
|
||||
/// Identify all `ExprKind::Name` nodes in an AST.
|
||||
struct NameFinder<'a> {
|
||||
/// A map from identifier to defining expression.
|
||||
names: BTreeMap<&'a str, &'a Expr>,
|
||||
names: FnvHashMap<&'a str, &'a Expr>,
|
||||
}
|
||||
|
||||
impl NameFinder<'_> {
|
||||
|
||||
22
src/flake8_bugbear/settings.rs
Normal file
22
src/flake8_bugbear/settings.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
//! Settings for the `pep8-naming` plugin.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
pub struct Options {
|
||||
pub extend_immutable_calls: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, Default)]
|
||||
pub struct Settings {
|
||||
pub extend_immutable_calls: Vec<String>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn from_options(options: Options) -> Self {
|
||||
Self {
|
||||
extend_immutable_calls: options.extend_immutable_calls.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/flake8_bugbear/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: Depends
|
||||
location:
|
||||
row: 19
|
||||
column: 50
|
||||
end_location:
|
||||
row: 19
|
||||
column: 63
|
||||
fix: ~
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::check_ast::Checker;
|
||||
use crate::checks::CheckCode;
|
||||
use crate::flake8_print::checks;
|
||||
|
||||
/// T201, T203
|
||||
pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
||||
if let Some(mut check) = checks::print_call(
|
||||
expr,
|
||||
@@ -26,7 +27,6 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
||||
.iter()
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
|
||||
match helpers::remove_stmt(
|
||||
checker.parents[context.defined_by],
|
||||
context.defined_in.map(|index| checker.parents[index]),
|
||||
@@ -38,7 +38,7 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
|
||||
}
|
||||
check.amend(fix)
|
||||
}
|
||||
Err(e) => error!("Failed to fix unused imports: {}", e),
|
||||
Err(e) => error!("Failed to remove print call: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,6 @@ pub fn categorize(
|
||||
static STATIC_CLASSIFICATIONS: Lazy<BTreeMap<&'static str, ImportType>> = Lazy::new(|| {
|
||||
BTreeMap::from([
|
||||
("__future__", ImportType::Future),
|
||||
("__main__", ImportType::FirstParty),
|
||||
// Force `disutils` to be considered third-party.
|
||||
("disutils", ImportType::ThirdParty),
|
||||
// Relative imports (e.g., `from . import module`).
|
||||
("", ImportType::FirstParty),
|
||||
])
|
||||
|
||||
@@ -64,7 +64,7 @@ pub fn check_imports(
|
||||
// Generate the sorted import block.
|
||||
let expected = format_imports(
|
||||
body,
|
||||
&settings.line_length,
|
||||
&(settings.line_length - indentation.len()),
|
||||
&settings.src,
|
||||
&settings.isort.known_first_party,
|
||||
&settings.isort.known_third_party,
|
||||
|
||||
@@ -2,5 +2,21 @@
|
||||
source: src/isort/mod.rs
|
||||
expression: checks
|
||||
---
|
||||
[]
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 15
|
||||
column: 0
|
||||
fix:
|
||||
patch:
|
||||
content: " from line_with_88 import aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n from line_with_89 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_90 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_91 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_92 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n from line_with_93 import (\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n )\n"
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 15
|
||||
column: 0
|
||||
applied: false
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ mod directives;
|
||||
mod docstrings;
|
||||
mod flake8_2020;
|
||||
pub mod flake8_annotations;
|
||||
mod flake8_bugbear;
|
||||
pub mod flake8_bandit;
|
||||
pub mod flake8_bugbear;
|
||||
mod flake8_builtins;
|
||||
mod flake8_comprehensions;
|
||||
mod flake8_print;
|
||||
|
||||
@@ -336,12 +336,15 @@ mod tests {
|
||||
#[test_case(CheckCode::B009, Path::new("B009_B010.py"); "B009")]
|
||||
#[test_case(CheckCode::B010, Path::new("B009_B010.py"); "B010")]
|
||||
#[test_case(CheckCode::B011, Path::new("B011.py"); "B011")]
|
||||
#[test_case(CheckCode::B012, Path::new("B012.py"); "B012")]
|
||||
#[test_case(CheckCode::B013, Path::new("B013.py"); "B013")]
|
||||
#[test_case(CheckCode::B014, Path::new("B014.py"); "B014")]
|
||||
#[test_case(CheckCode::B015, Path::new("B015.py"); "B015")]
|
||||
#[test_case(CheckCode::B016, Path::new("B016.py"); "B016")]
|
||||
#[test_case(CheckCode::B017, Path::new("B017.py"); "B017")]
|
||||
#[test_case(CheckCode::B018, Path::new("B018.py"); "B018")]
|
||||
#[test_case(CheckCode::B019, Path::new("B019.py"); "B019")]
|
||||
#[test_case(CheckCode::B021, Path::new("B021.py"); "B021")]
|
||||
#[test_case(CheckCode::B025, Path::new("B025.py"); "B025")]
|
||||
#[test_case(CheckCode::B026, Path::new("B026.py"); "B026")]
|
||||
#[test_case(CheckCode::C400, Path::new("C400.py"); "C400")]
|
||||
@@ -468,6 +471,12 @@ mod tests {
|
||||
#[test_case(CheckCode::N816, Path::new("N816.py"); "N816")]
|
||||
#[test_case(CheckCode::N817, Path::new("N817.py"); "N817")]
|
||||
#[test_case(CheckCode::N818, Path::new("N818.py"); "N818")]
|
||||
#[test_case(CheckCode::S101, Path::new("S101.py"); "S101")]
|
||||
#[test_case(CheckCode::S102, Path::new("S102.py"); "S102")]
|
||||
#[test_case(CheckCode::S104, Path::new("S104.py"); "S104")]
|
||||
#[test_case(CheckCode::S105, Path::new("S105.py"); "S105")]
|
||||
#[test_case(CheckCode::S106, Path::new("S106.py"); "S106")]
|
||||
#[test_case(CheckCode::S107, Path::new("S107.py"); "S107")]
|
||||
#[test_case(CheckCode::T201, Path::new("T201.py"); "T201")]
|
||||
#[test_case(CheckCode::T203, Path::new("T203.py"); "T203")]
|
||||
#[test_case(CheckCode::U001, Path::new("U001.py"); "U001")]
|
||||
@@ -485,6 +494,7 @@ mod tests {
|
||||
#[test_case(CheckCode::U010, Path::new("U010.py"); "U010")]
|
||||
#[test_case(CheckCode::U011, Path::new("U011_0.py"); "U011_0")]
|
||||
#[test_case(CheckCode::U011, Path::new("U011_1.py"); "U011_1")]
|
||||
#[test_case(CheckCode::U012, Path::new("U012.py"); "U012")]
|
||||
#[test_case(CheckCode::W292, Path::new("W292_0.py"); "W292_0")]
|
||||
#[test_case(CheckCode::W292, Path::new("W292_1.py"); "W292_1")]
|
||||
#[test_case(CheckCode::W292, Path::new("W292_2.py"); "W292_2")]
|
||||
@@ -507,7 +517,7 @@ mod tests {
|
||||
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
|
||||
let mut checks = test_path(
|
||||
Path::new("./resources/test/fixtures").join(path).as_path(),
|
||||
&settings::Settings::for_rule(check_code.clone()),
|
||||
&settings::Settings::for_rule(check_code),
|
||||
&fixer::Mode::Generate,
|
||||
)?;
|
||||
checks.sort_by_key(|check| check.location);
|
||||
|
||||
@@ -83,7 +83,7 @@ mod tests {
|
||||
use crate::pep8_naming::helpers::{is_acronym, is_camelcase, is_mixed_case};
|
||||
|
||||
#[test]
|
||||
fn test_is_camelcase() -> () {
|
||||
fn test_is_camelcase() {
|
||||
assert!(is_camelcase("Camel"));
|
||||
assert!(is_camelcase("CamelCase"));
|
||||
assert!(!is_camelcase("camel"));
|
||||
@@ -93,7 +93,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_mixed_case() -> () {
|
||||
fn test_is_mixed_case() {
|
||||
assert!(is_mixed_case("mixedCase"));
|
||||
assert!(is_mixed_case("mixed_Case"));
|
||||
assert!(is_mixed_case("_mixed_Case"));
|
||||
@@ -104,7 +104,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_acronym() -> () {
|
||||
fn test_is_acronym() {
|
||||
assert!(is_acronym("AB", "AB"));
|
||||
assert!(is_acronym("AbcDef", "AD"));
|
||||
assert!(!is_acronym("AbcDef", "Ad"));
|
||||
|
||||
@@ -27,7 +27,7 @@ mod tests {
|
||||
use crate::python::string::{is_lower, is_upper};
|
||||
|
||||
#[test]
|
||||
fn test_is_lower() -> () {
|
||||
fn test_is_lower() {
|
||||
assert!(is_lower("abc"));
|
||||
assert!(is_lower("a_b_c"));
|
||||
assert!(is_lower("a2c"));
|
||||
@@ -38,7 +38,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_upper() -> () {
|
||||
fn test_is_upper() {
|
||||
assert!(is_upper("ABC"));
|
||||
assert!(is_upper("A_B_C"));
|
||||
assert!(is_upper("A2C"));
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use once_cell::sync::Lazy;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
// See: https://pypi.org/project/typing-extensions/
|
||||
static TYPING_EXTENSIONS: Lazy<BTreeSet<&'static str>> = Lazy::new(|| {
|
||||
BTreeSet::from([
|
||||
static TYPING_EXTENSIONS: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
|
||||
FnvHashSet::from_iter([
|
||||
"Annotated",
|
||||
"Any",
|
||||
"AsyncContextManager",
|
||||
@@ -64,9 +63,9 @@ pub fn in_extensions(name: &str) -> bool {
|
||||
}
|
||||
|
||||
// See: https://docs.python.org/3/library/typing.html
|
||||
static IMPORTED_SUBSCRIPTS: Lazy<BTreeMap<&'static str, BTreeSet<&'static str>>> =
|
||||
static IMPORTED_SUBSCRIPTS: Lazy<FnvHashMap<&'static str, FnvHashSet<&'static str>>> =
|
||||
Lazy::new(|| {
|
||||
let mut import_map = BTreeMap::new();
|
||||
let mut import_map = FnvHashMap::default();
|
||||
for (name, module) in [
|
||||
// `collections`
|
||||
("ChainMap", "collections"),
|
||||
@@ -183,7 +182,7 @@ static IMPORTED_SUBSCRIPTS: Lazy<BTreeMap<&'static str, BTreeSet<&'static str>>>
|
||||
] {
|
||||
import_map
|
||||
.entry(name)
|
||||
.or_insert_with(BTreeSet::new)
|
||||
.or_insert_with(FnvHashSet::default)
|
||||
.insert(module);
|
||||
}
|
||||
import_map
|
||||
@@ -191,13 +190,13 @@ static IMPORTED_SUBSCRIPTS: Lazy<BTreeMap<&'static str, BTreeSet<&'static str>>>
|
||||
|
||||
// These are all assumed to come from the `typing` module.
|
||||
// See: https://peps.python.org/pep-0585/
|
||||
static PEP_585_BUILTINS_ELIGIBLE: Lazy<BTreeSet<&'static str>> =
|
||||
Lazy::new(|| BTreeSet::from(["Dict", "FrozenSet", "List", "Set", "Tuple", "Type"]));
|
||||
static PEP_585_BUILTINS_ELIGIBLE: Lazy<FnvHashSet<&'static str>> =
|
||||
Lazy::new(|| FnvHashSet::from_iter(["Dict", "FrozenSet", "List", "Set", "Tuple", "Type"]));
|
||||
|
||||
// These are all assumed to come from the `typing` module.
|
||||
// See: https://peps.python.org/pep-0585/
|
||||
static PEP_585_BUILTINS: Lazy<BTreeSet<&'static str>> =
|
||||
Lazy::new(|| BTreeSet::from(["dict", "frozenset", "list", "set", "tuple", "type"]));
|
||||
static PEP_585_BUILTINS: Lazy<FnvHashSet<&'static str>> =
|
||||
Lazy::new(|| FnvHashSet::from_iter(["dict", "frozenset", "list", "set", "tuple", "type"]));
|
||||
|
||||
fn is_pep593_annotated_subscript(name: &str) -> bool {
|
||||
name == "Annotated"
|
||||
@@ -210,7 +209,7 @@ pub enum SubscriptKind {
|
||||
|
||||
pub fn match_annotated_subscript(
|
||||
expr: &Expr,
|
||||
imports: &BTreeMap<&str, BTreeSet<&str>>,
|
||||
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
) -> Option<SubscriptKind> {
|
||||
match &expr.node {
|
||||
ExprKind::Attribute { attr, value, .. } => {
|
||||
@@ -239,9 +238,9 @@ pub fn match_annotated_subscript(
|
||||
// Verify that, e.g., `Union` is a reference to `typing.Union`.
|
||||
if let Some(modules) = IMPORTED_SUBSCRIPTS.get(&id.as_str()) {
|
||||
for module in modules {
|
||||
if imports
|
||||
if from_imports
|
||||
.get(module)
|
||||
.map(|imports| imports.contains(&id.as_str()))
|
||||
.map(|imports| imports.contains(&id.as_str()) || imports.contains("*"))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return if is_pep593_annotated_subscript(id) {
|
||||
@@ -261,7 +260,7 @@ pub fn match_annotated_subscript(
|
||||
/// Returns `true` if `Expr` represents a reference to a typing object with a
|
||||
/// PEP 585 built-in. Note that none of the PEP 585 built-ins are in
|
||||
/// `typing_extensions`.
|
||||
pub fn is_pep585_builtin(expr: &Expr, typing_imports: Option<&BTreeSet<&str>>) -> bool {
|
||||
pub fn is_pep585_builtin(expr: &Expr, from_imports: &FnvHashMap<&str, FnvHashSet<&str>>) -> bool {
|
||||
match &expr.node {
|
||||
ExprKind::Attribute { attr, value, .. } => {
|
||||
if let ExprKind::Name { id, .. } = &value.node {
|
||||
@@ -271,8 +270,9 @@ pub fn is_pep585_builtin(expr: &Expr, typing_imports: Option<&BTreeSet<&str>>) -
|
||||
}
|
||||
}
|
||||
ExprKind::Name { id, .. } => {
|
||||
typing_imports
|
||||
.map(|imports| imports.contains(&id.as_str()))
|
||||
from_imports
|
||||
.get("typing")
|
||||
.map(|imports| imports.contains(&id.as_str()) || imports.contains("*"))
|
||||
.unwrap_or_default()
|
||||
&& PEP_585_BUILTINS_ELIGIBLE.contains(&id.as_str())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use rustpython_ast::{Constant, KeywordData};
|
||||
use rustpython_parser::ast::{ArgData, Expr, ExprKind, Stmt, StmtKind};
|
||||
|
||||
@@ -9,29 +8,6 @@ use crate::checks::{Check, CheckKind};
|
||||
use crate::pyupgrade::types::Primitive;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
pub const PY33_PLUS_REMOVE_FUTURES: &[&str] = &[
|
||||
"nested_scopes",
|
||||
"generators",
|
||||
"with_statement",
|
||||
"division",
|
||||
"absolute_import",
|
||||
"with_statement",
|
||||
"print_function",
|
||||
"unicode_literals",
|
||||
];
|
||||
|
||||
pub const PY37_PLUS_REMOVE_FUTURES: &[&str] = &[
|
||||
"nested_scopes",
|
||||
"generators",
|
||||
"with_statement",
|
||||
"division",
|
||||
"absolute_import",
|
||||
"with_statement",
|
||||
"print_function",
|
||||
"unicode_literals",
|
||||
"generator_stop",
|
||||
];
|
||||
|
||||
/// U008
|
||||
pub fn super_args(
|
||||
scope: &Scope,
|
||||
@@ -183,28 +159,11 @@ pub fn type_of_primitive(func: &Expr, args: &[Expr], location: Range) -> Option<
|
||||
None
|
||||
}
|
||||
|
||||
/// U010
|
||||
pub fn unnecessary_future_import(
|
||||
version: PythonVersion,
|
||||
name: &str,
|
||||
location: Range,
|
||||
) -> Option<Check> {
|
||||
if (version >= PythonVersion::Py33 && PY33_PLUS_REMOVE_FUTURES.contains(&name))
|
||||
|| (version >= PythonVersion::Py37 && PY37_PLUS_REMOVE_FUTURES.contains(&name))
|
||||
{
|
||||
return Some(Check::new(
|
||||
CheckKind::UnnecessaryFutureImport(name.to_string()),
|
||||
location,
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// U011
|
||||
pub fn unnecessary_lru_cache_params(
|
||||
decorator_list: &[Expr],
|
||||
target_version: PythonVersion,
|
||||
imports: Option<&BTreeSet<&str>>,
|
||||
imports: &FnvHashMap<&str, FnvHashSet<&str>>,
|
||||
) -> Option<Check> {
|
||||
for expr in decorator_list.iter() {
|
||||
if let ExprKind::Call {
|
||||
@@ -213,8 +172,7 @@ pub fn unnecessary_lru_cache_params(
|
||||
keywords,
|
||||
} = &expr.node
|
||||
{
|
||||
if args.is_empty()
|
||||
&& helpers::match_name_or_attr_from_module(func, "lru_cache", "functools", imports)
|
||||
if args.is_empty() && helpers::match_module_member(func, "functools.lru_cache", imports)
|
||||
{
|
||||
// Ex) `functools.lru_cache()`
|
||||
if keywords.is_empty() {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use libcst_native::{Codegen, Expression, SmallStatement, Statement};
|
||||
use rustpython_ast::{Expr, Keyword, Location};
|
||||
use anyhow::Result;
|
||||
use libcst_native::{Codegen, Expression, ImportNames, SmallStatement, Statement};
|
||||
use rustpython_ast::{Expr, Keyword, Location, Stmt};
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
|
||||
use crate::ast::helpers;
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::autofix::{self, Fix};
|
||||
use crate::cst::matchers::match_module;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
/// Generate a fix to remove a base from a ClassDef statement.
|
||||
@@ -41,7 +43,7 @@ pub fn remove_class_def_base(
|
||||
}
|
||||
|
||||
return match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Some(Fix::replacement("".to_string(), start, end)),
|
||||
(Some(start), Some(end)) => Some(Fix::deletion(start, end)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -133,6 +135,63 @@ pub fn remove_super_arguments(locator: &SourceCodeLocator, expr: &Expr) -> Optio
|
||||
None
|
||||
}
|
||||
|
||||
/// U010
|
||||
pub fn remove_unnecessary_future_import(
|
||||
locator: &SourceCodeLocator,
|
||||
removable: &[usize],
|
||||
stmt: &Stmt,
|
||||
parent: Option<&Stmt>,
|
||||
deleted: &[&Stmt],
|
||||
) -> Result<Fix> {
|
||||
// TODO(charlie): DRY up with pyflakes::fixes::remove_unused_import_froms.
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let mut tree = match_module(&module_text)?;
|
||||
|
||||
let body = if let Some(Statement::Simple(body)) = tree.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Statement::Simple"));
|
||||
};
|
||||
let body = if let Some(SmallStatement::ImportFrom(body)) = body.body.first_mut() {
|
||||
body
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Expected node to be: SmallStatement::ImportFrom"
|
||||
));
|
||||
};
|
||||
|
||||
let aliases = if let ImportNames::Aliases(aliases) = &mut body.names {
|
||||
aliases
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("Expected node to be: Aliases"));
|
||||
};
|
||||
|
||||
// Preserve the trailing comma (or not) from the last entry.
|
||||
let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone());
|
||||
|
||||
// TODO(charlie): This is quadratic.
|
||||
for index in removable.iter().rev() {
|
||||
aliases.remove(*index);
|
||||
}
|
||||
|
||||
if let Some(alias) = aliases.last_mut() {
|
||||
alias.comma = trailing_comma;
|
||||
}
|
||||
|
||||
if aliases.is_empty() {
|
||||
autofix::helpers::remove_stmt(stmt, parent, deleted)
|
||||
} else {
|
||||
let mut state = Default::default();
|
||||
tree.codegen(&mut state);
|
||||
|
||||
Ok(Fix::replacement(
|
||||
state.to_string(),
|
||||
stmt.location,
|
||||
stmt.end_location.unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// U011
|
||||
pub fn remove_unnecessary_lru_cache_params(
|
||||
locator: &SourceCodeLocator,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustpython_ast::{Expr, ExprKind};
|
||||
|
||||
@@ -8,8 +7,8 @@ use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
static DEPRECATED_ALIASES: Lazy<BTreeMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
BTreeMap::from([
|
||||
static DEPRECATED_ALIASES: Lazy<FnvHashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
FnvHashMap::from_iter([
|
||||
("failUnlessEqual", "assertEqual"),
|
||||
("assertEquals", "assertEqual"),
|
||||
("failIfEqual", "assertNotEqual"),
|
||||
@@ -28,6 +27,7 @@ static DEPRECATED_ALIASES: Lazy<BTreeMap<&'static str, &'static str>> = Lazy::ne
|
||||
])
|
||||
});
|
||||
|
||||
/// U005
|
||||
pub fn deprecated_unittest_alias(checker: &mut Checker, expr: &Expr) {
|
||||
if let ExprKind::Attribute { value, attr, .. } = &expr.node {
|
||||
if let Some(target) = DEPRECATED_ALIASES.get(attr.as_str()) {
|
||||
|
||||
@@ -2,6 +2,7 @@ pub use deprecated_unittest_alias::deprecated_unittest_alias;
|
||||
pub use super_call_with_parameters::super_call_with_parameters;
|
||||
pub use type_of_primitive::type_of_primitive;
|
||||
pub use unnecessary_abspath::unnecessary_abspath;
|
||||
pub use unnecessary_encode_utf8::unnecessary_encode_utf8;
|
||||
pub use unnecessary_future_import::unnecessary_future_import;
|
||||
pub use unnecessary_lru_cache_params::unnecessary_lru_cache_params;
|
||||
pub use use_pep585_annotation::use_pep585_annotation;
|
||||
@@ -13,6 +14,7 @@ mod deprecated_unittest_alias;
|
||||
mod super_call_with_parameters;
|
||||
mod type_of_primitive;
|
||||
mod unnecessary_abspath;
|
||||
mod unnecessary_encode_utf8;
|
||||
mod unnecessary_future_import;
|
||||
mod unnecessary_lru_cache_params;
|
||||
mod use_pep585_annotation;
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::check_ast::Checker;
|
||||
use crate::pyupgrade;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
/// U008
|
||||
pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
// Only bother going through the super check at all if we're in a `super` call.
|
||||
// (We check this in `check_super_args` too, so this is just an optimization.)
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::check_ast::Checker;
|
||||
use crate::checks::CheckKind;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
/// U003
|
||||
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let Some(mut check) = checks::type_of_primitive(func, args, Range::from_located(expr)) {
|
||||
if checker.patch() {
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
/// U002
|
||||
pub fn unnecessary_abspath(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
|
||||
if let Some(mut check) = checks::unnecessary_abspath(func, args, Range::from_located(expr)) {
|
||||
if checker.patch() {
|
||||
|
||||
152
src/pyupgrade/plugins/unnecessary_encode_utf8.rs
Normal file
152
src/pyupgrade/plugins/unnecessary_encode_utf8.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use rustpython_ast::{Constant, Expr, ExprKind, Keyword};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
const UTF8_LITERALS: &[&str] = &["utf-8", "utf8", "utf_8", "u8", "utf", "cp65001"];
|
||||
|
||||
fn match_encoded_variable(func: &Expr) -> Option<&Expr> {
|
||||
if let ExprKind::Attribute {
|
||||
value: variable,
|
||||
attr,
|
||||
..
|
||||
} = &func.node
|
||||
{
|
||||
if attr == "encode" {
|
||||
return Some(variable);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn is_utf8_encoding_arg(arg: &Expr) -> bool {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
} = &arg.node
|
||||
{
|
||||
UTF8_LITERALS.contains(&value.to_lowercase().as_str())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_default_encode(args: &Vec<Expr>, kwargs: &Vec<Keyword>) -> bool {
|
||||
match (args.len(), kwargs.len()) {
|
||||
// .encode()
|
||||
(0, 0) => true,
|
||||
// .encode(encoding)
|
||||
(1, 0) => is_utf8_encoding_arg(&args[0]),
|
||||
// .encode(kwarg=kwarg)
|
||||
(0, 1) => {
|
||||
kwargs[0].node.arg == Some("encoding".to_string())
|
||||
&& is_utf8_encoding_arg(&kwargs[0].node.value)
|
||||
}
|
||||
// .encode(*args, **kwargs)
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Return a Fix for a default `encode` call removing the encoding argument,
|
||||
// keyword, or positional.
|
||||
fn delete_default_encode_arg_or_kwarg(
|
||||
expr: &Expr,
|
||||
args: &[Expr],
|
||||
kwargs: &[Keyword],
|
||||
patch: bool,
|
||||
) -> Option<Check> {
|
||||
if let Some(arg) = args.get(0) {
|
||||
let mut check = Check::new(CheckKind::UnnecessaryEncodeUTF8, Range::from_located(expr));
|
||||
if patch {
|
||||
check.amend(Fix::deletion(arg.location, arg.end_location.unwrap()));
|
||||
}
|
||||
Some(check)
|
||||
} else if let Some(kwarg) = kwargs.get(0) {
|
||||
let mut check = Check::new(CheckKind::UnnecessaryEncodeUTF8, Range::from_located(expr));
|
||||
if patch {
|
||||
check.amend(Fix::deletion(kwarg.location, kwarg.end_location.unwrap()));
|
||||
}
|
||||
Some(check)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Return a Fix replacing the call to encode by a `"b"` prefix on the string.
|
||||
fn replace_with_bytes_literal(
|
||||
expr: &Expr,
|
||||
constant: &Expr,
|
||||
locator: &SourceCodeLocator,
|
||||
patch: bool,
|
||||
) -> Check {
|
||||
let mut check = Check::new(CheckKind::UnnecessaryEncodeUTF8, Range::from_located(expr));
|
||||
if patch {
|
||||
let content = locator.slice_source_code_range(&Range {
|
||||
location: constant.location,
|
||||
end_location: constant.end_location.unwrap(),
|
||||
});
|
||||
let content = format!(
|
||||
"b{}",
|
||||
content.trim_start_matches('u').trim_start_matches('U')
|
||||
);
|
||||
check.amend(Fix::replacement(
|
||||
content,
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
))
|
||||
}
|
||||
check
|
||||
}
|
||||
|
||||
/// U012
|
||||
pub fn unnecessary_encode_utf8(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
args: &Vec<Expr>,
|
||||
kwargs: &Vec<Keyword>,
|
||||
) {
|
||||
if let Some(variable) = match_encoded_variable(func) {
|
||||
match &variable.node {
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str(literal),
|
||||
..
|
||||
} => {
|
||||
// "str".encode()
|
||||
// "str".encode("utf-8")
|
||||
if is_default_encode(args, kwargs) {
|
||||
if literal.is_ascii() {
|
||||
// "foo".encode()
|
||||
checker.add_check(replace_with_bytes_literal(
|
||||
expr,
|
||||
variable,
|
||||
checker.locator,
|
||||
checker.patch(),
|
||||
));
|
||||
} else {
|
||||
// "unicode text©".encode("utf-8")
|
||||
if let Some(check) =
|
||||
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
|
||||
{
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// f"foo{bar}".encode(*args, **kwargs)
|
||||
ExprKind::JoinedStr { .. } => {
|
||||
if is_default_encode(args, kwargs) {
|
||||
if let Some(check) =
|
||||
delete_default_encode_arg_or_kwarg(expr, args, kwargs, checker.patch())
|
||||
{
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,84 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use log::error;
|
||||
use rustpython_ast::{AliasData, Located};
|
||||
use rustpython_parser::ast::Stmt;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyupgrade::checks;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
use crate::pyupgrade::fixes;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, name: &str) {
|
||||
if let Some(check) = checks::unnecessary_future_import(
|
||||
checker.settings.target_version,
|
||||
name,
|
||||
Range::from_located(stmt),
|
||||
) {
|
||||
const PY33_PLUS_REMOVE_FUTURES: &[&str] = &[
|
||||
"nested_scopes",
|
||||
"generators",
|
||||
"with_statement",
|
||||
"division",
|
||||
"absolute_import",
|
||||
"with_statement",
|
||||
"print_function",
|
||||
"unicode_literals",
|
||||
];
|
||||
|
||||
const PY37_PLUS_REMOVE_FUTURES: &[&str] = &[
|
||||
"nested_scopes",
|
||||
"generators",
|
||||
"with_statement",
|
||||
"division",
|
||||
"absolute_import",
|
||||
"with_statement",
|
||||
"print_function",
|
||||
"unicode_literals",
|
||||
"generator_stop",
|
||||
];
|
||||
|
||||
/// U010
|
||||
pub fn unnecessary_future_import(checker: &mut Checker, stmt: &Stmt, names: &[Located<AliasData>]) {
|
||||
let target_version = checker.settings.target_version;
|
||||
|
||||
let mut removable_index: Vec<usize> = vec![];
|
||||
let mut removable_names: BTreeSet<&str> = BTreeSet::new();
|
||||
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))
|
||||
|| (target_version >= PythonVersion::Py37 && PY37_PLUS_REMOVE_FUTURES.contains(&name))
|
||||
{
|
||||
removable_index.push(index);
|
||||
removable_names.insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
if !removable_index.is_empty() {
|
||||
let mut check = Check::new(
|
||||
CheckKind::UnnecessaryFutureImport(
|
||||
removable_names.into_iter().map(String::from).collect(),
|
||||
),
|
||||
Range::from_located(stmt),
|
||||
);
|
||||
if checker.patch() {
|
||||
let context = checker.binding_context();
|
||||
let deleted: Vec<&Stmt> = checker
|
||||
.deletions
|
||||
.iter()
|
||||
.map(|index| checker.parents[*index])
|
||||
.collect();
|
||||
match fixes::remove_unnecessary_future_import(
|
||||
checker.locator,
|
||||
&removable_index,
|
||||
checker.parents[context.defined_by],
|
||||
context.defined_in.map(|index| checker.parents[index]),
|
||||
&deleted,
|
||||
) {
|
||||
Ok(fix) => {
|
||||
if fix.patch.content.is_empty() || fix.patch.content == "pass" {
|
||||
checker.deletions.insert(context.defined_by);
|
||||
}
|
||||
check.amend(fix);
|
||||
}
|
||||
Err(e) => error!("Failed to remove __future__ import: {}", e),
|
||||
}
|
||||
}
|
||||
checker.add_check(check);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ use rustpython_parser::ast::Expr;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyupgrade::{checks, fixes};
|
||||
|
||||
/// U011
|
||||
pub fn unnecessary_lru_cache_params(checker: &mut Checker, decorator_list: &[Expr]) {
|
||||
if let Some(mut check) = checks::unnecessary_lru_cache_params(
|
||||
decorator_list,
|
||||
checker.settings.target_version,
|
||||
checker.from_imports.get("functools"),
|
||||
&checker.from_imports,
|
||||
) {
|
||||
if checker.patch() {
|
||||
if let Some(fix) =
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::autofix::Fix;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::checks::{Check, CheckKind};
|
||||
|
||||
/// U006
|
||||
pub fn use_pep585_annotation(checker: &mut Checker, expr: &Expr, id: &str) {
|
||||
let mut check = Check::new(
|
||||
CheckKind::UsePEP585Annotation(id.to_string()),
|
||||
|
||||
@@ -41,6 +41,7 @@ fn union(elts: &[Expr]) -> Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/// U007
|
||||
pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, slice: &Expr) {
|
||||
if checker.match_typing_module(value, "Optional") {
|
||||
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::autofix::helpers;
|
||||
use crate::check_ast::Checker;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
/// U001
|
||||
pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr, targets: &[Expr]) {
|
||||
if let Some(mut check) =
|
||||
checks::useless_metaclass_type(targets, value, Range::from_located(stmt))
|
||||
@@ -29,7 +30,7 @@ pub fn useless_metaclass_type(checker: &mut Checker, stmt: &Stmt, value: &Expr,
|
||||
}
|
||||
check.amend(fix)
|
||||
}
|
||||
Err(e) => error!("Failed to fix unused imports: {}", e),
|
||||
Err(e) => error!("Failed to fix remove metaclass type: {}", e),
|
||||
}
|
||||
}
|
||||
checker.add_check(check);
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::check_ast::Checker;
|
||||
use crate::pyupgrade;
|
||||
use crate::pyupgrade::checks;
|
||||
|
||||
/// U004
|
||||
pub fn useless_object_inheritance(
|
||||
checker: &mut Checker,
|
||||
stmt: &Stmt,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustpython_ast::Location;
|
||||
|
||||
@@ -10,8 +9,8 @@ use crate::source_code_locator::SourceCodeLocator;
|
||||
use crate::Check;
|
||||
|
||||
/// See: https://github.com/microsoft/vscode/blob/095ddabc52b82498ee7f718a34f9dd11d59099a8/src/vs/base/common/strings.ts#L1094
|
||||
static CONFUSABLES: Lazy<BTreeMap<u32, u32>> = Lazy::new(|| {
|
||||
BTreeMap::from([
|
||||
static CONFUSABLES: Lazy<FnvHashMap<u32, u32>> = Lazy::new(|| {
|
||||
FnvHashMap::from_iter([
|
||||
(8232, 32),
|
||||
(8233, 32),
|
||||
(5760, 32),
|
||||
|
||||
@@ -12,7 +12,7 @@ use regex::Regex;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::settings::pyproject::load_options;
|
||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||
use crate::{flake8_annotations, flake8_quotes, fs, isort, pep8_naming};
|
||||
use crate::{flake8_annotations, flake8_bugbear, flake8_quotes, fs, isort, pep8_naming};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Configuration {
|
||||
@@ -30,6 +30,7 @@ pub struct Configuration {
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub isort: isort::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
@@ -133,6 +134,10 @@ impl Configuration {
|
||||
.flake8_annotations
|
||||
.map(flake8_annotations::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
flake8_bugbear: options
|
||||
.flake8_bugbear
|
||||
.map(flake8_bugbear::settings::Settings::from_options)
|
||||
.unwrap_or_default(),
|
||||
flake8_quotes: options
|
||||
.flake8_quotes
|
||||
.map(flake8_quotes::settings::Settings::from_options)
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
//! 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::PathBuf;
|
||||
|
||||
use fnv::FnvHashSet;
|
||||
use path_absolutize::path_dedot;
|
||||
use regex::Regex;
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::checks::CheckCode;
|
||||
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
|
||||
use crate::settings::configuration::Configuration;
|
||||
use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion};
|
||||
use crate::{flake8_annotations, flake8_quotes, isort, pep8_naming};
|
||||
use crate::{flake8_annotations, flake8_bugbear, flake8_quotes, isort, pep8_naming};
|
||||
|
||||
pub mod configuration;
|
||||
pub mod options;
|
||||
@@ -24,7 +24,7 @@ pub mod user;
|
||||
#[derive(Debug)]
|
||||
pub struct Settings {
|
||||
pub dummy_variable_rgx: Regex,
|
||||
pub enabled: BTreeSet<CheckCode>,
|
||||
pub enabled: FnvHashSet<CheckCode>,
|
||||
pub exclude: Vec<FilePattern>,
|
||||
pub extend_exclude: Vec<FilePattern>,
|
||||
pub line_length: usize,
|
||||
@@ -33,6 +33,7 @@ pub struct Settings {
|
||||
pub target_version: PythonVersion,
|
||||
// Plugins
|
||||
pub flake8_annotations: flake8_annotations::settings::Settings,
|
||||
pub flake8_bugbear: flake8_bugbear::settings::Settings,
|
||||
pub flake8_quotes: flake8_quotes::settings::Settings,
|
||||
pub isort: isort::settings::Settings,
|
||||
pub pep8_naming: pep8_naming::settings::Settings,
|
||||
@@ -51,6 +52,7 @@ impl Settings {
|
||||
exclude: config.exclude,
|
||||
extend_exclude: config.extend_exclude,
|
||||
flake8_annotations: config.flake8_annotations,
|
||||
flake8_bugbear: config.flake8_bugbear,
|
||||
flake8_quotes: config.flake8_quotes,
|
||||
isort: config.isort,
|
||||
line_length: config.line_length,
|
||||
@@ -64,7 +66,7 @@ impl Settings {
|
||||
pub fn for_rule(check_code: CheckCode) -> Self {
|
||||
Self {
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: BTreeSet::from([check_code]),
|
||||
enabled: FnvHashSet::from_iter([check_code]),
|
||||
exclude: Default::default(),
|
||||
extend_exclude: Default::default(),
|
||||
line_length: 88,
|
||||
@@ -72,6 +74,7 @@ impl Settings {
|
||||
src: vec![path_dedot::CWD.clone()],
|
||||
target_version: PythonVersion::Py310,
|
||||
flake8_annotations: Default::default(),
|
||||
flake8_bugbear: Default::default(),
|
||||
flake8_quotes: Default::default(),
|
||||
isort: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
@@ -81,7 +84,7 @@ impl Settings {
|
||||
pub fn for_rules(check_codes: Vec<CheckCode>) -> Self {
|
||||
Self {
|
||||
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
|
||||
enabled: BTreeSet::from_iter(check_codes),
|
||||
enabled: FnvHashSet::from_iter(check_codes),
|
||||
exclude: Default::default(),
|
||||
extend_exclude: Default::default(),
|
||||
line_length: 88,
|
||||
@@ -89,6 +92,7 @@ impl Settings {
|
||||
src: vec![path_dedot::CWD.clone()],
|
||||
target_version: PythonVersion::Py310,
|
||||
flake8_annotations: Default::default(),
|
||||
flake8_bugbear: Default::default(),
|
||||
flake8_quotes: Default::default(),
|
||||
isort: Default::default(),
|
||||
pep8_naming: Default::default(),
|
||||
@@ -123,8 +127,8 @@ fn resolve_codes(
|
||||
extend_select: &[CheckCodePrefix],
|
||||
ignore: &[CheckCodePrefix],
|
||||
extend_ignore: &[CheckCodePrefix],
|
||||
) -> BTreeSet<CheckCode> {
|
||||
let mut codes: BTreeSet<CheckCode> = BTreeSet::new();
|
||||
) -> FnvHashSet<CheckCode> {
|
||||
let mut codes: FnvHashSet<CheckCode> = FnvHashSet::default();
|
||||
for specificity in [
|
||||
PrefixSpecificity::Category,
|
||||
PrefixSpecificity::Hundreds,
|
||||
@@ -161,7 +165,7 @@ fn resolve_codes(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
use fnv::FnvHashSet;
|
||||
|
||||
use crate::checks::CheckCode;
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
@@ -170,19 +174,19 @@ mod tests {
|
||||
#[test]
|
||||
fn resolver() {
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[], &[], &[]);
|
||||
let expected = BTreeSet::from_iter([CheckCode::W292, CheckCode::W605]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W292, CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W6], &[], &[], &[]);
|
||||
let expected = BTreeSet::from_iter([CheckCode::W605]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W], &[], &[CheckCodePrefix::W292], &[]);
|
||||
let expected = BTreeSet::from_iter([CheckCode::W605]);
|
||||
let expected = FnvHashSet::from_iter([CheckCode::W605]);
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = resolve_codes(&[CheckCodePrefix::W605], &[], &[CheckCodePrefix::W605], &[]);
|
||||
let expected = BTreeSet::from_iter([]);
|
||||
let expected = FnvHashSet::from_iter([]);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::checks_gen::CheckCodePrefix;
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::{flake8_annotations, flake8_quotes, isort, pep8_naming};
|
||||
use crate::{flake8_annotations, flake8_bugbear, flake8_quotes, isort, pep8_naming};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||
@@ -19,13 +19,15 @@ pub struct Options {
|
||||
pub fix: Option<bool>,
|
||||
pub ignore: Option<Vec<CheckCodePrefix>>,
|
||||
pub line_length: Option<usize>,
|
||||
pub per_file_ignores: Option<BTreeMap<String, Vec<CheckCodePrefix>>>,
|
||||
pub select: Option<Vec<CheckCodePrefix>>,
|
||||
pub src: Option<Vec<String>>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
// Plugins
|
||||
pub flake8_annotations: Option<flake8_annotations::settings::Options>,
|
||||
pub flake8_bugbear: Option<flake8_bugbear::settings::Options>,
|
||||
pub flake8_quotes: Option<flake8_quotes::settings::Options>,
|
||||
pub isort: Option<isort::settings::Options>,
|
||||
pub pep8_naming: Option<pep8_naming::settings::Options>,
|
||||
// Tables are required to go last.
|
||||
pub per_file_ignores: Option<BTreeMap<String, Vec<CheckCodePrefix>>>,
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ mod tests {
|
||||
find_project_root, find_pyproject_toml, parse_pyproject_toml, Options, Pyproject, Tools,
|
||||
};
|
||||
use crate::settings::types::PatternPrefixPair;
|
||||
use crate::{flake8_quotes, pep8_naming};
|
||||
use crate::{flake8_bugbear, flake8_quotes, pep8_naming};
|
||||
|
||||
#[test]
|
||||
fn deserialize() -> Result<()> {
|
||||
@@ -146,6 +146,7 @@ mod tests {
|
||||
src: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -177,6 +178,7 @@ line-length = 79
|
||||
src: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -208,6 +210,7 @@ exclude = ["foo.py"]
|
||||
src: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -239,6 +242,7 @@ select = ["E501"]
|
||||
src: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -271,6 +275,7 @@ ignore = ["E501"]
|
||||
src: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_quotes: None,
|
||||
isort: None,
|
||||
pep8_naming: None,
|
||||
@@ -349,6 +354,12 @@ other-attribute = 1
|
||||
src: None,
|
||||
target_version: None,
|
||||
flake8_annotations: None,
|
||||
flake8_bugbear: Some(flake8_bugbear::settings::Options {
|
||||
extend_immutable_calls: Some(vec![
|
||||
"fastapi.Depends".to_string(),
|
||||
"fastapi.Query".to_string(),
|
||||
]),
|
||||
}),
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
inline_quotes: Some(Quote::Single),
|
||||
multiline_quotes: Some(Quote::Double),
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: range
|
||||
location:
|
||||
row: 85
|
||||
column: 60
|
||||
@@ -10,7 +11,8 @@ expression: checks
|
||||
row: 85
|
||||
column: 68
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: range
|
||||
location:
|
||||
row: 89
|
||||
column: 63
|
||||
@@ -18,7 +20,8 @@ expression: checks
|
||||
row: 89
|
||||
column: 71
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: range
|
||||
location:
|
||||
row: 93
|
||||
column: 59
|
||||
@@ -26,7 +29,8 @@ expression: checks
|
||||
row: 93
|
||||
column: 67
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: time.time
|
||||
location:
|
||||
row: 109
|
||||
column: 38
|
||||
@@ -34,7 +38,8 @@ expression: checks
|
||||
row: 109
|
||||
column: 49
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: dt.datetime.now
|
||||
location:
|
||||
row: 113
|
||||
column: 11
|
||||
@@ -42,7 +47,8 @@ expression: checks
|
||||
row: 113
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: dt.timedelta
|
||||
location:
|
||||
row: 113
|
||||
column: 31
|
||||
@@ -50,7 +56,8 @@ expression: checks
|
||||
row: 113
|
||||
column: 51
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: ~
|
||||
location:
|
||||
row: 117
|
||||
column: 29
|
||||
@@ -58,7 +65,8 @@ expression: checks
|
||||
row: 117
|
||||
column: 44
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: float
|
||||
location:
|
||||
row: 155
|
||||
column: 33
|
||||
@@ -66,7 +74,8 @@ expression: checks
|
||||
row: 155
|
||||
column: 47
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: float
|
||||
location:
|
||||
row: 160
|
||||
column: 29
|
||||
@@ -74,7 +83,8 @@ expression: checks
|
||||
row: 160
|
||||
column: 37
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: float
|
||||
location:
|
||||
row: 164
|
||||
column: 44
|
||||
@@ -82,7 +92,8 @@ expression: checks
|
||||
row: 164
|
||||
column: 57
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: float
|
||||
location:
|
||||
row: 170
|
||||
column: 20
|
||||
@@ -90,7 +101,8 @@ expression: checks
|
||||
row: 170
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: dt.datetime.now
|
||||
location:
|
||||
row: 170
|
||||
column: 30
|
||||
@@ -98,7 +110,8 @@ expression: checks
|
||||
row: 170
|
||||
column: 47
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: map
|
||||
location:
|
||||
row: 176
|
||||
column: 21
|
||||
@@ -106,7 +119,8 @@ expression: checks
|
||||
row: 176
|
||||
column: 62
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: random.randint
|
||||
location:
|
||||
row: 181
|
||||
column: 18
|
||||
@@ -114,7 +128,8 @@ expression: checks
|
||||
row: 181
|
||||
column: 59
|
||||
fix: ~
|
||||
- kind: FunctionCallArgumentDefault
|
||||
- kind:
|
||||
FunctionCallArgumentDefault: dt.datetime.now
|
||||
location:
|
||||
row: 181
|
||||
column: 36
|
||||
|
||||
95
src/snapshots/ruff__linter__tests__B012_B012.py.snap
Normal file
95
src/snapshots/ruff__linter__tests__B012_B012.py.snap
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
JumpStatementInFinally: return
|
||||
location:
|
||||
row: 5
|
||||
column: 8
|
||||
end_location:
|
||||
row: 5
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: return
|
||||
location:
|
||||
row: 13
|
||||
column: 12
|
||||
end_location:
|
||||
row: 13
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: return
|
||||
location:
|
||||
row: 21
|
||||
column: 12
|
||||
end_location:
|
||||
row: 21
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: return
|
||||
location:
|
||||
row: 31
|
||||
column: 12
|
||||
end_location:
|
||||
row: 31
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: return
|
||||
location:
|
||||
row: 44
|
||||
column: 20
|
||||
end_location:
|
||||
row: 44
|
||||
column: 26
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: break
|
||||
location:
|
||||
row: 66
|
||||
column: 12
|
||||
end_location:
|
||||
row: 66
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: continue
|
||||
location:
|
||||
row: 78
|
||||
column: 12
|
||||
end_location:
|
||||
row: 78
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: return
|
||||
location:
|
||||
row: 94
|
||||
column: 12
|
||||
end_location:
|
||||
row: 94
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: continue
|
||||
location:
|
||||
row: 101
|
||||
column: 8
|
||||
end_location:
|
||||
row: 101
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
JumpStatementInFinally: break
|
||||
location:
|
||||
row: 107
|
||||
column: 8
|
||||
end_location:
|
||||
row: 107
|
||||
column: 13
|
||||
fix: ~
|
||||
|
||||
69
src/snapshots/ruff__linter__tests__B019_B019.py.snap
Normal file
69
src/snapshots/ruff__linter__tests__B019_B019.py.snap
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 78
|
||||
column: 5
|
||||
end_location:
|
||||
row: 78
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 82
|
||||
column: 5
|
||||
end_location:
|
||||
row: 82
|
||||
column: 10
|
||||
fix: ~
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 86
|
||||
column: 5
|
||||
end_location:
|
||||
row: 86
|
||||
column: 22
|
||||
fix: ~
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 90
|
||||
column: 5
|
||||
end_location:
|
||||
row: 90
|
||||
column: 12
|
||||
fix: ~
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 94
|
||||
column: 5
|
||||
end_location:
|
||||
row: 94
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 98
|
||||
column: 5
|
||||
end_location:
|
||||
row: 98
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 102
|
||||
column: 5
|
||||
end_location:
|
||||
row: 102
|
||||
column: 26
|
||||
fix: ~
|
||||
- kind: CachedInstanceMethod
|
||||
location:
|
||||
row: 106
|
||||
column: 5
|
||||
end_location:
|
||||
row: 106
|
||||
column: 16
|
||||
fix: ~
|
||||
|
||||
85
src/snapshots/ruff__linter__tests__B021_B021.py.snap
Normal file
85
src/snapshots/ruff__linter__tests__B021_B021.py.snap
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 3
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 14
|
||||
column: 4
|
||||
end_location:
|
||||
row: 14
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 22
|
||||
column: 4
|
||||
end_location:
|
||||
row: 22
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 30
|
||||
column: 4
|
||||
end_location:
|
||||
row: 30
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 38
|
||||
column: 4
|
||||
end_location:
|
||||
row: 38
|
||||
column: 28
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 46
|
||||
column: 4
|
||||
end_location:
|
||||
row: 46
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 54
|
||||
column: 4
|
||||
end_location:
|
||||
row: 54
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 62
|
||||
column: 4
|
||||
end_location:
|
||||
row: 62
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 70
|
||||
column: 4
|
||||
end_location:
|
||||
row: 70
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind: FStringDocstring
|
||||
location:
|
||||
row: 74
|
||||
column: 4
|
||||
end_location:
|
||||
row: 74
|
||||
column: 48
|
||||
fix: ~
|
||||
|
||||
@@ -67,19 +67,19 @@ expression: checks
|
||||
- - shelve
|
||||
- false
|
||||
location:
|
||||
row: 33
|
||||
row: 32
|
||||
column: 4
|
||||
end_location:
|
||||
row: 33
|
||||
row: 32
|
||||
column: 17
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 33
|
||||
row: 32
|
||||
column: 0
|
||||
end_location:
|
||||
row: 34
|
||||
row: 33
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
@@ -87,39 +87,39 @@ expression: checks
|
||||
- - importlib
|
||||
- false
|
||||
location:
|
||||
row: 34
|
||||
row: 33
|
||||
column: 4
|
||||
end_location:
|
||||
row: 34
|
||||
row: 33
|
||||
column: 20
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
content: pass
|
||||
location:
|
||||
row: 34
|
||||
column: 0
|
||||
row: 33
|
||||
column: 4
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
row: 33
|
||||
column: 20
|
||||
applied: false
|
||||
- kind:
|
||||
UnusedImport:
|
||||
- - pathlib
|
||||
- false
|
||||
location:
|
||||
row: 38
|
||||
row: 37
|
||||
column: 4
|
||||
end_location:
|
||||
row: 38
|
||||
row: 37
|
||||
column: 18
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 38
|
||||
row: 37
|
||||
column: 0
|
||||
end_location:
|
||||
row: 39
|
||||
row: 38
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
@@ -127,19 +127,19 @@ expression: checks
|
||||
- - pickle
|
||||
- false
|
||||
location:
|
||||
row: 53
|
||||
row: 52
|
||||
column: 8
|
||||
end_location:
|
||||
row: 53
|
||||
row: 52
|
||||
column: 21
|
||||
fix:
|
||||
patch:
|
||||
content: pass
|
||||
location:
|
||||
row: 53
|
||||
row: 52
|
||||
column: 8
|
||||
end_location:
|
||||
row: 53
|
||||
row: 52
|
||||
column: 21
|
||||
applied: false
|
||||
|
||||
|
||||
29
src/snapshots/ruff__linter__tests__S101_S101.py.snap
Normal file
29
src/snapshots/ruff__linter__tests__S101_S101.py.snap
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: AssertUsed
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 11
|
||||
fix: ~
|
||||
- kind: AssertUsed
|
||||
location:
|
||||
row: 8
|
||||
column: 4
|
||||
end_location:
|
||||
row: 8
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind: AssertUsed
|
||||
location:
|
||||
row: 11
|
||||
column: 4
|
||||
end_location:
|
||||
row: 11
|
||||
column: 17
|
||||
fix: ~
|
||||
|
||||
21
src/snapshots/ruff__linter__tests__S102_S102.py.snap
Normal file
21
src/snapshots/ruff__linter__tests__S102_S102.py.snap
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: ExecUsed
|
||||
location:
|
||||
row: 3
|
||||
column: 4
|
||||
end_location:
|
||||
row: 3
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind: ExecUsed
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 13
|
||||
fix: ~
|
||||
|
||||
37
src/snapshots/ruff__linter__tests__S104_S104.py.snap
Normal file
37
src/snapshots/ruff__linter__tests__S104_S104.py.snap
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: HardcodedBindAllInterfaces
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 9
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind: HardcodedBindAllInterfaces
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 9
|
||||
fix: ~
|
||||
- kind: HardcodedBindAllInterfaces
|
||||
location:
|
||||
row: 14
|
||||
column: 5
|
||||
end_location:
|
||||
row: 14
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind: HardcodedBindAllInterfaces
|
||||
location:
|
||||
row: 18
|
||||
column: 8
|
||||
end_location:
|
||||
row: 18
|
||||
column: 17
|
||||
fix: ~
|
||||
|
||||
311
src/snapshots/ruff__linter__tests__S105_S105.py.snap
Normal file
311
src/snapshots/ruff__linter__tests__S105_S105.py.snap
Normal file
@@ -0,0 +1,311 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 12
|
||||
column: 11
|
||||
end_location:
|
||||
row: 12
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 13
|
||||
column: 8
|
||||
end_location:
|
||||
row: 13
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 14
|
||||
column: 9
|
||||
end_location:
|
||||
row: 14
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 15
|
||||
column: 6
|
||||
end_location:
|
||||
row: 15
|
||||
column: 14
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 16
|
||||
column: 9
|
||||
end_location:
|
||||
row: 16
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 17
|
||||
column: 8
|
||||
end_location:
|
||||
row: 17
|
||||
column: 16
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 18
|
||||
column: 10
|
||||
end_location:
|
||||
row: 18
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 19
|
||||
column: 18
|
||||
end_location:
|
||||
row: 19
|
||||
column: 26
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 20
|
||||
column: 18
|
||||
end_location:
|
||||
row: 20
|
||||
column: 26
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 22
|
||||
column: 16
|
||||
end_location:
|
||||
row: 22
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 23
|
||||
column: 12
|
||||
end_location:
|
||||
row: 23
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 24
|
||||
column: 14
|
||||
end_location:
|
||||
row: 24
|
||||
column: 22
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 25
|
||||
column: 11
|
||||
end_location:
|
||||
row: 25
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 26
|
||||
column: 14
|
||||
end_location:
|
||||
row: 26
|
||||
column: 22
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 27
|
||||
column: 13
|
||||
end_location:
|
||||
row: 27
|
||||
column: 21
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 28
|
||||
column: 15
|
||||
end_location:
|
||||
row: 28
|
||||
column: 23
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 29
|
||||
column: 23
|
||||
end_location:
|
||||
row: 29
|
||||
column: 31
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 30
|
||||
column: 23
|
||||
end_location:
|
||||
row: 30
|
||||
column: 31
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 34
|
||||
column: 15
|
||||
end_location:
|
||||
row: 34
|
||||
column: 23
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 38
|
||||
column: 19
|
||||
end_location:
|
||||
row: 38
|
||||
column: 27
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 39
|
||||
column: 16
|
||||
end_location:
|
||||
row: 39
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 40
|
||||
column: 17
|
||||
end_location:
|
||||
row: 40
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 41
|
||||
column: 14
|
||||
end_location:
|
||||
row: 41
|
||||
column: 22
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 42
|
||||
column: 17
|
||||
end_location:
|
||||
row: 42
|
||||
column: 25
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 43
|
||||
column: 16
|
||||
end_location:
|
||||
row: 43
|
||||
column: 24
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 44
|
||||
column: 18
|
||||
end_location:
|
||||
row: 44
|
||||
column: 26
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 46
|
||||
column: 12
|
||||
end_location:
|
||||
row: 46
|
||||
column: 20
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 47
|
||||
column: 9
|
||||
end_location:
|
||||
row: 47
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 48
|
||||
column: 10
|
||||
end_location:
|
||||
row: 48
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 49
|
||||
column: 7
|
||||
end_location:
|
||||
row: 49
|
||||
column: 15
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 50
|
||||
column: 10
|
||||
end_location:
|
||||
row: 50
|
||||
column: 18
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 51
|
||||
column: 9
|
||||
end_location:
|
||||
row: 51
|
||||
column: 17
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 52
|
||||
column: 11
|
||||
end_location:
|
||||
row: 52
|
||||
column: 19
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordString: s3cr3t
|
||||
location:
|
||||
row: 53
|
||||
column: 20
|
||||
end_location:
|
||||
row: 53
|
||||
column: 28
|
||||
fix: ~
|
||||
|
||||
14
src/snapshots/ruff__linter__tests__S106_S106.py.snap
Normal file
14
src/snapshots/ruff__linter__tests__S106_S106.py.snap
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
HardcodedPasswordFuncArg: s3cr3t
|
||||
location:
|
||||
row: 13
|
||||
column: 8
|
||||
end_location:
|
||||
row: 13
|
||||
column: 25
|
||||
fix: ~
|
||||
|
||||
50
src/snapshots/ruff__linter__tests__S107_S107.py.snap
Normal file
50
src/snapshots/ruff__linter__tests__S107_S107.py.snap
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
HardcodedPasswordDefault: default
|
||||
location:
|
||||
row: 5
|
||||
column: 28
|
||||
end_location:
|
||||
row: 5
|
||||
column: 37
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordDefault: posonly
|
||||
location:
|
||||
row: 13
|
||||
column: 44
|
||||
end_location:
|
||||
row: 13
|
||||
column: 53
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordDefault: kwonly
|
||||
location:
|
||||
row: 21
|
||||
column: 38
|
||||
end_location:
|
||||
row: 21
|
||||
column: 46
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordDefault: posonly
|
||||
location:
|
||||
row: 29
|
||||
column: 38
|
||||
end_location:
|
||||
row: 29
|
||||
column: 47
|
||||
fix: ~
|
||||
- kind:
|
||||
HardcodedPasswordDefault: kwonly
|
||||
location:
|
||||
row: 29
|
||||
column: 61
|
||||
end_location:
|
||||
row: 29
|
||||
column: 69
|
||||
fix: ~
|
||||
|
||||
@@ -3,48 +3,197 @@ source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind:
|
||||
UnnecessaryFutureImport: nested_scopes
|
||||
UnnecessaryFutureImport:
|
||||
- generators
|
||||
- nested_scopes
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 61
|
||||
fix: ~
|
||||
column: 48
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 1
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport: generators
|
||||
UnnecessaryFutureImport:
|
||||
- unicode_literals
|
||||
- with_statement
|
||||
location:
|
||||
row: 1
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 1
|
||||
column: 61
|
||||
fix: ~
|
||||
row: 2
|
||||
column: 55
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport: absolute_import
|
||||
UnnecessaryFutureImport:
|
||||
- absolute_import
|
||||
- division
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 48
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport: division
|
||||
UnnecessaryFutureImport:
|
||||
- generator_stop
|
||||
location:
|
||||
row: 3
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 48
|
||||
fix: ~
|
||||
- kind:
|
||||
UnnecessaryFutureImport: generator_stop
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
row: 4
|
||||
column: 37
|
||||
fix: ~
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport:
|
||||
- generator_stop
|
||||
- print_function
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 53
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport:
|
||||
- generators
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 49
|
||||
fix:
|
||||
patch:
|
||||
content: from __future__ import invalid_module
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 49
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport:
|
||||
- generator_stop
|
||||
location:
|
||||
row: 9
|
||||
column: 4
|
||||
end_location:
|
||||
row: 9
|
||||
column: 41
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 9
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport:
|
||||
- generators
|
||||
location:
|
||||
row: 10
|
||||
column: 4
|
||||
end_location:
|
||||
row: 10
|
||||
column: 37
|
||||
fix:
|
||||
patch:
|
||||
content: pass
|
||||
location:
|
||||
row: 10
|
||||
column: 4
|
||||
end_location:
|
||||
row: 10
|
||||
column: 37
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport:
|
||||
- generator_stop
|
||||
location:
|
||||
row: 13
|
||||
column: 4
|
||||
end_location:
|
||||
row: 13
|
||||
column: 41
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 13
|
||||
column: 0
|
||||
end_location:
|
||||
row: 14
|
||||
column: 0
|
||||
applied: false
|
||||
- kind:
|
||||
UnnecessaryFutureImport:
|
||||
- generators
|
||||
location:
|
||||
row: 14
|
||||
column: 4
|
||||
end_location:
|
||||
row: 14
|
||||
column: 53
|
||||
fix:
|
||||
patch:
|
||||
content: from __future__ import invalid_module
|
||||
location:
|
||||
row: 14
|
||||
column: 4
|
||||
end_location:
|
||||
row: 14
|
||||
column: 53
|
||||
applied: false
|
||||
|
||||
|
||||
260
src/snapshots/ruff__linter__tests__U012_U012.py.snap
Normal file
260
src/snapshots/ruff__linter__tests__U012_U012.py.snap
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
source: src/linter.rs
|
||||
expression: checks
|
||||
---
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 21
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 21
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 18
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 3
|
||||
column: 0
|
||||
end_location:
|
||||
row: 3
|
||||
column: 18
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 14
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 14
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 20
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 20
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 22
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 22
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 30
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 7
|
||||
column: 0
|
||||
end_location:
|
||||
row: 7
|
||||
column: 30
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 14
|
||||
column: 1
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"\"\"\nLorem\n\nIpsum\n\"\"\""
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 14
|
||||
column: 1
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 26
|
||||
column: 0
|
||||
end_location:
|
||||
row: 26
|
||||
column: 27
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 26
|
||||
column: 19
|
||||
end_location:
|
||||
row: 26
|
||||
column: 26
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 44
|
||||
column: 0
|
||||
end_location:
|
||||
row: 44
|
||||
column: 31
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 44
|
||||
column: 23
|
||||
end_location:
|
||||
row: 44
|
||||
column: 30
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 46
|
||||
column: 0
|
||||
end_location:
|
||||
row: 46
|
||||
column: 39
|
||||
fix:
|
||||
patch:
|
||||
content: ""
|
||||
location:
|
||||
row: 46
|
||||
column: 23
|
||||
end_location:
|
||||
row: 46
|
||||
column: 38
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 48
|
||||
column: 0
|
||||
end_location:
|
||||
row: 48
|
||||
column: 23
|
||||
fix:
|
||||
patch:
|
||||
content: "br\"fo\\o\""
|
||||
location:
|
||||
row: 48
|
||||
column: 0
|
||||
end_location:
|
||||
row: 48
|
||||
column: 23
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 49
|
||||
column: 0
|
||||
end_location:
|
||||
row: 49
|
||||
column: 22
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 49
|
||||
column: 0
|
||||
end_location:
|
||||
row: 49
|
||||
column: 22
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 50
|
||||
column: 0
|
||||
end_location:
|
||||
row: 50
|
||||
column: 23
|
||||
fix:
|
||||
patch:
|
||||
content: "bR\"fo\\o\""
|
||||
location:
|
||||
row: 50
|
||||
column: 0
|
||||
end_location:
|
||||
row: 50
|
||||
column: 23
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 51
|
||||
column: 0
|
||||
end_location:
|
||||
row: 51
|
||||
column: 22
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 51
|
||||
column: 0
|
||||
end_location:
|
||||
row: 51
|
||||
column: 22
|
||||
applied: false
|
||||
- kind: UnnecessaryEncodeUTF8
|
||||
location:
|
||||
row: 52
|
||||
column: 6
|
||||
end_location:
|
||||
row: 52
|
||||
column: 20
|
||||
fix:
|
||||
patch:
|
||||
content: "b\"foo\""
|
||||
location:
|
||||
row: 52
|
||||
column: 6
|
||||
end_location:
|
||||
row: 52
|
||||
column: 20
|
||||
applied: false
|
||||
|
||||
@@ -6,7 +6,7 @@ use assert_cmd::{crate_name, Command};
|
||||
#[test]
|
||||
fn test_stdin_success() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(crate_name!())?;
|
||||
cmd.args(&["-"]).write_stdin("").assert().success();
|
||||
cmd.args(["-"]).write_stdin("").assert().success();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ fn test_stdin_success() -> Result<()> {
|
||||
fn test_stdin_error() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(crate_name!())?;
|
||||
let output = cmd
|
||||
.args(&["-"])
|
||||
.args(["-"])
|
||||
.write_stdin("import os\n")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -26,7 +26,7 @@ fn test_stdin_error() -> Result<()> {
|
||||
fn test_stdin_filename() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(crate_name!())?;
|
||||
let output = cmd
|
||||
.args(&["-", "--stdin-filename", "F401.py"])
|
||||
.args(["-", "--stdin-filename", "F401.py"])
|
||||
.write_stdin("import os\n")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -38,7 +38,7 @@ fn test_stdin_filename() -> Result<()> {
|
||||
fn test_stdin_autofix() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(crate_name!())?;
|
||||
let output = cmd
|
||||
.args(&["-", "--fix"])
|
||||
.args(["-", "--fix"])
|
||||
.write_stdin("import os\nimport sys\n\nprint(sys.version)\n")
|
||||
.assert()
|
||||
.success();
|
||||
@@ -53,7 +53,7 @@ fn test_stdin_autofix() -> Result<()> {
|
||||
fn test_stdin_autofix_when_not_fixable_should_still_print_contents() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(crate_name!())?;
|
||||
let output = cmd
|
||||
.args(&["-", "--fix"])
|
||||
.args(["-", "--fix"])
|
||||
.write_stdin("import os\nimport sys\n\nif (1, 2):\n print(sys.version)\n")
|
||||
.assert()
|
||||
.failure();
|
||||
@@ -68,7 +68,7 @@ fn test_stdin_autofix_when_not_fixable_should_still_print_contents() -> Result<(
|
||||
fn test_stdin_autofix_when_no_issues_should_still_print_contents() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(crate_name!())?;
|
||||
let output = cmd
|
||||
.args(&["-", "--fix"])
|
||||
.args(["-", "--fix"])
|
||||
.write_stdin("import sys\n\nprint(sys.version)\n")
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
Reference in New Issue
Block a user